明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線(xiàn)學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

使用Java技術(shù)在Cocoon中完成商業(yè)邏輯

[摘要]如果你對(duì)Apache Cocoon有了解或者以涉足,你可能對(duì)在Cocoon中如何使用Java更好 的實(shí)現(xiàn)特定的邏輯有所疑惑。這篇文章將給您介紹如何使用XSP(Extensible Server Page) 和Action。同時(shí)還有示例和設(shè)計(jì)原則。 你可能聽(tīng)到了一些來(lái)自Apache的關(guān)于Cocoon...
如果你對(duì)Apache Cocoon有了解或者以涉足,你可能對(duì)在Cocoon中如何使用Java更好
的實(shí)現(xiàn)特定的邏輯有所疑惑。這篇文章將給您介紹如何使用XSP(Extensible Server Page)
和Action。同時(shí)還有示例和設(shè)計(jì)原則。

你可能聽(tīng)到了一些來(lái)自Apache的關(guān)于Cocoon的聲音。現(xiàn)在,在經(jīng)歷了三年的發(fā)展后,
Cocoon已經(jīng)逐漸的從一個(gè)簡(jiǎn)單的實(shí)現(xiàn)XSL (Extensible Stylesheet Language) 轉(zhuǎn)換
的servlet成長(zhǎng)為一個(gè)飽滿(mǎn)的Web應(yīng)用框架。

Cocoon是用Java開(kāi)發(fā)的,一般做為servlet運(yùn)行在象Tomcat這樣的servlet容器中。


在這篇文章里,我們將介紹到兩種辦法來(lái)在基于Cocoon的應(yīng)用中使用Java實(shí)現(xiàn)商業(yè)邏輯。

首先,讓我們來(lái)總的了解一下Cocoon。

Cocoon正式的定義是一個(gè)XML發(fā)布引擎,我們可以理解Cocoon為一個(gè)用來(lái)產(chǎn)生、轉(zhuǎn)換、處理
和輸出數(shù)據(jù)的框架。也可以理解Cocoon是一個(gè)從多種數(shù)據(jù)源接收數(shù)據(jù)再應(yīng)用不同的處理,最
后將數(shù)據(jù)以想要的格式輸出的機(jī)器。

我們也可以定義Cocoon是一個(gè)數(shù)據(jù)流機(jī)器。也就是說(shuō),當(dāng)你使用Cocoon時(shí),你定義數(shù)據(jù)的路
徑或者流程來(lái)產(chǎn)生Web應(yīng)用的頁(yè)面。


下面是Cocoon主要的一些基本的原理:

1、Cocoon把所有的數(shù)據(jù)做為SAX (Simple API for XML) 事件來(lái)處理,任何非XML的數(shù)據(jù)都要
轉(zhuǎn)變成XML描述。

2、生成器(能生成SAX事件)的組件負(fù)責(zé)處理輸入數(shù)據(jù)

3、序列化器負(fù)責(zé)處理輸出數(shù)據(jù),將數(shù)據(jù)輸出到客戶(hù)端(瀏覽器、文件等)。

4、開(kāi)發(fā)人員組合生成器、序列化器和其它組件構(gòu)成管道。所有的管道都在一個(gè)叫做站點(diǎn)地圖的
文件中定義。

5、通過(guò)URI (Uniform Resource Identifier)來(lái)匹配管道,但URI是與物理資源脫離的。


第5點(diǎn)需要說(shuō)明一下:

對(duì)于傳統(tǒng)的Web server,URI一般映射到物理資源。
例如,這個(gè)URI http://localhost/index.html 在Apache server將映射到一個(gè)叫index.html的
HTML文件。

在Cocoon中,URIs 和 物理資源可以是沒(méi)有任何絕對(duì)的相互關(guān)系的。你可以自由的設(shè)計(jì)URI來(lái)幫
助用戶(hù)更好的瀏覽你的站點(diǎn)。最后,你可以更好的組織你的文件讓其容易管理和維護(hù)。

為了更好的了解Cocoon的處理模型,可以看一個(gè)簡(jiǎn)單的管道。

下面這個(gè)例子定義了一個(gè)叫index.html的頁(yè)面。這個(gè)管道位于叫sitemap.xmap站點(diǎn)地圖中:

<map:match pattern="index.html">
<map:generate type="file" src="content/mainfile.xml"/>
<map:transform type="xslt" src="content/stylesheets/mainstyle.xsl"/>
<map:serialize type="html"/>
</map:match>


這個(gè)管道有三步:

首先是一個(gè)生成器組件FileGenerator從XML文件"content/mainfile.xml "讀取數(shù)據(jù)。
(FileGenerator實(shí)際上已經(jīng)提前在地圖中定義,可以通過(guò)"type"屬性來(lái)引用。Cocoon中所有的
管道組件都是通過(guò)它們的type屬性來(lái)引用的。)

接著進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換器TraxTransformer將XSL stylesheet應(yīng)用到引入的數(shù)據(jù)。

最后,序列化器HTMLSerializer將數(shù)據(jù)寫(xiě)到客戶(hù)端的瀏覽器。


你可能疑惑,上面所說(shuō)的和Java開(kāi)發(fā)有什么聯(lián)系呢?

我們把Cocoon的應(yīng)用分成三個(gè)部分:

1、數(shù)據(jù)的收集層 Data Collection (Generation)

2、數(shù)據(jù)的處理和轉(zhuǎn)換層 Data Processing&Transforming

3、數(shù)據(jù)的輸出層 Data Output(Serialization)

那么,Java開(kāi)發(fā)在Cocoon的處理轉(zhuǎn)換層是非常重要的。Cocoon的轉(zhuǎn)換和處理層是基于Cocoon的
應(yīng)用的核心,通過(guò)這一層對(duì)輸入數(shù)據(jù)的處理,邏輯的應(yīng)用,你就可以得到所期望的輸出。


在Cocoon中,你可以有下面四種實(shí)現(xiàn)邏輯的方法:

1、使用轉(zhuǎn)換器(Transformer)組件:他們按照你給定的規(guī)則轉(zhuǎn)換傳入的數(shù)據(jù)。典型的例子
便是TraxTransformer。


2、通過(guò)不同的 request、session、URI來(lái)選擇不同的組件做出正確的處理。

3、使用已有的或者自己實(shí)現(xiàn)的Action。

4、使用混合了Java代碼和內(nèi)容的XSP。


這篇文章介紹最后兩種辦法:XSP 和 Action。XSP 和 Action的開(kāi)發(fā)都是在servlet context內(nèi)。
確切的說(shuō),兩種組件(實(shí)際上是所有的組件)都要存取request, response, session, 和
context對(duì)象。在某些方面,你要實(shí)現(xiàn)的大量的邏輯都會(huì)與這些對(duì)象相互作用。


XSP

XSP是Cocoon項(xiàng)目的創(chuàng)新。你可以把它和JSP相比較,因?yàn)樗鼈兌际腔旌线壿嫼蛢?nèi)容而且JSP的
taglib和XSP的logicsheet也很相似。

XSP位于管道的起點(diǎn),實(shí)際上它被Cocoon轉(zhuǎn)換成生成器(Generator)來(lái)給管道中其余的組件提
供數(shù)據(jù)。

讓我們看下面這個(gè)叫 sample1.xsp 簡(jiǎn)單的示例:

<?xml version="1.0"?>

<xsp:page language="java" xmlns:xsp="http://apache.org/xsp">

<xsp:logic>
Date now = new Date();
String msg = "Boo!";
</xsp:logic>

<content>
<title>Welcome to Cocoon</title>

<paragraph>

This is an XSP. You can see how we it contain both logic
(inside the <xsp:logic> tags) and content. In the logic block
above, we created a Date object whose value is <xsp:expr>now</xsp:expr>.
Oh, we also had a special message for you: <xsp:expr>msg</xsp:expr>

</paragraph>
</content>

</xsp:page>

首先注意這個(gè)文檔的根標(biāo)記是<xsp:page>。

<xsp:page language="java" xmlns:xsp="http://apache.org/xsp">

這個(gè)標(biāo)記定義XSP的language(可以是Java或者JavaScript)和用到的邏輯單的namespace。

接著是我們定義了兩個(gè)Java變量的<xsp:logic>塊。

這些<xsp:logic>塊可以有多個(gè),可以出現(xiàn)在你希望的任何地方,而且可以包含各種Java代碼。

最后,是我們自己的內(nèi)容,從用戶(hù)自己的跟標(biāo)簽開(kāi)始,在上面的示例中是<content>。在內(nèi)容部分里,
我們可以用<xsp:expr>這個(gè)標(biāo)簽得到在前面定義的變量。

記住,一個(gè)XSP實(shí)際上就是一個(gè)生成器Generator。Cocoon將其轉(zhuǎn)換成Java源文件然后編譯、執(zhí)行它。
(如果想看XSP轉(zhuǎn)換成的Java源文件,到你的servlet容器的工作路徑下去找。例如,
如果你使用Tomcat 4.0.4,那么路徑就是下面這樣:
$CATALINA_HOME/work/Standalone/localhost/cocoon/cocoon-files/org/apache/cocoon/www.)

XSP執(zhí)行后產(chǎn)生的XML數(shù)據(jù)被傳遞給管道其余的組件。


看下面這個(gè)管道實(shí)例:

<map:pipeline match="*.xsp">
<map:generate type="serverpages" src="examples/{1}.xsp"/>
<map:serialize type="xml"/>
</map:pipeline>


這里,我們使用一個(gè)指定的生成器 ServerPagesGenerator,來(lái)處理我們簡(jiǎn)單的XSP。返回給客戶(hù)
端未加修飾的XML。

注意例子中使用了特別的 {1} 變量引用:它代替值在管道開(kāi)始處的通配符指示的值。也就是說(shuō),
如果我們?cè)跒g覽器中打開(kāi)我們的Web應(yīng)用中的sample1.xsp,那么 {1}的值便是sample1。

記住,同多數(shù)Cocoon組件一樣,XSP訪(fǎng)問(wèn)request, response, session, 和 context 對(duì)象。這些
對(duì)象實(shí)際上是HttpServletRequest, HttpServletResponse, HttpSession, 和
HttpServletContext的封裝,Cocoon正式版本提供了大量的存取這些對(duì)象的方法。


XSP在從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)的時(shí)候特別有用。

數(shù)據(jù)庫(kù)數(shù)據(jù)自然地以行和列組織,所以數(shù)據(jù)庫(kù)數(shù)據(jù)很容易轉(zhuǎn)換到XML。然而,JDBC
(Java Database Connectivity)沒(méi)有適合地代碼完成向XML的轉(zhuǎn)換。

XSP可以讓我們?cè)谧x取數(shù)據(jù)時(shí)很容易,這要感謝ESQL 邏輯單。ESQL 邏輯單除了隱藏了詳細(xì)
的JDBC代碼,還允許將行和列放入到特定的標(biāo)簽中。同時(shí)ESQL 邏輯單也可以執(zhí)行嵌套查詢(xún)
和執(zhí)行更新命令。

下面,我們舉個(gè)XSP應(yīng)用的例子:

假如我們想將一些Cocoon的資源(名稱(chēng)和URL)存儲(chǔ)到數(shù)據(jù)庫(kù)。


首先,我們定義存放資源的數(shù)據(jù)表,然后當(dāng)用戶(hù)通過(guò)關(guān)鍵字搜索時(shí),我們使用XSP來(lái)找到相應(yīng)
的行,將數(shù)據(jù)顯示給用戶(hù)。

隨后,我們構(gòu)建一個(gè)表單來(lái)增加新的列。


表的定義和插入的數(shù)據(jù)如下面所示。我們這里使用的數(shù)據(jù)庫(kù)是MySQL,如果您使用的是其它的
數(shù)據(jù)庫(kù),要注意做相應(yīng)的改動(dòng)。這個(gè)例子中,必須要有配置好數(shù)據(jù)庫(kù)連接池。

表結(jié)構(gòu)如下:

use test;

create table Resources (
ResourceURL varchar(255) not null,
ResourceName varchar(64) not null
);

插入一些資源數(shù)據(jù):

insert into Resources values
(&acute;;http://xml.apache.org/cocoon&acute;;, &acute;Cocoon Home Page&acute;);

insert into Resources values
(&acute;;http://www.galatea.com/flashguides/cocoon-tips-2.xml&acute;;, &acute;Cocoon 2.0 Tips and Tricks&acute;);



表建好后并且Cocoon也正確的配置過(guò)后,我們就可以寫(xiě)下面這個(gè)XSP例子:

<?xml version="1.0"?>

<xsp:page language="java"
xmlns:xsp="http://apache.org/xsp"
xmlns:esql="http://apache.org/cocoon/SQL/v2">

<xsp:logic>
String keyword = request.getParameter("value");
</xsp:logic>

<content>
<title>Search results</title>
<esql:connection>
<esql:pool>resources</esql:pool>
<esql:execute-query>
<esql:query>
select * from Resources
where ResourceName like &acute;%<xsp:expr>keyword</xsp:expr>%&acute;
</esql:query>
<esql:results>
<resources>
<esql:row-results>
<resource>
<esql:get-columns/>
</resource>
</esql:row-results>
</ resources >
</esql:results>
</esql:execute-query>
</esql:connection>

</content>
</xsp:page>



注意在<xsp:page>標(biāo)簽中聲明的名稱(chēng)空間(namespace)。任何時(shí)候,在XSP中使用邏輯單的時(shí)候,
必須要聲明其名稱(chēng)空間(namespace)。你可以在Cocoon webapp路徑下的WEB-INF/cocoon.xconf找
到邏輯單的定義。XSP 名稱(chēng)空間的聲明就是要說(shuō)明這是個(gè)XSP邏輯單。

實(shí)際上,所有的XSP至少要實(shí)現(xiàn)XSP邏輯單。在XSP被轉(zhuǎn)換成Java源文件之前,其中的邏輯單
(實(shí)際上僅是XSL文件)會(huì)先做XSLT處理轉(zhuǎn)換成Java代碼。因此,在上面的例子中的所有的
ESQL標(biāo)簽都會(huì)轉(zhuǎn)換成了我們所熟悉的JDBC代碼。但是并不是所有的標(biāo)簽都可以變成JDBC代碼,
注意上面示例中的<esql:pool>塊,它涉及到了在WEB-INF/cocoon.xconf文件中定義的數(shù)據(jù)庫(kù)
連接池。上面程序中使用的連接池叫做"resources" ,當(dāng)然你可以使用你所喜歡的定義。

注意,我們這里使用<resources> 這個(gè)標(biāo)簽將結(jié)果集包了起來(lái)而且每行的數(shù)據(jù)都放到<resource>標(biāo)
簽里。這樣我們就可以很容易的編寫(xiě)樣式表來(lái)將XML轉(zhuǎn)換成其它瀏覽器可以理解的格式。我們沒(méi)有
為表的列定義任何標(biāo)簽,通過(guò)使用<esql:get-columns/>,Cocoon會(huì)將每一列的值放到自動(dòng)以相應(yīng)
的列名定義的標(biāo)簽里面。


現(xiàn)在,讓我注意一下例子中的SQL查詢(xún)語(yǔ)句,正如你所看到的,這條SQL是動(dòng)態(tài)生成的。當(dāng)用戶(hù)
通過(guò)GETs 或者 POSTs提交數(shù)據(jù)到這個(gè)XSP后,在XSP的頂部,我們將request參數(shù)的值賦給
了keyword變量,然后根據(jù)keyword組成SQL語(yǔ)句。


既然這個(gè)例子很簡(jiǎn)單,讓我們把它變復(fù)雜一點(diǎn),加入Email功能,可以在用戶(hù)提供Email地址后,
將查詢(xún)結(jié)果發(fā)送給用戶(hù)。

XSP示例如下:

<?xml version="1.0"?>

<xsp:page language="java"
xmlns:xsp="http://apache.org/xsp"
xmlns:esql="http://apache.org/cocoon/SQL/v2"
xmlns:sendmail="http://apache.org/cocoon/sendmail/1.0"
xmlns:xsp-request="http://apache.org/xsp/request/2.0"
>

<content>

<xsp:logic>
String keyword = <xsp-request:get-parameter name="value"/>;
String emailAddr = <xsp-request:get-parameter name="email"/>;
String emailBody = "";
</xsp:logic>

<title>Search results</title>
<esql:connection>
<esql:pool>resources</esql:pool>
<esql:execute-query>
<esql:query>
select * from Resources where ResourceName like
&acute;%<xsp:expr>keyword</xsp:expr>%&acute; order by ResourceName
</esql:query>
<esql:results>
<resources>
<esql:row-results>
<resource>
<xsp:logic>
emailBody += <esql:get-string column="ResourceName"/>;
emailBody += ", " + <esql:get-string column="ResourceURL"/> + "\n";
</xsp:logic>
<esql:get-columns/>
</resource>
</esql:row-results>
</resources>
</esql:results>
</esql:execute-query>
</esql:connection>

<xsp:logic>
if (emailAddr != null) {
<sendmail:send-mail>
<sendmail:charset>ISO-8859-1</sendmail:charset>
<sendmail:smtphost>MY_SMTP_HOST</sendmail:smtphost>
<sendmail:from>MY_FROM_ADDRESS</sendmail:from>
<sendmail:to><xsp:expr>emailAddr</xsp:expr></sendmail:to>
<sendmail:subject>Cocoon Search Results</sendmail:subject>
<sendmail:body><xsp:expr>emailBody</xsp:expr></sendmail:body>
</sendmail:send-mail>
}
</xsp:logic>

</content>
</xsp:page>


來(lái)自sendmail邏輯單的幾個(gè)標(biāo)簽讓我們擁有了發(fā)送EMAIL的能力。在這個(gè)例子中,我們將查詢(xún)結(jié)果
的每一行相加賦值給emailBody變量做為郵件的正文。當(dāng)用戶(hù)通過(guò)request參數(shù)提供一個(gè)EMAIL地址,
我們就可以發(fā)送EMAIL了。當(dāng)然這需要您提前設(shè)定好SMTP服務(wù)器和FROM地址。

Cocoon知道根據(jù)sendmail邏輯單來(lái)處理在sendmail名稱(chēng)空間里的標(biāo)簽,因?yàn)檫@個(gè)名稱(chēng)空間已經(jīng)
在<xsp:page>標(biāo)簽中已經(jīng)聲明。查看示例中的聲明,你會(huì)看到xsp-request這個(gè)名稱(chēng)空間。
xsp-request邏輯單提供了Request常用方法的封裝。雖然在XSP中直接訪(fǎng)問(wèn)request對(duì)象和
使用xsp-request邏輯單沒(méi)有什么功能上的區(qū)別,但是,理論上使用logicsheet的標(biāo)簽比直
接的JAVA代碼更優(yōu)美。

在運(yùn)行這個(gè)例子之前,你必須要先在cocoon.xconf文件中設(shè)置sendmail邏輯單,Cocoon的配置
文件都在Web application 的 WEB-INF目錄下。用你熟悉的編輯器打開(kāi)cocoon.xconf文件,
找到<target-language name="java">標(biāo)簽。在這個(gè)塊內(nèi),你會(huì)發(fā)現(xiàn)所有其它邏輯單的定義。
在最后一個(gè)邏輯單(SOAP邏輯單)后加入下面的內(nèi)容:

<builtin-logicsheet>
<parameter name="prefix" value="mail"/>
<parameter name="uri" value="http://apache.org/cocoon/sendmail/1.0"/>
<parameter name="href"
value="resource://org/apache/cocoon/components/language/markup/xsp/java/sendmail.xsl"/>
</builtin-logicsheet>


這個(gè)定義將http://apache.org.cocoon/sendmail/1.0名稱(chēng)空間和已經(jīng)包括在Cocoon JAR中
的sendmail.xsl樣式表聯(lián)合起來(lái)。

要使用sendmail邏輯單的功能,Cocoon必須要 mail.jar 和 activation.jar這兩個(gè)JAR。
如果您使用的Server是Tomcat4.x的話(huà),那么它們位于$CATALINA_HOME/common/lib。



Actions


Action功能很強(qiáng)大,你可以將它放到管道的任何地方。Action可以認(rèn)為是小的自包含的機(jī)器,
它獲取某些輸入數(shù)據(jù),做一些處理,然后返回HashMap對(duì)象。不同于Cocoon中的Generators,
Transformers, Serializers組件,Action不對(duì)實(shí)際的XML內(nèi)容做任何事情,它主要在管道中
實(shí)現(xiàn)一些邏輯。


學(xué)習(xí)Action包括要對(duì)管道參數(shù)做些了解,有時(shí)管道的組件必須交流數(shù)據(jù)。當(dāng)然,XML內(nèi)容會(huì)通
過(guò)SAX事件傳遞;但是,我們所說(shuō)的是管道組件本身的功能需要的值。

管道參數(shù)有兩種:input 和 output。Input參數(shù)是由緊跟在組件聲明后面的一個(gè)或者多個(gè)
<map:parameter>標(biāo)簽來(lái)定義的。它們?yōu)榻M件提供一個(gè)或者多個(gè)值來(lái)影響其操作。

Matcher和Action這兩個(gè)組件可以為它們后面的組件提供能存取的Output變量。
這些output參數(shù)放在HashMap對(duì)象里,可以通過(guò)key名稱(chēng)(如:{1})來(lái)引用。
所有的管道都至少有一個(gè)由管道開(kāi)始處的Matcher提供的HashMap。我們?cè)诠艿乐惺褂眠@個(gè)
HashMap對(duì)象,使用{1}可以取得HashMap中Key為1的值。


Cocoon本身包含一些內(nèi)置的Action。其中有一個(gè)是依靠數(shù)據(jù)庫(kù)來(lái)鑒別用戶(hù)的Action。當(dāng)我們
想保護(hù)Cocoon中的某些頁(yè)面,只允許授權(quán)的用戶(hù)訪(fǎng)問(wèn)時(shí),可以將用戶(hù)的ID和密碼存儲(chǔ)到數(shù)據(jù)
庫(kù)里,然后使用DatabaseAuthenticationAction來(lái)做登錄確認(rèn)。
這個(gè)DatabaseAuthenticationAction要求我們提供一個(gè)XML描述文件來(lái)說(shuō)明要使用哪個(gè)表和哪
些列。下面是這個(gè)描述文件的示例:


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

<auth-descriptor>
<connection>resources</connection>
<table name="Users">
<select dbcol="USER_NAME" request-param="userid" to-session="userid"/>
<select dbcol="USER_PASSWORD" request-param="userpwd"/>
</table>
</auth-descriptor>


上面這個(gè)文件說(shuō)明用戶(hù)認(rèn)證Action將使用resources連接池和User表,request提交的userid參數(shù)
和userpwd參數(shù)將與User表中的USER_NAME和USER_PASSWORD列來(lái)比較,如果成功確認(rèn),將參
數(shù)userid寫(xiě)到session里。

當(dāng)你在管道中使用一個(gè)Action時(shí),它必須先在站點(diǎn)地圖中的<map:components>塊中的
<map:actions>塊中定義。如下:

<map:components>
<!-- all other component definitions go here -->
<map:actions>
<map:action name="authenticator"
src="org.apache.cocoon.acting.DatabaseAuthenticatorAction"/>
<!-- other actions definitions go here -->
</map:actions>
</map:components>

一旦定義過(guò)后,就可以使用這個(gè)Action來(lái)負(fù)責(zé)我們要保護(hù)的區(qū)域。下面為要保護(hù)的區(qū)域定
義了三個(gè)管道:


<map:match pattern="protected/login.html">
<map:read mime-type="text/html" src="secret/login.html"/>
</map:match>

<map:match pattern="protected/login">
<map:act type="authenticator">
<map:parameter name="descriptor" value=" secret/auth-info.xml"/>
<map:redirect-to uri="index.html"/>
</map:act>

<map:redirect-to uri="login.html"/>
</map:match>

<map:match pattern="protected/*">
<map:match type="sessionstate" pattern="*">
<map:parameter name="attribute-name" value="userid"/>

<map:match pattern="protected/*.html">
<map:read mime-type="text/html" src=" secret/*.html"/>
</map:match>

<map:match pattern="protected/*.xsp">
<map:generate type="serverpages" src=" secret/{1}.xsp"/>
<map:serialize type="xml"/>
</map:match>
</map:match>

<map:redirect-to uri="login.html"/>
</map:match>


第一個(gè)管道簡(jiǎn)單的提供了一個(gè)登錄的表單,是個(gè)HTML文件,不需要轉(zhuǎn)換。第二個(gè)管道處理
從login.html提交的實(shí)際的登錄動(dòng)作。第三個(gè)來(lái)處理我們要保護(hù)的內(nèi)容。

下面我們做詳細(xì)的論述:

DatabaseAuthenticationAction依靠描述文件來(lái)驗(yàn)證登錄。但是,我們?nèi)绾沃莉?yàn)證是否
成功呢?對(duì)于Action,如果它們返回了有效的HashMap,那么在<map:act>塊里的部分將執(zhí)
行。如果返回null值,那么塊下面的部分將執(zhí)行。也就是說(shuō),按照上面管道的定義,我們
有兩種可能的結(jié)果,即:如果認(rèn)證通過(guò),我們就可以到達(dá)受保護(hù)的區(qū)域,如果失敗將返回
到Login頁(yè)面。

在 protected/* 管道有幾個(gè)嵌套的Matcher,第二個(gè)的type是sessionstate,這個(gè)Matcher
實(shí)際上是WildcardSessionAttributeMatcher,在這里用來(lái)讀取session里的userid的值。

在這個(gè)例子中,我們知道DatabaseAuthenticationAction設(shè)置了一個(gè)叫userid的session屬
性,我們通過(guò)檢測(cè)userid屬性來(lái)判斷用戶(hù)是否登錄成功,如果它不存在,則轉(zhuǎn)向到login頁(yè)面。

在Cocoon已經(jīng)有一個(gè)DatabaseAddAction可用來(lái)插入數(shù)據(jù),但為了更好的理解Action,我們
將寫(xiě)一個(gè)自己的Action用來(lái)將新的Resource記錄插入到Resources表中。

我們假想你已經(jīng)編寫(xiě)了一個(gè)HTML頁(yè)面,可用來(lái)POST兩個(gè)變量name和url到管道。我們的
Action將從Request對(duì)象中找回name和url參數(shù),將其插入到表中,最后返回一個(gè)HashMap對(duì)象。

下面是程序代碼:

package test;

import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentSelector;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.acting.AbstractAction;

import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;

import java.util.HashMap;
import java.util.Map;

public class AddResourceAction extends AbstractAction
implements ThreadSafe, Composable, Disposable
{
protected ComponentSelector dbselector;
protected ComponentManager manager;

public void compose(ComponentManager manager) throws ComponentException {
this.dbselector =
(ComponentSelector) manager.lookup(DataSourceComponent.ROLE + "Selector");

}

protected final DataSourceComponent getDataSource(String pool)
throws ComponentException {
return (DataSourceComponent) this.dbselector.select(pool);
}

public Map act( Redirector redirector, SourceResolver resolver,
Map objectModel, String
source, Parameters param )
throws Exception
{
Request req = ObjectModelHelper.getRequest(objectModel);
Session ses = req.getSession(true);

String poolName = param.getParameter("pool");

String resourceName = req.getParameter("name");
String resourceUrl = req.getParameter("url");

if (poolName == null) {
getLogger().error("Missing a pool name");
return null;
}

if (resourceName == null resourceUrl == null) {
getLogger().error("Missing input parameters");
return null;
}

Map map = new HashMap();

DataSourceComponent dataSource = getDataSource(poolName);
Connection conn = null;
boolean status = false;

try {
conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
String cmd = "insert into Resources values (&acute;" +
resourceName + "&acute;, &acute;" +
resourceUrl + "&acute;)";
stmt.executeUpdate(cmd);

map.put("resource-name", resourceName);
map.put("resource-url", resourceUrl);

getLogger().debug("Resources insert completed by user " +
ses.getId());

status = true;
stmt.close();
} catch (Exception e) {
getLogger().error("Stmt failed: ", e);
} finally {
try {
if (conn != null) conn.close();
} catch (SQLException sqe) {
getLogger().warn("Error closing the datasource", sqe);
}
}

if (!status) return null;
return(map);
}

public void dispose() {
this.manager.release(dbselector);
}
}

這兒有大量的東西需要消化,特別是如果你不熟悉Cocoon的結(jié)構(gòu)。我們一步步的來(lái)說(shuō)明。

首先,Cocoon action的主方法是 act(),當(dāng)在管道中使用Action時(shí)Cocoon將調(diào)用這個(gè)方法。
在這個(gè)示例中,act()得到Request參數(shù)、從連接池中得到數(shù)據(jù)庫(kù)連接,執(zhí)行插入,然后填充
HashMap對(duì)象,并將其返回。

在Act方法的開(kāi)始是從ObjectModelHelper組件中取得Request對(duì)象,然后得到兩個(gè)參數(shù)。這個(gè)
Action需要另外一個(gè)參數(shù),pool;它將告訴我們使用哪個(gè)連接池。如果這個(gè)參數(shù)沒(méi)有,那么
Action將返回null而且將錯(cuò)誤寫(xiě)到日志里。有了pool的名稱(chēng),我們就可以從連接池得到數(shù)據(jù)
庫(kù)的連接。Avalon的Excalibur組件用來(lái)負(fù)責(zé)Cocoon的連接池。如果你不熟悉Avalon,可以
訪(fǎng)問(wèn)這里http://jakarta.apache.org/avalon/ 。

代碼中的insert statement是直接的JDBC語(yǔ)法。在插入成功后,會(huì)將成功的信息寫(xiě)到日志里。
對(duì)于日志,如果按上面Action的寫(xiě)法,所有的日志信息都寫(xiě)到你的Web Application的
WEB-INF/logs/sitemap.log文件。

最后,我們將兩個(gè)輸入?yún)?shù)寫(xiě)到了Map對(duì)象,雖然它們都在Request對(duì)象中,這樣做是多余的,
但我們這樣做是為了示例Map對(duì)象的用法。

看一下這個(gè)Action在站點(diǎn)地圖中的定義。我們必須首先在站點(diǎn)地圖的<map:components>區(qū)定
義這個(gè)Action。

<map:components>
<!-- all other component definitions go here -->
<map:actions>
<map:action name="authenticator"
src="org.apache.cocoon.acting.DatabaseAuthenticatorAction"/>
<map:action name="add-resource" src="test.AddResourceAction"
logger="sitemap.action.AddResourceAction"/>
<!-- other actions definitions go here -->
</map:actions>
</map:components>


在管道中使用這個(gè)Action:


<map:match pattern="addresource">
<map:act type="add-resource">
<map:parameter name="pool" value="resources"/>
<map:read mime-type="text/html" src="examples/confirmation.html"/>
</map:act>

<map:read mime-type="text/html" src="examples/addresource.html"/>
</map:match>

可以看到,在<map:act> 行的下面,緊跟著的<map:parameter>標(biāo)簽為Action提供"pool"參數(shù)。
一切順利的話(huà),Action將返回一個(gè)Map對(duì)象,confirmation頁(yè)面將被顯示。


在瀏覽器中打開(kāi)http://localhost:8080/cocoon/addresource,你會(huì)看到一個(gè)輸入表單。
表單提交后,如果插入成功,將顯示confirmation頁(yè)面,如果出現(xiàn)錯(cuò)誤,將再次返回到表單
頁(yè)面。查看WEB-INF/logs/sitemap.log,錯(cuò)誤信息會(huì)告訴你出現(xiàn)了什么錯(cuò)誤。


如何有效的使用XSP和Action?


XSP和Action是在Cocoon中實(shí)現(xiàn)邏輯的兩種不同的辦法。選擇哪一種更適合呢?

XSP在取數(shù)據(jù)或者創(chuàng)建結(jié)構(gòu)化的數(shù)據(jù)方面是很有用的。Action被證明在控制數(shù)據(jù)流程
(并不產(chǎn)生或者影響數(shù)據(jù))的邏輯實(shí)現(xiàn)上很有用。我們上面看到的用戶(hù)驗(yàn)證和數(shù)據(jù)庫(kù)
操作便是這樣的兩個(gè)例子。

然而,有一點(diǎn)需要說(shuō)明的問(wèn)題:XSP會(huì)將邏輯和內(nèi)容混合。而Cocoon的一個(gè)基本的原則
就是邏輯、內(nèi)容、表示的分離。

在使用XSP的時(shí)候,我們提出以下幾點(diǎn)建議:

首先,盡可能的使用邏輯單,邏輯單會(huì)很好的將Java代碼隱藏。

第二,盡量使用Cocoon的提供的功能,如:在做數(shù)據(jù)庫(kù)的Select的時(shí)候,我們也可以用
SqlTransformer來(lái)實(shí)現(xiàn)。

第三,在決策方面的邏輯盡可能的使用Selector, Matcher或Action組件。

最后,當(dāng)無(wú)法避免在你的XSP中插入Java邏輯的話(huà),盡可能的讓<xsp:logic>小,而且不要
把它們散布到各種你的標(biāo)簽中。