Using Maven in J2EE Projects (在J2EE項(xiàng)目中使用Maven)
譯注:由于原文的source code是基于maven 1.0編寫的,部分代碼存在著一定的問題。此外,本文的部分內(nèi)容所提到的問題在最新的maven 1.02中已經(jīng)不存在了。
In the previous section you saw how to use Maven to set up a project template, compile source files, create a jar and publish the artifact into the repository. There is much more to Maven than that. We will cover inheritance in Maven POM, setting up a Sample J2EE project, creating WARs, EJB-JARs, dependency jars and ultimately creating EARs. However let's first look at another important concept in Maven.
從前文中,你已經(jīng)學(xué)會了如何使用Maven使用項(xiàng)目模板、編譯源文件、創(chuàng)建jar包、將制品發(fā)布到倉庫中,Maven的基本功能都在這里了。下面我們將會介紹Maven POM的基礎(chǔ)、設(shè)置J2EE 演示項(xiàng)目、創(chuàng)建WAR、EJB-JAR、依賴包、以及最終的EAR。但在這之前,我們先看一下Maven中的另一個(gè)重要概念。
In Maven, a project can produce only one artifact. The top-level project can have multiple sub projects and they in turn can have siblings too. But at the end, each of them can only produce one artifact. This makes a lot of sense if you consider how the Ant scripts are used to create multiple artifacts from a project - which gets confusing over time - to say the least. Typically, projects have a monolithic (or maybe a couple) source tree(s), from which all the dependency jars, WARs and EARs are created using different build scripts. At the end, the message of application partitioning is lost and classes are created in different packages without consideration to the classloader mechanism, making it even harder to keep build scripts up to date. Maven discourages this practice at the outset. Of course Maven gives you flexibility to override this practice. There are ways to produce multiple artifacts but that goes against the very spirit of Maven and has to be avoided unless absolutely essential.
在Maven中,一個(gè)項(xiàng)目只能生成一個(gè)制品。最頂層的項(xiàng)目可以含有多個(gè)子項(xiàng)目、也就是說,每個(gè)項(xiàng)目可以有兄弟項(xiàng)目。但無論如何,每個(gè)項(xiàng)目只能有一個(gè)制品。將Maven和Ant比較,“Ant可以為一個(gè)項(xiàng)目創(chuàng)建多個(gè)制品”,這確實(shí)是個(gè)需要花一段時(shí)間才能想通的問題。在典型情況下,每個(gè)項(xiàng)目都擁有一個(gè)(或多個(gè))源代碼樹,從中可以build出各種jar、WAR、EAR。最后,項(xiàng)目中的所有分割信息都被隱藏了起來,根本沒有考慮classloader機(jī)制,直接編譯各包的class,很難保持構(gòu)建腳本及時(shí)更新。Maven則不鼓勵(lì)這樣的工作方式,當(dāng)然Maven靈活的提供了其他的方式實(shí)現(xiàn)此功能。在Maven中,你可以在一個(gè)項(xiàng)目中創(chuàng)建多個(gè)制品,但這本身就違反了Maven的精神,除非必要,我們都應(yīng)該盡量去避免它。
Customizing Maven with maven.xml (使用maven.xml對Maven進(jìn)行定制)
Until now you saw how to compile java classes in a maven project and create jar files out of it. What if you wanted to customize Maven behavior? You can create a file called maven.xml in the same folder where project.xml resides. In this file, you can customize goals, define and pre and post goals.
到現(xiàn)在為止,你已經(jīng)知道在Maven項(xiàng)目中如何編譯java文件并將其打包,那么如何定制Maven的行為呢?你可以在Maven的項(xiàng)目中,創(chuàng)建一個(gè)maven.xml文件,其位置和project.xml相同。在此文件中,你可以定制goal,定義pre、post goal。
Listing 6 shows a sample maven.xml. We are achieving quite a bit in this file. Look at the default goal for the project. The default goal is set to foobar-dist. A goal is equivalent of an Ant target. The default goal is the same as the default target in Ant. What this means is that the default goal is executed when you go to the directory containing this file and simply type maven. The foobar-dist goal is simply a wrapper on top of another goal called war:install. The "install" goal in the war plugin creates a war and publishes it into the repository. The "attainGoal" instructs maven to achieve the goal described by the name attribute.
表6 中給出了maven.xml的例子,從中我們可以學(xué)到很多東西。請看其中定義的該項(xiàng)目的默認(rèn)goal,它被設(shè)為了foobar-dist。Maven中的goal和Ant中的target是相同的概念,Maven中的默認(rèn)goal和Ant中的默認(rèn)target相同。Maven的默認(rèn)goal意味著:在該項(xiàng)目的目錄下,直接敲入“maven”命令,這時(shí)將會執(zhí)行默認(rèn)goal。這里例子中的foobar-dist是對“war:install”的封裝。在插件war中的“install”goal的作用是創(chuàng)建一個(gè)war,并將其發(fā)布到倉庫中。“attainGoal”則是告訴Maven去執(zhí)行“name”屬性所描述的任務(wù)。
The preGoal element instructs Maven to execute the defined tasks in the preGoal before achieving the goal specified in its name attribute. This can get confusing at times. Note that the preGoal name (java:compile) is different from the default goal (foobar-dist). The default goal for this project installs a war file. However the war plugin has to compile the java files in the project before creating the actual WAR archive. The preGoal specified is linked to this java:compile goal rather than the foobar-dist goal. In this case, the pre goal executes a Xdoclet maven plug in. We will use Xdoclet Maven plugin later in this article to generate tlds (tag library definitions) from the actual tag classes.
PreGoal元素則告訴Maven在執(zhí)行g(shù)oal之前,執(zhí)行在其“name”屬性中所描述的任務(wù)。有時(shí)候這也許會讓你犯糊涂,注意這里的preGoal 的name(java:compile)與默認(rèn)goal(foobar-dist)的name是不同的。本項(xiàng)目中的默認(rèn)goal是安裝一個(gè)war文件,但是在正式創(chuàng)建WAR文件之前,必須首先要編譯java文件,(譯者注:但是在編譯java之前,通過pregoal的定義,在java:compile調(diào)用之前,添加了新的調(diào)用xdoclet:webdoclet)。這里preGoal鏈接指向java:compile,而不是foobar-dist, 在本例中,pre goal將會執(zhí)行Xdoclet插件的xdoclet:webdoclet。在這篇文章的后面,我們還會使用Xdoclet插件,從tag class中生成tld(tag library definitions)文件。(譯者注:表6中的preGoal調(diào)用了xdoclet:webdoclet,而此xdoclet插件可能是Maven沒有預(yù)先安裝的,需要執(zhí)行“maven plugin:download -DartifactId=maven-xdoclet-plugin -DgroupId=xdoclet -Dversion=1.2”進(jìn)行下載。此外,由于xdoclet目前的并不是在一個(gè)jar文件中的,所以還需要在project.xml中加載一些其他jar文件。)
A postGoal can be specified for any of the implicit goals achieved as part of the default goal or for the default goal itself. In Listing 6, a postGoal is specified for the war:war goal, which gets executed just before war:install goal. The postGoal specifies that a certain directory have to be created after the WAR is built. Notice the use of standard ant tasks within the postGoal. In fact any of the ant tasks can be embedded within the goal, preGoal or postGoal. This is what makes Maven flexible. It is also very easy to abuse this flexibility by compiling and creating multiple artifacts using Ant tasks within Maven. In other words, you would have written Ant scripts under the hoods of Maven. That practice is discouraged and should be used if there is no other option. If you find yourself repeating the steps of putting ant tasks into Maven goals, what you need is a custom plugin.
同樣的是,postGoal 可以做為任何goal的一部分,在表6中,postGoal則是專門為“war:war”定義的,這將在war:install之前被執(zhí)行。(譯者注:因?yàn)楸?中,為war:war定義了添加了postGoal擴(kuò)展,而war:war將由war:install調(diào)用,因此文中說“這將在war:install之前被執(zhí)行”。讀者不妨可以將postGoal這一段注釋掉執(zhí)行以觀察執(zhí)行結(jié)果,以進(jìn)行比較。)在本例中,postGoal的定義是:在創(chuàng)建了WAR之后,創(chuàng)建一個(gè)目錄。注意,postGoal中使用了Ant中的task,事實(shí)上,Maven中可以嵌入任何Ant的task,無論是在goal, pregoal, postgoal中。它給Maven帶來了極大的靈活性,但是也很容易帶來Maven的誤用-“使用Ant的task進(jìn)行代碼編譯、創(chuàng)建制品”,也就是說,你只是在Maven的帽子下面仍然依從Ant的方法編寫Ant腳本,如果有可能,請別使用這種方式完成任務(wù)。如果你發(fā)現(xiàn)你不斷的往Maven種添加Ant的task,那么你就該停下來去找一個(gè)適合的Maven插件。
Listing 6 Sample maven.xml
<project default="foobar-dist" xmlns:m="jelly:maven">
<preGoal name="java:compile">
<attainGoal name="xdoclet:webdoclet"/>
</preGoal>
<goal name="foobar-dist">
<attainGoal name="war:install" />
</goal>
<postGoal name="war:war">
<mkdir dir="${test.result.dir}"/>
<echo>Creating directories</echo>
</postGoal>
</project>
A custom plugin is written in a file called plugin.jelly using the Jelly scripting language and bundled along with project.xml and other files into a jar and copied into the C:/Documents And Settings/<login-id>/.maven/plugins directory. This plugin is then available to all of your processes using Maven. We will customize the war plugin later in this article since it lacks some of the features we need.
Maven的插件存放于C:/Documents And Settings/<login-id>/.maven/plugins目錄中,每個(gè)插件是一個(gè)jar文件,其中包括:使用Jelly腳本語言編寫的plugin.jell文件、project.xml 以及其他文件。因?yàn)?/SPAN>war插件不能滿足一些我們的要求,后面我們將對war插件進(jìn)行定制(譯者注:由于本篇文章寫于2003,它所使用的是Maven 1.0,其問題已經(jīng)解決)。
Figure 3 Inter-Project relationships for Foobar Travels
圖3 Foobar Travels中的內(nèi)部項(xiàng)目關(guān)系圖
Let us now jump to applying Maven to J2EE projects. The most common J2EE artifact generated and deployed is the EAR. The EAR itself consists of dependency jars, ejb jars, wars and third party libraries. In other words, EAR is an aggregate artifact. For Foobar Travels, we will use the same project template as shown in Figure 2. Figure 3 shows the inter-project relationship and hierarchy. The master project template is defined at the top project level. A simplified master template is shown in Listing 7 only with the essential elements. A real one will have most of the items described in the section Maven basics. The key thing to note is that the dependencies are not declared in this template. This is because the master project.xml is just that - a template for other sub projects to extend. The only thing we have not touched upon from Listing 7 is the ${pom.artifactId}. I will explain this shortly in the context of extending the template. Figure 4 shows the structure of the EAR file generated by this project. You will find that the EAR content organization closely resembles the Maven project shown in Figure 3. In most of the J2EE project you would not deviate from a deployment artifact as in Figure 4. And when you stick to the usual route, Maven "out-of-the-box" build scripts work as is.
現(xiàn)在讓我開始將Maven使用到J2EE項(xiàng)目中。J2EE項(xiàng)目中最經(jīng)常生成發(fā)布的制品是EAR。EAR制品中包含的內(nèi)容有:各種依賴包、EJB JAR、WAR以及第三方庫文件,EAR是一個(gè)聚合包。對于Foobar Travels,我們將使用圖2中的項(xiàng)目模板。圖3給出了其內(nèi)部項(xiàng)目的依賴關(guān)系。項(xiàng)目的主模板將在項(xiàng)目的最頂層進(jìn)行定義,表7中只給出了主模板中的關(guān)鍵元素信息。在Maven basics部分我們已經(jīng)給出了實(shí)際中的絕大部分元素的信息。需要注意的是:在此模板中,并沒用給出依賴關(guān)系,這是因?yàn)橹髂0錺roject.xml只是一個(gè)給子項(xiàng)目進(jìn)行擴(kuò)展的模板。在表7中我們唯一以前未曾接觸到的是${pom.artifactId},這將在講述如何擴(kuò)展模板的時(shí)候?qū)λM(jìn)行解釋。圖4給出了本項(xiàng)目所生成的EAR文件的結(jié)構(gòu),你會發(fā)現(xiàn)這里EAR的內(nèi)容和圖3中的Maven項(xiàng)目的內(nèi)容緊緊的結(jié)合在一起。在絕大部分J2EE項(xiàng)目中,最后所部署的制品基本都和圖4所顯示項(xiàng)目的內(nèi)容基本一致。如果你采用常規(guī)方法使用Maven的,它“out-of-the-box”構(gòu)造腳本就可以如上使用了(譯者注:“out-of-the-box”是以前的一個(gè)開源項(xiàng)目,參考http://www.javaworld.com/javaworld/jw-04-2003/jw-0404-outofbox.html )。
Figure 4 The EAR structure for Foobar Travels
圖4 FooBar Travels的EAR結(jié)構(gòu)圖
Listing 7 Master Project Definition Template
表7主項(xiàng)目定義模板
<project>
<pomVersion>3</pomVersion>
<id>foobar-online</id>
<groupId>Foobar-Travels</groupId>
<currentVersion>2.0</currentVersion>
<name>Foobar Online Project</name>
<organization>
<name>Foobar Travels</name>
</organization>
<package>foobar.*</package>
<distributionDirectory>
/foobar/dist/${pom.artifactId}/
</distributionDirectory>
<dependencies/>
<build>
<sourceDirectory>${basedir}/src/java</sourceDirectory>
<resources/>
</build>
</project>

To summarize Figure 4, we have to build the following artifacts from the project shown in Figure 3.
- foobar-services-2.0.jar - The dependency jar
- reservationejb-2.0.jar - The Reservation EJB and create its Manifest file with Manifest Class-path pointing to the services jar file.
- personejb-2.0.jar - The Person EJB and create its Manifest file with Manifest Class-path pointing to the services jar file.
- foobar-web-2.0.jar - The Web application for Foobar Travels and create its Manifest file with Manifest Class-path pointing to the personejb, reservationejb and service jars
- application.xml - the J2EE application deployment descriptor.
- Build the EAR by including all of the above artifacts and also including the castor jar file since the services jar depends on it at runtime.
總結(jié)圖4,我們將從圖3 的項(xiàng)目中創(chuàng)建出以下的制品:
1、 foobar-services-2.0.jar -其所依賴的包
2、 reservationejb-2.0.jar -Reservation EJB,需要?jiǎng)?chuàng)建Manifest文件,其中聲明class-path,指向services jar文件。
3、 personejb-2.0.jar-Person EJB,需要?jiǎng)?chuàng)建Manifest文件,其中聲明class-path,指向services jar文件。
4、 foobar-web-2.0.jar-Foobar Travels 的Web程序,需要?jiǎng)?chuàng)建Manifest文件,其中聲明class-path,指向personejb、reservationejb、services jar文件。
5、 application.xml -J2EE應(yīng)用的部署描述符。
6、 創(chuàng)建EAR,其中包含上面的所有的制品、另外還需要包含castor.jar,因?yàn)?/SPAN>service jar會在運(yùn)行的時(shí)候使用它。
Building the dependency jars (創(chuàng)建依賴包)
Listing 8 shows the project definition for Services - a subproject in Maven. This subproject creates the foobar-services-2.0.jar file - a dependency library used by both ejbs and the web tier. Note that the definition extends from the master template in Listing 7. By extending from the parent, it inherits all the properties. It defines the dependency elements to indicate the dependencies on the J2EE APIs (not defined in the parent) and overrides the id, name and description defined in the parent.
表8中給出了子項(xiàng)目Services的定義,該項(xiàng)目將會創(chuàng)建foobar-services-2.0.jar,在EJB和Web程序中都會使用到該文件。注意,這里的定義侍從表7中的模板擴(kuò)展而來的。通過擴(kuò)展父模板,它繼承了所有的屬性。其中它定義了它將會依賴于J2EE API(這并沒有在父模板中定義),并且覆蓋(override)了父模板中的id, name and description。
Listing 8 Services Sub-Project Definition
表8 子項(xiàng)目Service的定義
<project>
<extend>${basedir}/../project.xml</extend>
<id>foobar-services</id>
<name>Foobar Services Framework</name>
<package>foobar.service.*</package>
<description>Services JAR project.</description>
<dependencies>
<dependency>
<groupId>j2ee</groupId>
<artifactId>j2ee</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>

Now let us get back to the ${pom.artifactId} mentioned earlier (Listing 7). This lets you introduce polymorphism into Maven. At runtime, the value of ${pom.artifactId} for the Services sub-project is its id - foobar-services. Hence the value of the distribution directory for the Services project is /foobar/dist/foobar-services. In other words the pom can capture the variables and abstract them in a standard manner and establish a procedural approach instead of the chaotic one.
現(xiàn)在讓我講解前面提到的${pom.artifactId}(表7中),它允許你將多態(tài)(polymorphism)引入到Maven中。在運(yùn)行的時(shí)候, ${pom.artifactId}的具體值是子項(xiàng)目Services的id:foobar-services.因此Services項(xiàng)目的distributionDirectory值是:/foobar/dist/foobar-services.換句話說,POM能夠捕捉變量的值,并且在運(yùn)行的時(shí)候通過一種標(biāo)準(zhǔn)方式,及時(shí)用確切值替換原變量。
Now let us introduce you to the maven.xml for the Services Project. Technically speaking, maven.xml is not needed since you can always go to this sub project and execute maven jar:install. However as we will see later, having a maven.xml will help us eliminate manual execution of Maven from every sub-project. The maven.xml for the Services project looks as follows.
下面介紹Services項(xiàng)目中的maven.xml。從技術(shù)角度來看,maven.xml并不是必需的,你可以到該子項(xiàng)目的目錄下執(zhí)行命令“jar:install”。但是我們后面就可以看到,擁有這樣的一個(gè)maven.xml文件將會極大的幫助我們,不再需要人工到每個(gè)子目錄下面執(zhí)行maven命令。Services子項(xiàng)目的maven.xml文件內(nèi)容如下:
<project>
<extend>${basedir}/../project.xml</extend>
<id>foobar-services</id>
<name>Foobar Services Framework</name>
<package>foobar.service.*</package>
<description>Services JAR project.</description>
<dependencies>
<dependency>
<groupId>j2ee</groupId>
<artifactId>j2ee</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>

Building the EJB jars (構(gòu)建EJB Jar)
After having built the dependency jars let us turn our focus to building the ejb jars. This is no different, since there is already a ejb plugin and you can use the ejb:install goal to create and publish the ejb jars. However we would also like to remind you that the ejb jar (loaded by the EJB class loader) depends on the Services built in the previous step (See Figure 4). What this means for you is that the ejb manifest classpath should point to the foobar-services-2.0 jar. Listing 9 shows the project.xml for the Reservation EJB jar. Other ejb project definitions are similar.
在創(chuàng)建了依賴包后,我們開始創(chuàng)建ejb jar。毫無區(qū)別的是,Maven中已經(jīng)有了ejb插件 ,你可以使用ejb:install創(chuàng)建和發(fā)布ejb jar。但是我必須提醒你,ejb jar(由EJB class loader載入)依賴于前一步圖4中剛創(chuàng)建的Services包。也就是說,你必須在ejb manifest中聲明classpath,將它指向foobar-services-2.0 jar。表9給出了Reservation EJB jar的project.xml。其他EJB項(xiàng)目的定義與此類似。
NOTE: In the downloadable example supplied with this article, no EJBs are included. The reason is to have even non-ejb developers using Tomcat to see Maven in action. However this section describes what it takes to build EJBs with Maven.
注意:在供下載的例子中,并沒有包含EJB程序。因?yàn)楸WC非EJB開發(fā)者可以在Tomcat中使用,了解Maven的使用方法。但是本段所講的內(nèi)容則是如何使用Maven構(gòu)建EJB。 |
As usual the ejb project depends on the J2EE jars. However since it also depends on the foobar-services-2.0.jar (loaded by the EAR class loader), it has to add that jar file name to its manifest classpath. You can achieve this by setting the property ejb.manifest.classpath to be true. The Maven variable, pom.currentVersion stands for the current version of the project. In our case, it is 2.0. This setting indicates that the version of services project on which the ejb project depends is the same as the current version of the ejb project. This version is set on the parent project template. For future releases, the version number need not be changed for every dependency in every project. Just change it in the Master project template and it takes effect everywhere. Ah, the beauty of inheritance!
和平常一樣,EJB項(xiàng)目會依賴J2EE包,但是他還依賴foobar-services-2.0.jar(由EAR Class loader載入)。因此不得不將此jar的文件名機(jī)阿然到manifest的classpath中。你可以將ejb.manifest.classpath的值設(shè)為true。pom.currentVersion代表項(xiàng)目的當(dāng)前版本,在我們的例子中是2.0。這里的設(shè)置意味著EJB項(xiàng)目和其所依賴的Services項(xiàng)目兩者的版本號是一樣的。這里的版本號被設(shè)置為父項(xiàng)目模板中的版本。在將來發(fā)布的時(shí)候,則無需一一修改各個(gè)子項(xiàng)目的版本號,直接修改父項(xiàng)目的模板即可。這就是繼承的作用。
Listing 9 Reservation EJB Project Definition 表9 Reservation EJB項(xiàng)目的定義
<project>
<extend>${basedir}/../project.xml</extend>
<id>reservationejb</id>
<name>Foobar Reservation Components</name>
<package>foobar.reservation.*</package>
<description>Reservation Components project</description>
<dependencies>
<dependency>
<groupId>j2ee</groupId>
<artifactId>j2ee</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>Foobar-Travels</groupId>
<artifactId>foobar-services</artifactId>
<version>${pom.currentVersion}</version>
<properties>
<ejb.manifest.classpath>true</ejb.manifest.classpath>
</properties>
</dependency>
</dependencies>
</project>
The discussion of building the ejb jars is incomplete without mentioning the maven.xml file. This file is not straight forward as in the services project. Listing 10 shows the maven.xml for the Reservation EJB project. There are a few things happening here. First, the foobar-dist goal is used as a wrapper for attaining the goal ejb:install. One of the first goals to be achieved during the install is init, when the file system and other resources are initialized and then the ejb classes are compiled. The compiler expects to find the home, local and remote interfaces, failing which it will throw a compiler error. This is where XDoclet comes into play.
在討論構(gòu)建ejb jar的時(shí)候,必須要談到其maven.xml。這里的maven.xml則不像Services項(xiàng)目中那樣加納隊(duì)那了。表10給出了其定義,有以下幾點(diǎn)內(nèi)容:首先,foobar-dist goal封裝了ejb:install;在“install”剛初始化的之前,文件系統(tǒng)和其他資源必須首先被初始化,隨后編譯ejb的class文件。編譯器將嘗試尋找home、local、interface,如果失敗,則編譯報(bào)錯(cuò)。這就是XDolet所搬演的角色。
Listing 10 maven.xml for the Reservation EJB Project
表10 Reservation EJB項(xiàng)目的maven.xml
<project default="foobar-dist" xmlns:m="jelly:maven"
xmlns:ant="jelly:ant">
<goal name="foobar-dist">
<attainGoal name="ejb:install" />
</goal>

<preGoal name="ejb:init">
<attainGoal name="xdoclet:ejbdoclet"/>
</preGoal>

<postGoal name="ejb:install">
<ant:property name="maven.ejb.install.dir"
value="${maven.repo.local}/${pom.artifactDirectory}/ejbs"/>
<ant:mkdir dir="${maven.ejb.install.dir}"/>
<ant:copy file="${maven.build.dir}/${maven.final.name}.jar"
tofile="${maven.ejb.install.dir}/../
jars/${maven.final.name}.jar"/>
</postGoal>
</project>

XDoclet is an open source project hosted on the SourceForge. By using EJB Doclet specific tags in the Bean class (the implementation class), you signal the XDoclet to generate the home, local and remote interfaces. You will also provide the jndi names, and other information using similar tags. On parsing the file, the XDoclet will also generate appropriate deployment descriptors. Now let us get back to see how it fits in here.
XDoclet是sourceforge中的一個(gè)開源項(xiàng)目,通過在Bean class文件(EJB 實(shí)現(xiàn)文件)中的EJB Doclet 描述tag,告訴Xdoclet產(chǎn)生home, local interface和remote interface。你還可以使用類似tag,寫入jndi name以及其他信息。Xdoclet經(jīng)過分析源文件之后,將會生成相應(yīng)的部署描述符。現(xiàn)在讓我們回頭看它是怎么運(yùn)行的。
XDoclet provides Maven plugins for generating the above-mentioned artifacts. By specifying the ejbdoclet as the preGoal for ejb:init, you will generate home, local and remote interfaces and xml deployment descriptors just in time for the compilation.
XDoclet為Maven提供了插件,可以生成上述的制品。通過將ejbdoclet設(shè)置為ejb:init的pregoal,它將會在編譯期間幫你生成home, local interface, remote interface和部署描述符xml文件。
Now let us look at the postGoal for ejb:install. As you know, the ejb:install generates the ejb jar as the artifact and puts it in the repository in the location "C:/Documents And Settings/<login-id>/.maven/repository/Foobar-Online/jars". Suppose that you want the ejbs to be in a separate directory called ejbs instead of the jars, you have to do it after the ejb:install is finished with its business. That's why it is a postGoal. For those familiar with Ant, the postGoal is nothing but a collection of Ant tasks. Again, a couple of Maven specific properties are used. Don't try to remember all of the Maven defined properties at once. Over time, these will become second nature.
現(xiàn)在讓我們看一下ejb:install的postGoal吧。正如你所知道的,ejb:install將會生成ejb jar制品,并將其發(fā)布到倉庫C:/Documents And Settings/<login-id>/.maven/repository/Foobar-Online/jars目錄。假設(shè)你想將ejb部署到名為“ejbs”的目錄中,而不是目錄“jars”,這必須在ejb:install完成之后才能做。這就是為何這里使用postGoal的原因。熟悉Ant的讀者就能知道,這里的postGoal是Ant的task的集合,這里使用了Maven的許多特有屬性。并沒有必要一次性的記住Maven所定義的所有屬性,過一段時(shí)間,這些就變成你的第二本能啦。
You will notice there are two distinct categories of properties - those with their name beginning with pom and the rest of them. The properties with their names beginning with pom are the individual elements in the project.xml. For instance, the currentVersion element defined in project.xml has a property with the name pom.currentVersion and so on. The project.xml is loaded to create the POM model by Maven. POM model attribute values are evaluated by using the $ sign before the attribute names. As more plugins are added new properties emerge. This is going to be a challenge. You may not find adequate documentation of these properties. The only way to find out is by opening the scripts for each of these plugins in plugin.jelly file. Luckily there aren't very many properties that you will have to know.
注意到這里有兩類屬性:名字以pom開頭算第一類,其余的是第二類。第一類的屬性是在project.xml中定義的元素。例如project.xml定義的currentVersion元素可以使用pom.currentversion進(jìn)行訪問,其他也是如此。Maven將會載入project.xml形成POM模型。可以在屬性名前面加$開頭訪問其屬性值。隨著插件的增多,這確實(shí)會帶來一定的問題。你根本無法找到這些屬性的文檔,唯一能做的就是打開每個(gè)插件的pligin.jelly,查看其內(nèi)容。幸好,你并不需要了解太多的屬性。
In your project, if you find yourself writing the same pregoals and postgoals for every ejb subproject for example, it is time to roll your own plugin.
在你的項(xiàng)目中,如果你為每個(gè)ejb都寫了相同的pregoal、postgoal,那么你就該寫自己的插件啦。
Building the WAR (構(gòu)建war)
After the ejb jar, it is war time. We will accomplish quite a bit in this section. First we will introduce you to the project.xml. Then we will add a minor missing functionality into the war plugin. Then we will show you how to use XDoclet to generate tlds for a sample tag. Then we will end the section with maven.xml.
在創(chuàng)建了Ejb jar后,該創(chuàng)建war包了。我們將在后面對此進(jìn)行講述。首先我們介紹project.xml,然后將給war插件添加一些未曾有的功能(譯注:在maven 1.02版本中,問題已經(jīng)解決),接著將演示如何使用XDoclet生成為tag例子生成tld文件,最后介紹maven.xml。
Listing 11 shows the project.xml for the Foobar web application. The actual project definition has much more dependencies and is downloadable. Here we are showing only the relevant portions to illustrate the concepts. The important part is the dependencies section. The web application depends on the J2EE APIs provided by the container at runtime as well as compile time. However there is no need to bundle the J2EE API with the WAR since it is the servlet container's responsibility. In addition the web application depends on Struts 1.1 jar file. The property setting <war.bundle>true</war.bundle> indicates that struts.jar has to be bundled with the WAR. Whenever the war plugin is instructed to bundle the jars, it puts them under the WEB-INF/lib directory of the WAR. Compare this to manually copying the jars into the WEB-INF/lib directory in Ant. The WAR also depends on the Foobar Services jar file, foobar-services-2.0.jar. However we do not want to copy this jar into the WEB-INF/lib since it is a dependency library and shared by both the web tier and the ejbs. (Recall that any jars and classes residing in WEB-INF/lib and WEB-INF/classes are loaded by the WAR class loader.) Hence we set the war.bundle property as false for foobar-services jar file. You get all this functionality out of the box with Maven war plugin.
表11給出了Foobar Web程序的project.xml,實(shí)際的project.xml文件的內(nèi)容要比這里演示的內(nèi)容多許多。這里我們僅介紹相關(guān)部分,講解其概念。最關(guān)鍵的部分就是depencies部分,該Web程序依賴于J2EE API,它們將由容器在編譯器和運(yùn)行時(shí)提供。因此無需在WAR中加入J2EE的包,這時(shí)Servlet容器的責(zé)任。另外,該程序依賴于Structs 1.1 jar文件,<war.bundle>true</war.bundle>表示這struts.jar將會打入到WAR包中。war插件將會把jar文件加入到WAR的WEB-INF/lib目錄下,而Ant中則需要將jar文件手工拷貝到WEB-INF/lib目錄。WAR還依賴于Foobar Services的foobar-services-2.0.jar文件,但是我們并不需要將它拷貝到WEB-INF/lib目錄,因?yàn)樵摪且蕾嚢珽JB、WEB都需要使用此包(記住所有在WEB-INF/lib和WEB-INF/classes 的jar或class都是由WAR class loader所加載的) 。因此將foobar-services jar文件的war.bundle 屬性設(shè)為false。所有的這一切都可以通過Maven的war插件完成。
Listing 11 maven.xml for the Web Project
<project>
<extend>${basedir}/../project.xml</extend>
<id>foobar-web</id>
<name>Foobar web application</name>
<package>foobar.webapp.*</package>
<description>Foobar Web project.</description>
<dependencies>
<dependency>
<groupId>j2ee</groupId>
<artifactId>j2ee</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>jakarta-struts</groupId>
<artifactId>jakarta-struts</artifactId>
<version>1.0.2</version>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>Foobar-Travels</groupId>
<artifactId>foobar-services</artifactId>
<version>${pom.currentVersion}</version>
<properties>
<war.bundle>false</war.bundle>
<war.manifest.classpath>true</war.manifest.classpath>
</properties>
</dependency>
</dependencies>
</project>
However the Maven war plugin lacks one feature. It does not have the capability to set the manifest classpath. Without this feature you cannot expect to use dependency libraries. Hence I decided to add this feature to the war plugin. Only two simple changes were required to achieve this. I decided to illustrate this change to show how intuitive and easy it is to write plugins in Jelly or customize the ones that already exist. Open the plugin.jelly for the war plugin from C:/Documents And Settings/<login-id>/.maven/plugins/maven-war-plugin-<version>-[SNAPSHOT]. These changes have to be done in the goal named war (The actual WAR - deployment archive, is built in a goal named as war in the war plugin). Add the following code in that goal.
但是目前的Maven的war插件還存在著一定的缺陷,它無法設(shè)置manifest的classpath(譯者注:此功能已經(jīng)在Maven 1.02中完成),你無法正常的使用依賴庫。因此我決定給插件添加以下功能,只需要做兩處改變即可。下面講述如何使用Jelly簡單的修改現(xiàn)有插件,或者自己寫一個(gè)插件。在C:/Documents And Settings/<login-id>/.maven/plugins/maven-war-plugin-<version>-[SNAPSHOT]目錄中打開war插件的plugin.helly,找到名為war的goal(事實(shí)上WAR發(fā)布包就是使用war插件中的名為war的構(gòu)建的),添加下面的內(nèi)容。
<j:forEach var="dep" items="${pom.dependencies}">
<j:if test="${dep.getProperty('war.manifest.classpath')=='true'}">
<j:set var="maven.war.classpath"
value="${maven.war.classpath} ${dep.artifact}"/>
</j:if>
</j:forEach>
This code iterates over each of the dependencies listed in the project.xml (identified by pom.dependencies) and checks if the war.manifest.classpath property is set to true. If so, then it appends that artifact name to a property called maven.war.classpath.
這里的代碼將會遍歷project.xml中的所有依賴包(由pom.dependencies可以辨別),然后檢查war.manifest.classpath是否被設(shè)為true。如果真,則將此制品的名字加入到屬性maven.war.classpath中。
The second change is needed when the manifest file is written out. The manifest file is created by the ant plugin by executing a goal named manifest. The manifest creation code is shown below. The line in bold is the line I inserted to set the manifest classpath attribute. This uses the previously set maven.war.classpath.
第二處改變在manifest文件生成的時(shí)候。在執(zhí)行名為manifest的goal的時(shí)候,Maven將調(diào)用Ant的插件,創(chuàng)建生成manifest文件。Manifest的創(chuàng)建代碼如下顯示。黑體部分是我寫的設(shè)置manifest的classpath屬性的代碼,它將使用到前面設(shè)置的maven.war.classpath屬性。
<ant:manifest>
<ant:attribute name="Built-By" value="${user.name}" />
<ant:attribute name="Class-Path" value="${maven.war.classpath}"/>
<ant:section name="${pom.package}">
<ant:attribute name="Specification-Title"
value="${pom.artifactId}" />
<ant:attribute name="Specification-Version"
value="${pom.currentVersion}" />
<ant:attribute name="Specification-Vendor"
value="${pom.organization.name}" />
</ant:section>
</ant:manifest>
That was simple wasn't it! Hopefully this will inspire you to write your own plugins when needed, instead of repetitively adding tasks into preGoals and postGoals.
非常簡單,不是么?也許這會激發(fā)你在必要的時(shí)候?qū)懽约旱牟寮6皇窍笙惹澳菢硬粩嗟脑谌蝿?wù)中插入preGoal和postGoal。
Written by Srikanth Shenoy November 2003
Translated by jinfeng wang 2005年三月
原文地址:http://www.theserverside.com/articles/article.tss?l=MavenMagic
本文的翻譯已經(jīng)原作者同意,轉(zhuǎn)載請保持原文。如有問題和意見可以和原作者或我聯(lián)系。
Part 1: http://m.tkk7.com/jinfeng_wang/archive/2005/03/11/1956.html
Part 3: http://m.tkk7.com/jinfeng_wang/archive/2005/03/15/2089.html