使用Java技術(shù)在Cocoon中完成商業(yè)邏輯
發(fā)表時間:2024-06-11 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]如果你對Apache Cocoon有了解或者以涉足,你可能對在Cocoon中如何使用Java更好 的實現(xiàn)特定的邏輯有所疑惑。這篇文章將給您介紹如何使用XSP(Extensible Server Page) 和Action。同時還有示例和設(shè)計原則。 你可能聽到了一些來自Apache的關(guān)于Cocoon...
如果你對Apache Cocoon有了解或者以涉足,你可能對在Cocoon中如何使用Java更好
的實現(xiàn)特定的邏輯有所疑惑。這篇文章將給您介紹如何使用XSP(Extensible Server Page)
和Action。同時還有示例和設(shè)計原則。
你可能聽到了一些來自Apache的關(guān)于Cocoon的聲音,F(xiàn)在,在經(jīng)歷了三年的發(fā)展后,
Cocoon已經(jīng)逐漸的從一個簡單的實現(xiàn)XSL (Extensible Stylesheet Language) 轉(zhuǎn)換
的servlet成長為一個飽滿的Web應(yīng)用框架。
Cocoon是用Java開發(fā)的,一般做為servlet運行在象Tomcat這樣的servlet容器中。
在這篇文章里,我們將介紹到兩種辦法來在基于Cocoon的應(yīng)用中使用Java實現(xiàn)商業(yè)邏輯。
首先,讓我們來總的了解一下Cocoon。
Cocoon正式的定義是一個XML發(fā)布引擎,我們可以理解Cocoon為一個用來產(chǎn)生、轉(zhuǎn)換、處理
和輸出數(shù)據(jù)的框架。也可以理解Cocoon是一個從多種數(shù)據(jù)源接收數(shù)據(jù)再應(yīng)用不同的處理,最
后將數(shù)據(jù)以想要的格式輸出的機器。
我們也可以定義Cocoon是一個數(shù)據(jù)流機器。也就是說,當(dāng)你使用Cocoon時,你定義數(shù)據(jù)的路
徑或者流程來產(chǎn)生Web應(yīng)用的頁面。
下面是Cocoon主要的一些基本的原理:
1、Cocoon把所有的數(shù)據(jù)做為SAX (Simple API for XML) 事件來處理,任何非XML的數(shù)據(jù)都要
轉(zhuǎn)變成XML描述。
2、生成器(能生成SAX事件)的組件負責(zé)處理輸入數(shù)據(jù)
3、序列化器負責(zé)處理輸出數(shù)據(jù),將數(shù)據(jù)輸出到客戶端(瀏覽器、文件等)。
4、開發(fā)人員組合生成器、序列化器和其它組件構(gòu)成管道。所有的管道都在一個叫做站點地圖的
文件中定義。
5、通過URI (Uniform Resource Identifier)來匹配管道,但URI是與物理資源脫離的。
第5點需要說明一下:
對于傳統(tǒng)的Web server,URI一般映射到物理資源。
例如,這個URI http://localhost/index.html 在Apache server將映射到一個叫index.html的
HTML文件。
在Cocoon中,URIs 和 物理資源可以是沒有任何絕對的相互關(guān)系的。你可以自由的設(shè)計URI來幫
助用戶更好的瀏覽你的站點。最后,你可以更好的組織你的文件讓其容易管理和維護。
為了更好的了解Cocoon的處理模型,可以看一個簡單的管道。
下面這個例子定義了一個叫index.html的頁面。這個管道位于叫sitemap.xmap站點地圖中:
<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>
這個管道有三步:
首先是一個生成器組件FileGenerator從XML文件"content/mainfile.xml "讀取數(shù)據(jù)。
(FileGenerator實際上已經(jīng)提前在地圖中定義,可以通過"type"屬性來引用。Cocoon中所有的
管道組件都是通過它們的type屬性來引用的。)
接著進行轉(zhuǎn)換,轉(zhuǎn)換器TraxTransformer將XSL stylesheet應(yīng)用到引入的數(shù)據(jù)。
最后,序列化器HTMLSerializer將數(shù)據(jù)寫到客戶端的瀏覽器。
你可能疑惑,上面所說的和Java開發(fā)有什么聯(lián)系呢?
我們把Cocoon的應(yīng)用分成三個部分:
1、數(shù)據(jù)的收集層 Data Collection (Generation)
2、數(shù)據(jù)的處理和轉(zhuǎn)換層 Data Processing&Transforming
3、數(shù)據(jù)的輸出層 Data Output(Serialization)
那么,Java開發(fā)在Cocoon的處理轉(zhuǎn)換層是非常重要的。Cocoon的轉(zhuǎn)換和處理層是基于Cocoon的
應(yīng)用的核心,通過這一層對輸入數(shù)據(jù)的處理,邏輯的應(yīng)用,你就可以得到所期望的輸出。
在Cocoon中,你可以有下面四種實現(xiàn)邏輯的方法:
1、使用轉(zhuǎn)換器(Transformer)組件:他們按照你給定的規(guī)則轉(zhuǎn)換傳入的數(shù)據(jù)。典型的例子
便是TraxTransformer。
2、通過不同的 request、session、URI來選擇不同的組件做出正確的處理。
3、使用已有的或者自己實現(xiàn)的Action。
4、使用混合了Java代碼和內(nèi)容的XSP。
這篇文章介紹最后兩種辦法:XSP 和 Action。XSP 和 Action的開發(fā)都是在servlet context內(nèi)。
確切的說,兩種組件(實際上是所有的組件)都要存取request, response, session, 和
context對象。在某些方面,你要實現(xiàn)的大量的邏輯都會與這些對象相互作用。
XSP
XSP是Cocoon項目的創(chuàng)新。你可以把它和JSP相比較,因為它們都是混合邏輯和內(nèi)容而且JSP的
taglib和XSP的logicsheet也很相似。
XSP位于管道的起點,實際上它被Cocoon轉(zhuǎn)換成生成器(Generator)來給管道中其余的組件提
供數(shù)據(jù)。
讓我們看下面這個叫 sample1.xsp 簡單的示例:
<?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>
首先注意這個文檔的根標記是<xsp:page>。
<xsp:page language="java" xmlns:xsp="http://apache.org/xsp">
這個標記定義XSP的language(可以是Java或者JavaScript)和用到的邏輯單的namespace。
接著是我們定義了兩個Java變量的<xsp:logic>塊。
這些<xsp:logic>塊可以有多個,可以出現(xiàn)在你希望的任何地方,而且可以包含各種Java代碼。
最后,是我們自己的內(nèi)容,從用戶自己的跟標簽開始,在上面的示例中是<content>。在內(nèi)容部分里,
我們可以用<xsp:expr>這個標簽得到在前面定義的變量。
記住,一個XSP實際上就是一個生成器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ù)被傳遞給管道其余的組件。
看下面這個管道實例:
<map:pipeline match="*.xsp">
<map:generate type="serverpages" src="examples/{1}.xsp"/>
<map:serialize type="xml"/>
</map:pipeline>
這里,我們使用一個指定的生成器 ServerPagesGenerator,來處理我們簡單的XSP。返回給客戶
端未加修飾的XML。
注意例子中使用了特別的 {1} 變量引用:它代替值在管道開始處的通配符指示的值。也就是說,
如果我們在瀏覽器中打開我們的Web應(yīng)用中的sample1.xsp,那么 {1}的值便是sample1。
記住,同多數(shù)Cocoon組件一樣,XSP訪問request, response, session, 和 context 對象。這些
對象實際上是HttpServletRequest, HttpServletResponse, HttpSession, 和
HttpServletContext的封裝,Cocoon正式版本提供了大量的存取這些對象的方法。
XSP在從數(shù)據(jù)庫讀取數(shù)據(jù)的時候特別有用。
數(shù)據(jù)庫數(shù)據(jù)自然地以行和列組織,所以數(shù)據(jù)庫數(shù)據(jù)很容易轉(zhuǎn)換到XML。然而,JDBC
(Java Database Connectivity)沒有適合地代碼完成向XML的轉(zhuǎn)換。
XSP可以讓我們在讀取數(shù)據(jù)時很容易,這要感謝ESQL 邏輯單。ESQL 邏輯單除了隱藏了詳細
的JDBC代碼,還允許將行和列放入到特定的標簽中。同時ESQL 邏輯單也可以執(zhí)行嵌套查詢
和執(zhí)行更新命令。
下面,我們舉個XSP應(yīng)用的例子:
假如我們想將一些Cocoon的資源(名稱和URL)存儲到數(shù)據(jù)庫。
首先,我們定義存放資源的數(shù)據(jù)表,然后當(dāng)用戶通過關(guān)鍵字搜索時,我們使用XSP來找到相應(yīng)
的行,將數(shù)據(jù)顯示給用戶。
隨后,我們構(gòu)建一個表單來增加新的列。
表的定義和插入的數(shù)據(jù)如下面所示。我們這里使用的數(shù)據(jù)庫是MySQL,如果您使用的是其它的
數(shù)據(jù)庫,要注意做相應(yīng)的改動。這個例子中,必須要有配置好數(shù)據(jù)庫連接池。
表結(jié)構(gòu)如下:
use test;
create table Resources (
ResourceURL varchar(255) not null,
ResourceName varchar(64) not null
);
插入一些資源數(shù)據(jù):
insert into Resources values
(´;http://xml.apache.org/cocoon´;, ´Cocoon Home Page´);
insert into Resources values
(´;http://www.galatea.com/flashguides/cocoon-tips-2.xml´;, ´Cocoon 2.0 Tips and Tricks´);
表建好后并且Cocoon也正確的配置過后,我們就可以寫下面這個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 ´%<xsp:expr>keyword</xsp:expr>%´
</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>標簽中聲明的名稱空間(namespace)。任何時候,在XSP中使用邏輯單的時候,
必須要聲明其名稱空間(namespace)。你可以在Cocoon webapp路徑下的WEB-INF/cocoon.xconf找
到邏輯單的定義。XSP 名稱空間的聲明就是要說明這是個XSP邏輯單。
實際上,所有的XSP至少要實現(xiàn)XSP邏輯單。在XSP被轉(zhuǎn)換成Java源文件之前,其中的邏輯單
(實際上僅是XSL文件)會先做XSLT處理轉(zhuǎn)換成Java代碼。因此,在上面的例子中的所有的
ESQL標簽都會轉(zhuǎn)換成了我們所熟悉的JDBC代碼。但是并不是所有的標簽都可以變成JDBC代碼,
注意上面示例中的<esql:pool>塊,它涉及到了在WEB-INF/cocoon.xconf文件中定義的數(shù)據(jù)庫
連接池。上面程序中使用的連接池叫做"resources" ,當(dāng)然你可以使用你所喜歡的定義。
注意,我們這里使用<resources> 這個標簽將結(jié)果集包了起來而且每行的數(shù)據(jù)都放到<resource>標
簽里。這樣我們就可以很容易的編寫樣式表來將XML轉(zhuǎn)換成其它瀏覽器可以理解的格式。我們沒有
為表的列定義任何標簽,通過使用<esql:get-columns/>,Cocoon會將每一列的值放到自動以相應(yīng)
的列名定義的標簽里面。
現(xiàn)在,讓我注意一下例子中的SQL查詢語句,正如你所看到的,這條SQL是動態(tài)生成的。當(dāng)用戶
通過GETs 或者 POSTs提交數(shù)據(jù)到這個XSP后,在XSP的頂部,我們將request參數(shù)的值賦給
了keyword變量,然后根據(jù)keyword組成SQL語句。
既然這個例子很簡單,讓我們把它變復(fù)雜一點,加入Email功能,可以在用戶提供Email地址后,
將查詢結(jié)果發(fā)送給用戶。
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
´%<xsp:expr>keyword</xsp:expr>%´ 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>
來自sendmail邏輯單的幾個標簽讓我們擁有了發(fā)送EMAIL的能力。在這個例子中,我們將查詢結(jié)果
的每一行相加賦值給emailBody變量做為郵件的正文。當(dāng)用戶通過request參數(shù)提供一個EMAIL地址,
我們就可以發(fā)送EMAIL了。當(dāng)然這需要您提前設(shè)定好SMTP服務(wù)器和FROM地址。
Cocoon知道根據(jù)sendmail邏輯單來處理在sendmail名稱空間里的標簽,因為這個名稱空間已經(jīng)
在<xsp:page>標簽中已經(jīng)聲明。查看示例中的聲明,你會看到xsp-request這個名稱空間。
xsp-request邏輯單提供了Request常用方法的封裝。雖然在XSP中直接訪問request對象和
使用xsp-request邏輯單沒有什么功能上的區(qū)別,但是,理論上使用logicsheet的標簽比直
接的JAVA代碼更優(yōu)美。
在運行這個例子之前,你必須要先在cocoon.xconf文件中設(shè)置sendmail邏輯單,Cocoon的配置
文件都在Web application 的 WEB-INF目錄下。用你熟悉的編輯器打開cocoon.xconf文件,
找到<target-language name="java">標簽。在這個塊內(nèi),你會發(fā)現(xiàn)所有其它邏輯單的定義。
在最后一個邏輯單(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>
這個定義將http://apache.org.cocoon/sendmail/1.0名稱空間和已經(jīng)包括在Cocoon JAR中
的sendmail.xsl樣式表聯(lián)合起來。
要使用sendmail邏輯單的功能,Cocoon必須要 mail.jar 和 activation.jar這兩個JAR。
如果您使用的Server是Tomcat4.x的話,那么它們位于$CATALINA_HOME/common/lib。
Actions
Action功能很強大,你可以將它放到管道的任何地方。Action可以認為是小的自包含的機器,
它獲取某些輸入數(shù)據(jù),做一些處理,然后返回HashMap對象。不同于Cocoon中的Generators,
Transformers, Serializers組件,Action不對實際的XML內(nèi)容做任何事情,它主要在管道中
實現(xiàn)一些邏輯。
學(xué)習(xí)Action包括要對管道參數(shù)做些了解,有時管道的組件必須交流數(shù)據(jù)。當(dāng)然,XML內(nèi)容會通
過SAX事件傳遞;但是,我們所說的是管道組件本身的功能需要的值。
管道參數(shù)有兩種:input 和 output。Input參數(shù)是由緊跟在組件聲明后面的一個或者多個
<map:parameter>標簽來定義的。它們?yōu)榻M件提供一個或者多個值來影響其操作。
Matcher和Action這兩個組件可以為它們后面的組件提供能存取的Output變量。
這些output參數(shù)放在HashMap對象里,可以通過key名稱(如:{1})來引用。
所有的管道都至少有一個由管道開始處的Matcher提供的HashMap。我們在管道中使用這個
HashMap對象,使用{1}可以取得HashMap中Key為1的值。
Cocoon本身包含一些內(nèi)置的Action。其中有一個是依靠數(shù)據(jù)庫來鑒別用戶的Action。當(dāng)我們
想保護Cocoon中的某些頁面,只允許授權(quán)的用戶訪問時,可以將用戶的ID和密碼存儲到數(shù)據(jù)
庫里,然后使用DatabaseAuthenticationAction來做登錄確認。
這個DatabaseAuthenticationAction要求我們提供一個XML描述文件來說明要使用哪個表和哪
些列。下面是這個描述文件的示例:
<?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>
上面這個文件說明用戶認證Action將使用resources連接池和User表,request提交的userid參數(shù)
和userpwd參數(shù)將與User表中的USER_NAME和USER_PASSWORD列來比較,如果成功確認,將參
數(shù)userid寫到session里。
當(dāng)你在管道中使用一個Action時,它必須先在站點地圖中的<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>
一旦定義過后,就可以使用這個Action來負責(zé)我們要保護的區(qū)域。下面為要保護的區(qū)域定
義了三個管道:
<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>
第一個管道簡單的提供了一個登錄的表單,是個HTML文件,不需要轉(zhuǎn)換。第二個管道處理
從login.html提交的實際的登錄動作。第三個來處理我們要保護的內(nèi)容。
下面我們做詳細的論述:
DatabaseAuthenticationAction依靠描述文件來驗證登錄。但是,我們?nèi)绾沃莉炞C是否
成功呢?對于Action,如果它們返回了有效的HashMap,那么在<map:act>塊里的部分將執(zhí)
行。如果返回null值,那么塊下面的部分將執(zhí)行。也就是說,按照上面管道的定義,我們
有兩種可能的結(jié)果,即:如果認證通過,我們就可以到達受保護的區(qū)域,如果失敗將返回
到Login頁面。
在 protected/* 管道有幾個嵌套的Matcher,第二個的type是sessionstate,這個Matcher
實際上是WildcardSessionAttributeMatcher,在這里用來讀取session里的userid的值。
在這個例子中,我們知道DatabaseAuthenticationAction設(shè)置了一個叫userid的session屬
性,我們通過檢測userid屬性來判斷用戶是否登錄成功,如果它不存在,則轉(zhuǎn)向到login頁面。
在Cocoon已經(jīng)有一個DatabaseAddAction可用來插入數(shù)據(jù),但為了更好的理解Action,我們
將寫一個自己的Action用來將新的Resource記錄插入到Resources表中。
我們假想你已經(jīng)編寫了一個HTML頁面,可用來POST兩個變量name和url到管道。我們的
Action將從Request對象中找回name和url參數(shù),將其插入到表中,最后返回一個HashMap對象。
下面是程序代碼:
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 (´" +
resourceName + "´, ´" +
resourceUrl + "´)";
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)。我們一步步的來說明。
首先,Cocoon action的主方法是 act(),當(dāng)在管道中使用Action時Cocoon將調(diào)用這個方法。
在這個示例中,act()得到Request參數(shù)、從連接池中得到數(shù)據(jù)庫連接,執(zhí)行插入,然后填充
HashMap對象,并將其返回。
在Act方法的開始是從ObjectModelHelper組件中取得Request對象,然后得到兩個參數(shù)。這個
Action需要另外一個參數(shù),pool;它將告訴我們使用哪個連接池。如果這個參數(shù)沒有,那么
Action將返回null而且將錯誤寫到日志里。有了pool的名稱,我們就可以從連接池得到數(shù)據(jù)
庫的連接。Avalon的Excalibur組件用來負責(zé)Cocoon的連接池。如果你不熟悉Avalon,可以
訪問這里http://jakarta.apache.org/avalon/ 。
代碼中的insert statement是直接的JDBC語法。在插入成功后,會將成功的信息寫到日志里。
對于日志,如果按上面Action的寫法,所有的日志信息都寫到你的Web Application的
WEB-INF/logs/sitemap.log文件。
最后,我們將兩個輸入?yún)?shù)寫到了Map對象,雖然它們都在Request對象中,這樣做是多余的,
但我們這樣做是為了示例Map對象的用法。
看一下這個Action在站點地圖中的定義。我們必須首先在站點地圖的<map:components>區(qū)定
義這個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>
在管道中使用這個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>標簽為Action提供"pool"參數(shù)。
一切順利的話,Action將返回一個Map對象,confirmation頁面將被顯示。
在瀏覽器中打開http://localhost:8080/cocoon/addresource,你會看到一個輸入表單。
表單提交后,如果插入成功,將顯示confirmation頁面,如果出現(xiàn)錯誤,將再次返回到表單
頁面。查看WEB-INF/logs/sitemap.log,錯誤信息會告訴你出現(xiàn)了什么錯誤。
如何有效的使用XSP和Action?
XSP和Action是在Cocoon中實現(xiàn)邏輯的兩種不同的辦法。選擇哪一種更適合呢?
XSP在取數(shù)據(jù)或者創(chuàng)建結(jié)構(gòu)化的數(shù)據(jù)方面是很有用的。Action被證明在控制數(shù)據(jù)流程
(并不產(chǎn)生或者影響數(shù)據(jù))的邏輯實現(xiàn)上很有用。我們上面看到的用戶驗證和數(shù)據(jù)庫
操作便是這樣的兩個例子。
然而,有一點需要說明的問題:XSP會將邏輯和內(nèi)容混合。而Cocoon的一個基本的原則
就是邏輯、內(nèi)容、表示的分離。
在使用XSP的時候,我們提出以下幾點建議:
首先,盡可能的使用邏輯單,邏輯單會很好的將Java代碼隱藏。
第二,盡量使用Cocoon的提供的功能,如:在做數(shù)據(jù)庫的Select的時候,我們也可以用
SqlTransformer來實現(xiàn)。
第三,在決策方面的邏輯盡可能的使用Selector, Matcher或Action組件。
最后,當(dāng)無法避免在你的XSP中插入Java邏輯的話,盡可能的讓<xsp:logic>小,而且不要
把它們散布到各種你的標簽中。