STRUTS

1 STRUTS

這是一個 STRUTS 的範例程式,取自於 JSP 教學手冊第 19.20 章的範例 tourism。

目錄結構:

C:\Tomcat 5.5\webapps\begjsp-tourism\
index.jsp
confirmRegistration.jsp
confirmUpdate.jsp
editAttraction.jsp
editAttractionContent.jsp
editEvent.jsp
editEventContent.jsp
home.jsp
homeContent.jsp
listEvents.jsp
listEventsContent.jsp
login.jsp
loginContent.jsp
menu.jsp
registerAttraction.jsp
registerAttractionContent.jsp
template.jsp
viewAttraction.jsp
viewAttractionContent.jsp
WEB-INF\
        web.xml
        struts-config.xml
        struts-bean.tld
        struts-html.tld
        struts-logic.tld
        struts-template.tld
        lib\
            struts.jar
        classes\
                TourismResources.properties
        classes\com\wrox\tourism\action\
                                        BeanNames.java
                                        DeleteEventAction.java
                                        DeregisterAttractionAction.java
                                        EditAttractionAction.java
                                        EditEventAction.java
                                        HomeAction.java
                                        ListEventsAction.java
                                        LogoffAction.java
                                        RegisterAttractionAction.java
                                        UpdateAttractionAction.java
                                        UpdateEventAction.java
                                        ViewAttractionAction.java
        classes\com\wrox\tourism\business\
                                          AttractionBO.java
                                          AttractionException.java
                                          EventBO.java
                                          EventException.java
                                          Role.java
        classes\com\wrox\tourism\entity\
                                        Attraction.java
                                        Event.java
                                        UserRole.java
        classes\com\wrox\tourism\db\
                                    AttractionDAO.java
                                    CreateException.java
                                    DuplicateKeyException.java
                                    EventDAO.java
                                    FinderException.java
                                    NoSuchEntityException.java
                                    ObjectNotFoundException.java
                                    UserRoleDAO.java
        classes\com\wrox\tourism\db\util
                                        ConnectionPool.java
                                        DBInitServlet.java


1.1 編譯與包裝

JAVA 程式碼分布在不同的目錄中,可以使用 APACHE ANT 或 ECLIPSE 編譯。 如果要把所有類別檔包裝成一個 JAR 檔,以方便複製與管理,可以使用 jar.exe 進行壓縮與包裝 。


1.1.1 使用 APACHE ANT

ANT 的用法很簡單,需要一個編譯檔 build.xml 在原始碼的根目錄,接著在該目錄中執行 ant.bat 即可。

這樣簡單工具的安裝步驟也很容易。 到 http://ant.apache.org/bindownload.cgi 下載壓縮包 apache-ant-1.8.2-bin.zip。 將檔案解壓縮後複製到根目錄。到控制台的系統的進階設定=二個環境變數。
1.新增環境變數 ANT_HOME,變數值 C:\apache-ant-1.8.2。
2.新增路徑 C:\apache-ant-1.8.2\bin 到環境變數 Path。
3.將 build.xml 放置在原始檔的根目錄。

另外,原始檔的目錄必須佈局為 src、lib、classes 三個目錄。 src 存放所有 java 原始檔,lib 存放原始檔會參考到的類別庫檔(jar檔),classes 存放編譯後的類別檔(class檔)。

案子的根目錄。

java

案子的類別庫檔目錄。

java

案子的原始碼目錄。這裡顯示出來的是編譯前,actions 目錄中的 JAVA 原始檔。

java
   build.xml
01 <project name="Database Example" default="compile" basedir=".">
01 <description>This is a build file for the project</description>
01   <property environment="env"/>
01   <property name="tomcat.home" value="${env.CATALINA_HOME}"/>
01   <property name="lib.path" value="lib"/>
01   <property name="classes.path" value="classes"/>
01   <property name="source.path" value="src"/>
01   <path id="classpath">
01     <pathelement location="${tomcat.home}/lib/servlet.jar"/>
01   </path>
01   <target name="create-dir">
01     <mkdir dir="${classes.path}"/>
01   </target>
01   <target name="compile" depends="create-dir"
01           description="Compile Java codes">
01     <javac classpathref="classpath" 
01            destdir="${classes.path}"
01            srcdir="${source.path}"
01            includes="**/*.java"/>
01   </target>
01   <target name="clean" description="Remove all generated class files">
01     <delete includeEmptyDirs="true">
01       <fileset dir="${classes.path}" includes="**/*"/>
01     </delete>
01   </target>
01 </project>

ANT 的執行過程與結果。

java

編譯後,產生的類別檔目錄。這裡顯示出來的是編譯後,actions 目錄中的類別檔群。

java

最後,將目錄 classes 的所有內容複製 (即目錄 com),到 C:\Tomcat 5.5\webapps\begjsp-tourism\WEB-INF\classes,即可。


1.1.2 使用 ECLIPSE

因為 JAVA 程式碼分布在不同的目錄中,筆者使用 ECLIPSE FOR JAVA,將 JAVA 程式碼放在同一個案子的 src 目錄。 編譯後,將 bin 的 class 目錄複製到 C:\Tomcat 5.5\webapps\begjsp-tourism\WEB-INF\classes。

struts

編譯前的最後一個動作是,到 ECLIPSE 的功能表 PROJECT 的 PROPERTIES,設定要引入的類別庫。 這些類別庫包括 jsp-api.jar、servlet-api.jar、struts.jar、mm.mysql-2.0.4-bin.jar。 類別庫 jsp-api.jar 和 servlet-api.jar 可以在 C:\Tomcat 5.5\common\lib 找到。
類別庫 struts.jar 可以在 C:\jakarta-struts-1.0.2\lib 找到。
類別庫 mm.mysql-2.0.4-bin.jar 是 JDBC 的驅動程式。

struts

編譯後,將目錄 bin 的所有內容複製(即目錄 com),到 C:\Tomcat 5.5\webapps\begjsp-tourism\WEB-INF\classes,即可。


1.1.3 包裝類別檔

因為編譯完的類別檔分散在不同的目錄,複製與管理上可能會比較不方便,此時可使用 JAR.EXE 將目錄下的所有類別檔封裝。 這個範例是當類別檔群是在 C:\code\example\classes 的各子目錄中時,將此目錄內的類別檔全部壓縮並包裝能一個 JAR 檔。

C:\code\example>jar cvf ./bin/tourism.jar -C classes /

struts

完成後,將此 JAR 檔複製到 C:\Tomcat 5.5\webapps\begjsp-tourism\WEB-INF\lib,並清空 ~WEB-INF\classes 目錄內的檔案,即可。


1.2 執行

執行方式是打開瀏覽器,輸入網址 http://localhost:8080/begjsp-tourism/,即可。
執行結果:
網頁首頁。

struts

2 WEB.XML

宣告此 XML 檔的版本規格,版本 XML 1.0,文字的編碼規格為 ISO-8859-1。

   WEB-INF\WEB.XML
01 <?xml version="1.0" encoding="ISO-8859-1"?>

宣告此 XML 檔的文件根元素 web-app,內容類型為網路應用程式 Web Application 2.2,根據的文件類型定義檔為 web-app_2_2.dtd。

   WEB-INF\WEB.XML
01 <!DOCTYPE web-app
02   PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
03   "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

行號說明
01表示這是一個 web-app 文件。
02PUBLIC 表示後面的字串是 DTD 的公用名稱,包括所屬機構 SUN、使用 WEB APPLICATION DTD、文字為英文(EN)。
03此 DTD 檔所在的 URL,本機找不到此檔案的話,可以到這個位址取得描述 web-app 的 DTD 檔。

在本機中檔案 web-app_2_2.dtd 位於 C:\jakarta-struts-1.0.2\lib,路徑已經包含在 TOMCAT 的 JAVA CLASSPATH,此檔案定義 web-app 的文件類型。 在這個範例中會使用到 web-app 的三個子元素 servlet、servlet-mapping、taglib。

? 表示此子元素只能出現一次或不出現。
* 表示子元素可以出現任意次數。
servlet* 表示可以宣告任意數目的 SERVLET,這些 SERVLET 都會由網路伺服器執行。
servlet-mapping* 表示可以宣告任意數目的路徑映射。
taglib* 表示可以宣告任意數目的標籤物件映射。

   http://java.sun.com/j2ee/dtds/web-app_2_2.dtd
01 <!ELEMENT web-app (icon?, display-name?, description?, distributable?,
02 context-param*, servlet*, servlet-mapping*, session-config?,
03 mime-mapping*, welcome-file-list?, error-page*, taglib*,
04 resource-ref*, security-constraint*, login-config?, security-role*,
05 env-entry*, ejb-ref*)>

宣告一個 ActionServlet,名稱為 action,使用資源檔 ApplicationResources.properties,優先權為二。 在這之後,凡指定 servlet-name 為 action 所宣告的元素,都會歸入到 SERVLET action。

   WEB-INF\WEB.XML
01 <servlet/>
02   <servlet-name>action</servlet-name>
03   <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
04   <init-param>
05     <param-name>application</param-name>
06     <param-value>TourismResources</param-value>
07   </init-param>
08   <load-on-startup>2</load-on-startup>
09 </servlet>

行號說明
02設定將要在 TOMCAT 容器中執行的 SERVLET 的名稱為 action。
03SERVELT 的類別為 struts 的 ActionServlet。之後由 action 取代 ActionServlet 的路徑與全名。
04~07設定應用程式的參數,使用資源檔 TourismResources,即 WEB-INF/classes/TourismResources.properties。
08設定啟動優先權值為 2。優先權值越低,優先權越高。

宣告一個 DBInitServlet,名稱為 dbInit,使用 JDBC 驅動程式 org.gjt.mm.mysql.Driver,連結位址是 jdbc:mysql://localhost:3306/tourism_db。 允許最小連結數 1,最大連結數 10, SERVLET dbInit 的優先權為 1,表示為最高優先的 SERVLET。

   WEB-INF\WEB.XML
01 <servlet></span>
01   <servlet-name>dbInit</servlet-name>
01   <servlet-class>com.wrox.tourism.db.util.DBInitServlet</servlet-class>
01   <init-param>
01     <param-name>driverClass</param-name>
01     <param-value>org.gjt.mm.mysql.Driver</param-value>
01   </init-param>
01   <init-param>
01     <param-name>jdbcURL</param-name>
01     <param-value>jdbc:mysql://localhost:3306/tourism_db</param-value>
01   </init-param>
01   <init-param>
01     <param-name>minCount</param-name>
01     <param-value>1</param-value>
01   </init-param>
01   <init-param>
01     <param-name>maxCount</param-name>
01     <param-value>10</param-value>
01   </init-param>
01   <load-on-startup>1</load-on-startup>
01 </servlet>

所有 *.do 的要求都會由 servlet-class 所指示的 org.apache.struts.action.ActionServlet 服務。 ActionServlet 會根據 struts-config.xml 中的 action-mappings 所映射的路徑,執行 *.do 要求的服務。

   WEB-INF\WEB.XML
01 <servlet-mapping>
02   <servlet-name>action</servlet-name>
03   <url-pattern>*.do</url-pattern>
04 </servlet-mapping>

行號說明
02指定所屬 SERVLET 名稱為 action。
03所有的 *.do 都會對應到路徑為 /* 的動作。

歡迎畫面的檔案列表,這裡指列出一個檔案,所以 index.jsp 必須存在於網頁應用程式的根目錄。

   WEB-INF\WEB.XML
01 <welcome-file-list>
02   <welcome-file>index.jsp</welcome-file>
03 </welcome-file-list>

宣告 STRUTS 的四個標籤函式庫 bean、html、logic、template。

   WEB-INF\WEB.XML
01 <taglib>
02   <taglib-uri>/bean</taglib-uri>
03   <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
04 </taglib>

行號說明
01宣告標籤函式庫。
02設定標籤路徑為 bean,這個名稱會用在 JSP 檔案中,以 <bean:xxx ... > 的形式使用。
03設定標籤描述檔路徑,即標籤函式庫中的每個標籤所對應的標籤類別檔。

   WEB-INF\WEB.XML
01 <taglib>
02   <taglib-uri>/html</taglib-uri>
03   <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
04 </taglib>

行號說明
01宣告標籤函式庫。
02設定標籤路徑為 bean,這個名稱會用在 JSP 檔案中,以 <bean:xxx ... > 的形式使用。
03設定標籤描述檔路徑,即標籤函式庫中的每個標籤所對應的標籤類別檔。

   WEB-INF\WEB.XML
01 <taglib>
02   <taglib-uri>/logic</taglib-uri>
03   <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
04 </taglib>

行號說明
01宣告標籤函式庫。
02設定標籤路徑為 bean,這個名稱會用在 JSP 檔案中,以 <bean:xxx ... > 的形式使用。
03設定標籤描述檔路徑,即標籤函式庫中的每個標籤所對應的標籤類別檔。

   WEB-INF\WEB.XML
01 <taglib>
02   <taglib-uri>/template</taglib-uri>
03   <taglib-location>/WEB-INF/struts-template.tld</taglib-location>
04 </taglib>

宣告以下資源必須經過 attraction 確認通過後,才允許使用。但筆者覺得好像沒有用到。

   WEB-INF\WEB.XML
01 <security-constraint>
01   <web-resource-collection>
01     <web-resource-name>Edit attraction</web-resource-name>
01     <url-pattern>/editAttraction.do</url-pattern>
01   </web-resource-collection>
01   <web-resource-collection>
01     <web-resource-name>Update attraction</web-resource-name>
01     <url-pattern>/updateAttraction.do</url-pattern>
01   </web-resource-collection>
01   <web-resource-collection>
01     <web-resource-name>Deregister attraction</web-resource-name>
01     <url-pattern>/deregisterAttraction.do</url-pattern>
01   </web-resource-collection>
01   <web-resource-collection>
01     <web-resource-name>List events</web-resource-name>
01     <url-pattern>/listEvents.do</url-pattern>
01   </web-resource-collection>
01   <web-resource-collection>
01     <web-resource-name>Add event</web-resource-name>
01     <url-pattern>/editEvent.jsp</url-pattern>
01   </web-resource-collection>
01   <web-resource-collection>
01     <web-resource-name>Edit event</web-resource-name>
01     <url-pattern>/editEvent.do</url-pattern>
01   </web-resource-collection>
01   <web-resource-collection>
01     <web-resource-name>Update event</web-resource-name>
01     <url-pattern>/updateEvent.do</url-pattern>
01   </web-resource-collection>
01   <web-resource-collection>
01     <web-resource-name>Delete event</web-resource-name>
01     <url-pattern>/deleteEvent.do</url-pattern>
01   </web-resource-collection>
01   <auth-constraint>
01     <role-name>attraction</role-name>
01   </auth-constraint>
01 </security-constraint>

宣告認證登入格式為表格登入,發生錯誤時繼續回到原登入頁。

   WEB-INF\WEB.XML
01 <login-config>
01   <auth-method>FORM</auth-method>
01   <form-login-config>
01     <form-login-page>/login.jsp</form-login-page>
01     <form-error-page>/login.jsp</form-error-page>
01   </form-login-config>
01 </login-config>


3 STRUTS-CONFIG.XML

宣告此 XML 檔的版本規格,版本 XML 1.0,文字的編碼規格為 ISO-8859-1。

   WEB-INF\STRUTS-CONFIG.XML
01 <?xml version="1.0" encoding="ISO-8859-1" ?>

宣告此 XML 檔的文件根元素 struts-config, 內容類型為 STRUTS 組態檔 Struts Configuration 1.0, 根據的文件類型定義檔為 struts-config_1_0.dtd。

   WEB-INF\STRUTS-CONFIG.XML
01 <!DOCTYPE struts-config 
01    PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
01           "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">

所有子元素都在會 struts-config 標籤內宣告。

   WEB-INF\STRUTS-CONFIG.XML
01 <struts-config>
01   ....
01 </struts-config>

定義子元素 form-bean,包括 attractionForm 和 eventForm。

   WEB-INF\STRUTS-CONFIG.XML
01 <form-beans>
01   <form-bean name="attractionForm" type="com.wrox.tourism.entity.Attraction"/>
01   <form-bean name="eventForm" type="com.wrox.tourism.entity.Event"/>
01 </form-beans>

定義子元素 action-mappings,有 11 個動作要映射,包括 home.do、viewAttraction.do、 registerAttraction.do、editAttraction.do、updateAttraction.do、listEvents.do、 editEvent.do、updateEvent.do、deleteEvent.do、deregisterAttraction.do、logoff.do。 動作 validateEmployee 的動作類別是 com.example.ValidateEmployeeAction,使用的 BEAN 物件為 empForm,範圍為頁範圍。 輸入頁是 inputContent.jsp,動作成功時的轉送頁是 outputContent.jsp。

   WEB-INF\STRUTS-CONFIG.XML
01 <action-mappings>
01   <action path="/validateEmployee"
01     type="com.example.ValidateEmployeeAction"
01     name="empForm"
01     scope="request"
01     input="/inputContent.jsp">
01       <forward name="success" path="/outputContent.jsp"/>
01   </action>
01   ....
01 </action-mappings>

當 validateEmployee 動作執行時,會從 inputContent.jsp 傳遞四個物件 mapping、form、request、response 給 ValidateEmployeeAction 類別實例。 ValidateEmployeeAction 類別實例會執行其建構子和 perform 方法。 這四個物件會再從類別傳遞給 perform,並執行動作。

mapping動作映射的相關參數,就是上面的 validateEmployee 動作的各參數。
form將 inputContent 頁內 validateEmployee.do 所在的 html:form 內容,包裝成 ActionForm from 實例,再由 empForm 所代表的 EmployeeForm 類別解釋之。
request呼叫此動作的 inputContent.jsp 物件內容包裝成一個 HttpServletRequest 物件,提供 perform 修改其內容。當動作失敗,回到 inputContent.jsp 時,可顯示網頁上。
response將要轉送的 outputContent.jsp 物件內容包裝成一個 HttpServletResponse 物件。當動作成功,轉送到 outputContent.jsp 時,可顯示於網頁上。

01 public ActionForward perform(ActionMapping mapping,ActionForm form,
01         HttpServletRequest request,HttpServletResponse response)
01             throws IOException,ServletException {....}

動作 fileUpload 的動作類別是 com.example.FileUploadAction,使用的 BEAN 物件為 fileUploadForm。 沒有輸入頁和轉送頁,這應該是是因為輸入和轉送都在同一頁,不需要設定。這個動作在這個範例中沒有用到。

   WEB-INF\STRUTS-CONFIG.XML
01 <action-mappings>
01   ....
01   <action path="/fileUpload"
01     type="com.example.FileUploadAction"
01     name="fileUploadForm">
01   </action>
01 </action-mappings>


4 JSP 檔群

STRUTS 中,JSP 檔的地位主要是畫面的構成。 透過 JSP 語法和標籤的使用,得到想要的網頁畫面效果。 這個範例使用到 19 個 JSP 檔案。

所有的 JSP 檔都放在 C:\Tomcat 5.5\webapps\begjsp-tourism\ 目錄,這是網頁應用程式的根目錄。

網頁畫面:
分成五個部分,分別標記 1、2、3、4、5。
這是樣板檔 template.jsp 的配置。
畫面區域 1 是一個標頭字串。
畫面區域 2 是目錄檔 menu.jsp。
畫面區域 3 會隨不同的 JSP 檔而變動,會有七種不同的變動方式,由 ?Content.jsp 的檔名所表示。
畫面區域 4 是一個空白畫面,寬度是畫面的 10%。
畫面區域 5 會顯示目前登入者的名稱,這個畫面沒有登入者。

struts

筆者將所有 JSP 檔依功能分類為特殊網頁檔、一般網頁檔、網頁內容檔。

特殊網頁檔,包括歡迎網頁檔、網頁樣板檔、網頁目錄檔。

檔案說明
index.jsp網站的歡迎畫面。
template.jsp網頁樣板檔,用於佈置各個網頁畫面。
menu.jsp目錄畫面,位於樣板檔的左側,畫面 2。

一般網頁檔使用網頁樣板檔,佈置整個網頁的內容。 有些網頁的內容已經由樣板檔固定住,只留下畫面區域 3 給網頁檔使用。 各個網頁檔會取用自己的網頁內容檔,並將其設定到畫面區域 3,以構成完整的網頁畫面。

因為 confirmRegistration.jsp 和 confirmUpdate.jsp 只是在畫面區域 3 顯示簡單訊息,沒有專屬的網頁內容檔。

home.jsp首頁畫面。
editAttraction.jsp特色編輯網頁檔。
editEvent.jsp事件編輯網頁檔。
listEvents.jsp事件列表網頁檔。
login.jsp登入網頁檔。
registerAttraction.jsp特色註冊網頁檔。
viewAttraction.jsp特色檢視網頁檔。
confirmRegistration.jsp確認註冊成功網頁檔。
confirmUpdate.jsp確認更新成功網頁檔。

網頁內容檔雖然也是 JSP 檔,它只是完整網頁的一部分,不能獨自顯示於網頁上。 網頁內容檔會依據網頁樣板檔 template.jsp 的規劃,顯示漁網頁的畫面區域 3。

homeContent.jsp首頁網頁內容檔,畫面區域 3。
editAttractionContent.jsp特色編輯網頁內容檔,畫面區域 3。
editEventContent.jsp事件編輯網頁內容檔,畫面區域 3。
listEventsContent.jsp事件列表網頁內容檔,畫面區域 3。
loginContent.jsp登入網頁內容檔,畫面區域 3。
registerAttractionContent.jsp特色註冊網頁內容檔,畫面區域 3。
viewAttractionContent.jsp特色檢視網頁內容檔,畫面區域 3。


4.1 INDEX.JSP

INDEX.JSP 是任何客端連結到網站時會觸發的第一個 JSP 網頁檔。 WEB.XML 設定了 INDEX.JSP 為網站的歡迎畫面,STRUTS 的 ACTION SERVLET 面對客端的要求,執行 INDEX.JSP,以回應客端。 INDEX.JSP 的程式碼很簡單的將網頁重新導向到動作 HOME.DO,最後轉送到 HOME.JSP。

   C:\Tomcat 5.5\webapps\begjsp-tourism\index.jsp
01 <%@ taglib uri="/logic" prefix="logic" %>
01 <logic:redirect href="home.do"/>

HOME.JSP 的顯示畫面。

struts

4.1.1 HOME.DO

HOME.DO 是一個由 STRUTS-CONFIG.XML 定義的動作映射,STRUTS SERVLET 接著會執行其所屬的動作類別 com.wrox.tourism.actions.HomeAction。

   WEB-INF\STRUTS-CONFIG.XML
01 <action path="/home"01   type="com.wrox.tourism.actions.HomeAction"
01   unknown="true">
01   <forward name="success" path="/home.jsp"/>
01 </action>

HomeAction 類別的 perform 方法執行成功時,會將取得的資料以 attractionList 物件名稱儲存在要求頁物件,一起轉送給 home.jsp。

   WEB-INF\classes\com\wrox\tourism\actions\HomeAction.java
01 try{
01     con = pool.getConnection();
01     AttractionDAO attractionDAO = new AttractionDAO(con);
01     Collection col = attractionDAO.findAll();
01     request.setAttribute(BeanNames.ATTRACTION_LIST,col);
01     return mapping.findForward("success");
01 }

home.jsp 使用 homeContent.jsp 將資料內容顯示到網頁中,即完成 INDEX.JSP 歡迎網頁檔的資料畫面。 homeContent.jsp 的程式碼可視為 home.jsp 的一部分,所以透過 request 物件傳送給 home.jsp 的資料可以由 homeContent.jsp 直接取用。 這裡使用 STRUTS 邏輯迴圈標籤,將 attractionList 集合物件的內容一個個顯示出來。 迴圈的名稱是 attraction,這個名稱會在每次的迴圈中,代表 attractionList 中的一個物件,物件類別為 com.wrox.tourism.entity.Attraction,是一個 ACTION FORM 類別的實例。 request 表示 attractionList 物件是隨著 request 物件而來的,要到 request 物件中取得此物件。

結果會產生具有超連結的 attraction 列表,超連結使用 userId 連結參數,用來顯示該 attraciton 的詳細內容,這是另一個動作映射。

   C:\Tomcat 5.5\webapps\begjsp-tourism\HomeContent.jsp
01 <logic:iterate name="attractionList"
01   id="attraction"
01   scope="request"
01   type="com.wrox.tourism.entity.Attraction">
01   <tr>
01   <td>
01   <a href="viewAttraction.do?userId=<bean:write name="attraction"
01     property="userId"/>">
01     <bean:write name="attraction" property="name"/>
01   </a>
01   </td>
01   <td>
01     <bean:write name="attraction" property="description"/>
01   </td>
01   </tr>
01 </logic:iterate>


4.1.2 VIEWATTRACTION.DO

當使用者從客端網頁上點了某一個 ATTRATION,會觸發 VIEWATTRACTION.DO 的動作。 VIEWATTRACTION.DO 會以其輸入參數 userId,從資料庫取得匹配的 ATTRACTION 的各項餐數和事件列表。 再將網頁轉送到 VIEWATTRATION.JSP,以顯示該 ATTRACTION 的詳細資訊。

struts

VIEWATTRACTION.DO 是一個由 STRUTS-CONFIG.XML 定義的動作映射,STRUTS SERVLET 接著會執行其所屬的動作類別 com.wrox.tourism.actions.ViewAttractionAction。

01 <action path="/viewAttraction"</span>
01   type="com.wrox.tourism.actions.ViewAttractionAction"
01   input="/home.do">
01   <forward name="success" path="/viewAttraction.jsp"/>
01 </action>

ViewAttraction 類別的 perform 方法執行成功時,會將取得的資料以 attractionForm 和 eventList 物件名稱儲存在要求頁物件,一起轉送給 viewAttraction.jsp。

01 try{
01     con = pool.getConnection();
01     String userId = request.getParameter("userId");
01     AttractionDAO attractionDAO = new AttractionDAO(con);
01     Attraction attraction =attractionDAO.findByPrimaryKey(userId);
01     request.setAttribute(BeanNames.ATTRACTION_FORM,attraction);
01     EventDAO eventDAO = new EventDAO(con);
01     Collection col = eventDAO.findAll(userId);
01     request.setAttribute(BeanNames.EVENT_LIST,col);
01     return mapping.findForward("success");
01 }

viewAttraction.jsp 使用 viewAttractionContent.jsp 將資料內容顯示到網頁中,即完成 VIEWATTRACTION.DO 的超連結動作。 viewAttractionContent.jsp 的程式碼可視為 viewAttraction.jsp 的一部分,所以透過 request 物件傳送給 viewAttraction.jsp 的資料可以由 viewAttractionContent.jsp 直接取用。 這裡使用 STRUTS BEAN 寫入標籤將 attrationForm 的內容顯示出來,並使用 STRUTS 邏輯迴圈標籤,將 eventList 集合物件的內容一個個顯示出來。 迴圈的名稱是 event,這個名稱會在每次的迴圈中,代表 eventList 中的一個物件,物件類別為 com.wrox.tourism.entity.Event,是一個 ACTION FORM 類別的實例。 request 表示 eventList 物件是隨著 request 物件而來的,要到 request 物件中取得此物件。

顯示 attractionFrom 的內容。

01 <table cellpadding="2">
01    ....
01    <bean:write name="attractionForm" property="name"/>
01    ....
01    <bean:write name="attractionForm" property="description"/>
01    ....
01    <bean:write name="attractionForm" property="webSite"/>
01    ....
01    <bean:write name="attractionForm" property="address"/>
01 </table>

顯示 evenList 的內容。

01 <table cellpadding="2" width="80%">
01   ....
01  <logic:iterate name="eventList"
01    id="event"
01    scope="request"
01    type="com.wrox.tourism.entity.Event">
01   <tr>
01    <td>
01     <bean:write name="event" property="name"/>
01    </td>
01    <td>
01     <bean:write name="event" property="description"/>
01    </td>
01    <td>
01     <bean:write name="event" property="ticketPrice"/>
01    </td>
01   </tr>
01  </logic:iterate>
01 </table>


4.2 MENU.JSP

MENU.JSP 是一個比較特別的網頁檔,因為每一個網頁都會出現。 MENU.JSP 可以回到首頁,或到註冊頁,或透過動作映射執行 ATTRACTION、EVENT 的編輯工作。

struts

4.2.1 回到首頁

當使用者點了 Home,網頁就回到首頁。

01 <html:link href="home.do">Home</html:link>


4.2.2 執行註冊

當使用者點了 Register,進入 ATTRACTION 註冊頁,註冊新的 ATTRACTION。

01 <html:link href="registerAttraction.jsp">Register</html:link>

registerAttraction.jsp 會使用已經取得的 ATTRACTION 資料,繪製成表格給使用者填寫或修改。 當使用者按下 Register Attraction 後,就會觸發 registerAttraction.do 動作。

01 <html:form action="/registerAttraction.do" focus="userId">
01   ....
01   <html:text property="userId" size="15"/>
01   ....
01   <html:password property="password" size="15"/>
01   ....
01   <html:text property="name" size="30"/>
01   ....
01   <html:text property="webSite" size="30"/>
01   ....
01   <html:textarea property="description" rows="3" cols="20"/>
01   ....
01   <html:textarea property="address" rows="3" cols="20"/>
01   ....
01   <html:submit value="Register Attraction"/>
01   ....
01 </html:form>

動作映射 registerAttraction.do 定義在 STRUTS-CONFIG.XML,會執行動作類別 com.wrox.tourism.actions.RegisterAttractionAction。 動作成功時,轉送到 confirmRegistration.jsp,顯示註冊成功訊息。

01 <action path="/registerAttraction"
01   type="com.wrox.tourism.actions.RegisterAttractionAction"
01   input="/registerAttraction.jsp"
01   name="attractionForm"
01   scope="request">
01   <forward name="success" path="/confirmRegistration.jsp"/>
01 </action>

confirmRegistration.jsp 使用網頁樣板檔製作網頁,並在網頁區域 3 顯示成功訊息,意思是 ATTRACTION 已經成功註冊了。

01 <%@ taglib uri="/template" prefix="template" %>
01 <template:insert template="template.jsp">
01   <template:put name="content"
01     content="Your attraction's details have been registered"
01     direct="true"/>
01 </template:insert>


4.2.3 登入編輯

當使用者點了 Logon,會執行動作 editAttraction.do,進入 ATTRACTION 註冊頁,註冊新的 ATTRACTION。 這裡使用了 STRUTS 邏輯標籤 notPresent,表示 attraction 角色不存在的話,才顯示這個超連結。 並且要使用這個連結之前,必須得到 attraction 角色的確認,才能執行 editAttraction.do。

01 <logic:notPresent role="attraction">
01   <html:link href="editAttraction.do">Logon</html:link>
01   ....
01 </logic:notPresent>


4.2.3.1 登入

為了實現角色確認,必須指定認證格式和確證方式。 認證格式部分,在 WEB-INF\WEB.XML 中指定 FORM 格式,認證頁為 login.jsp。

struts

01 <login-config>
01   <auth-method>FORM</auth-method>
01   <form-login-config>
01     <form-login-page>/login.jsp</form-login-page>
01     <form-error-page>/login.jsp</form-error-page>
01   </form-login-config>
01 </login-config>

認證格式部分,在 C:\Tomcat 5.5\conf\server.xml 中指定透過資料庫的認證方式。 Realm 是 TOMCAT 用來設定認證工作的描述,在 SERVER.XML 中已經有範本,只要去除註解,改成下面的參數,即可。

01 <Realm className="org.apache.catalina.realm.JDBCRealm"
01        driverName="org.gjt.mm.mysql.Driver"
01        connectionURL="jdbc:mysql://localhost:3306/tourism_db?"
01        userTable="attraction" 
01        userNameCol="user_id" 
01        userCredCol="password"
01        userRoleTable="user_role" 
01        roleNameCol="role_name"/>


4.2.3.2 編輯

通過認證後,就會執行 editAttraction.do。 動作映射 editAttraction.do 定義在 STRUTS-CONFIG.XML,執行動作類別 com.wrox.tourism.actions.EditAttractionAction。

struts

01 <action path="/editAttraction"
01   type="com.wrox.tourism.actions.EditAttractionAction"
01   input="/home.do">
01   <forward name="success" path="/editAttraction.jsp"/>
01 </action>

動作成功時,網頁進入 editAttraction.jsp,ATTRACTION 編輯頁,註冊新的 ATTRACTION。動作失敗時,回到首頁。

01 try {
01      con = pool.getConnection();
01      String userId = request.getRemoteUser();
01      AttractionDAO attractionDAO = new AttractionDAO(con);
01      Attraction attraction = attractionDAO.findByPrimaryKey(userId);
01      request.setAttribute(BeanNames.ATTRACTION_FORM,attraction);
01      return mapping.findForward("success");
01 }

editAttraction.jsp 編輯 ATTRACTION 的內容,包括 password、name、webSite、address、description 等欄位。 完成後,按下 Update Attraction 按鈕,觸發動作 updateAttraction.do,進行更新。

01 <table cellpadding="2">
01   <html:form action="/updateAttraction.do" focus="password">
01     ....
01     <bean:write name="attractionForm" property="userId"/>
01     <html:hidden property="userId"/>
01     ....
01     <html:password property="password" size="15"/>
01     ....
01     <html:text property="name" size="30"/>
01     ....
01     <html:textarea property="webSite"/>
01     ....
01     <html:textarea property="address" rows="3" cols="20"/>
01     ....
01     <html:textarea property="description" rows="3" cols="20"/>
01     ....
01     <html:submit value="Update Attraction"/>
01   </html:form>
01 </table>


4.2.4 登入狀態的目錄

當已經是登入狀態時,才會顯示這部分的目錄。
共有五個目錄項目。

Attraction Details編輯 ATTRACTION。
List Events列出這個 ATTRACTION 的 所有事件。
Add Event增加一個事件。
Deregister註銷 ATTRACTION。
Logoff登出。
struts

01 <logic:present role="attraction">
01   <html:link href="editAttraction.do">Attraction Details</html:link>
01    
01   <br/>
01   <html:link href="listEvents.do">List Events</html:link>
01    
01   <br/>
01   <html:link href="editEvent.jsp">Add Event</html:link>
01    
01   <br/>
01   <html:link href="deregisterAttraction.do">Deregister</html:link>
01    
01   <br/>
01   <html:link href="logoff.do">Logoff</html:link>
01    
01   <br/>
01 </logic:present>


4.2.4.1 編輯 ATTRACTION

當使用者點了 Attraction Details,執行 editAttraction.do,以 userId 從資料庫讀取 ATTRACTION 資料。 動作成功時,網頁進入 editAttraction.jsp,ATTRACTION 編輯頁,註冊新的 ATTRACTION。

struts

動作映射 editAttraction.do 定義在 STRUTS-CONFIG.XML,執行動作類別 com.wrox.tourism.actions.EditAttractionAction。

01 <action path="/editAttraction"
01   type="com.wrox.tourism.actions.EditAttractionAction"
01   input="/home.do">
01   <forward name="success" path="/editAttraction.jsp"/>
01 </action>

EditAttractionAction 會使用 userId ,讀取資料庫相符合的 ATTRACTION,存入要求物件,並轉傳到 editAttraction.jsp。

01 try{
01     con = pool.getConnection();
01     String userId = request.getRemoteUser();
01     AttractionDAO attractionDAO = new AttractionDAO(con);
01     Attraction attraction = attractionDAO.findByPrimaryKey(userId);
01     request.setAttribute(BeanNames.ATTRACTION_FORM,attraction);
01     return mapping.findForward("success");
01 }

editAttraction.jsp 編輯 ATTRACTION 的內容,包括 password、name、webSite、address、description 等欄位。 完成後,按下 Update Attraction 按鈕,觸發動作 updateAttraction.do,進行更新。

01 <table cellpadding="2">
01   <html:form action="/updateAttraction.do" focus="password">
01     ....
01     <bean:write name="attractionForm" property="userId"/>
01     <html:hidden property="userId"/>
01     ....
01     <html:password property="password" size="15"/>
01     ....
01     <html:text property="name" size="30"/>
01     ....
01     <html:textarea property="webSite"/>
01     ....
01     <html:textarea property="address" rows="3" cols="20"/>
01     ....
01     <html:textarea property="description" rows="3" cols="20"/>
01     ....
01     <html:submit value="Update Attraction"/>
01   </html:form>
01 </table>


4.2.4.2 事件列表

當使用者點了 List Events,執行 listEvents.do。
動作執行成功時,畫面如下。

struts

動作映射 listEvents.do 定義在 STRUTS-CONFIG.XML,會執行動作類別 com.wrox.tourism.actions.ListEventsAction。

01 <action path="/listEvents"
01   type="com.wrox.tourism.actions.ListEventsAction"
01   input="/editAttraction.do">
01   <forward name="success" path="/listEvents.jsp"/>
01 </action>

動作類別 ListEventsAction 會讀取 ATTRACTION 的所有事件,網頁轉往 listEvents.jsp。

01 try{
01     con = pool.getConnection();
01     String userId = request.getRemoteUser();
01     EventDAO eventDAO = new EventDAO(con);
01     Collection col = eventDAO.findAll(userId);
01     request.setAttribute(BeanNames.EVENT_LIST,col);
01     return mapping.findForward("success");
01 }

listEvents.jsp 將所有事件條列,每個事件都有二個超連結,可以編輯事件,也可以刪除事件。

01 <logic:iterate name="eventList"
01   id="event"
01   scope="request"
01   type="com.wrox.tourism.entity.Event">
01     ....
01     <a href="editEvent.do?eventId=<bean:write name="event" property="eventId"/>">
01     <bean:write name="event" property="name"/>
01     ....
01     <bean:write name="event" property="description"/>
01     ....
01     <bean:write name="event" property="ticketPrice"/>
01     ....
01     <a href="deleteEvent.do?eventId=<bean:write name="event"
01     property="eventId"/>">
01         delete
01     </a>
01     ....
01 </logic:iterate>

當使用者點了事件名稱,就會觸發動作 editEvent.do。 動作映射 editEvent.do 定義在 STRUTS-CONFIG.XML,會執行動作類別 com.wrox.tourism.actions.EditEventAction。 這一部分的動作和加入事件一樣,在下一節解說。

當使用者點了事件後面的 delete,就會觸發動作 deleteEvent.do。 動作映射 deleteEvent.do 定義在 STRUTS-CONFIG.XML,會執行動作類別 com.wrox.tourism.actions.DeleteEventAction。

01 <action path="/deleteEvent"
01   type="com.wrox.tourism.actions.DeleteEventAction"
01   input="/listEvents.do">
01   <forward name="success" path="/listEvents.do"/>
01 </action>

動作類別 DeleteEventAction 使用 evenId,呼叫業務類別的 removeEvent 方法,移除事件。

01 try{
01     int eventId = Integer.parseInt(request.getParameter("eventId"));
01     EventBO eventBO = new EventBO();
01     eventBO.removeEvent(eventId);
01     return mapping.findForward("success");
01 }

業務類別 eventBO 使用 evenId,呼叫事件資料存取類別的 remove 方法,移除事件。

01 try {
01     con = pool.getConnection();
01     EventDAO eventDAO = new EventDAO(con);
01     eventDAO.remove(eventId);
01     con.commit();
01 }

事件資料存取類別的 remove 方法執行資料庫的項目移除描述,將項目從事件目錄移除。

01 String sql = "DELETE FROM event WHERE event_id = ?";
01 try {
01   ....
01   ps = con.prepareStatement(sql);
01   ps.setInt(1,eventId);
01   if (ps.executeUpdate() != 1) {
01     throw new NoSuchEntityException("error.removed.event");
01   }
01 }


4.2.4.3 加入事件

當使用者點了 Add Event,執行 editEvent.do。

struts

動作映射 editEvent.do 定義在 STRUTS-CONFIG.XML,會執行動作類別 com.wrox.tourism.actions.EditEventAction。

01 <action path="/editEvent"
01   type="com.wrox.tourism.actions.EditEventAction"
01   input="/listEvents.do">
01   <forward name="success" path="/editEvent.jsp"/>
01 </action>

動作類別 EditEventAction 使用 eventId 讀取事件的資訊,存入 request 物件,轉送到 editEvent.jsp。

01 try{
01     con = pool.getConnection();
01     int eventId = Integer.parseInt(request.getParameter("eventId"));
01     EventDAO eventDAO = new EventDAO(con);
01     Event event = eventDAO.findByPrimaryKey(eventId);
01     request.setAttribute(BeanNames.EVENT_FORM,event);
01     return mapping.findForward("success");
01 }

editEvent.jsp 將 request 物件傳過來的事件物件會顯示幾個文字區域,用來修改事件內容。 當使用者按下 Update,觸發 updateEvent.do,進行事件更新的動作。

01 <html:form action="/updateEvent.do" focus="name">
01   <html:hidden property="eventId"/>
01   ....
01   <html:text property="name" size="30"/>
01   ....
01   <html:textarea property="description" rows="3" cols="20"/>
01   ....
01   <html:text property="ticketPrice" size="10"/>
01   ....
01   <html:submit value="Update"/>
01   ....
01 </html:form>


4.2.4.4 註銷 ATTRACTION

當使用者點了 Deregister,執行 deregisterAttraction.do。

01 <action path="/deregisterAttraction"
01   type="com.wrox.tourism.actions.DeregisterAttractionAction"
01   input="/editAttraction.do">
01   <forward name="success" path="/home.do" redirect="true"/>
01 </action>

動作類別 DeregisterAttractionAction 會將 ATTRACTION 刪除,並將登入狀態取消,網頁轉往首頁。

01 try{
01     String userId = request.getRemoteUser();
01     AttractionBO attractionBO = new AttractionBO();
01     attractionBO.deregisterAttraction(userId);
01     request.getSession().invalidate();
01     return mapping.findForward("success");
01 }


4.2.4.5 登出

當使用者點了 Logoff,執行 logoff.do。 動作映射 logoff.do 定義在 STRUTS-CONFIG.XML,會執行動作類別 com.wrox.tourism.actions.LogoffAction。 動作執行成功時,轉送 home.do,這會回到首頁。失敗的話,繼續留在編輯 ATTRACTION 的畫面。

01 <action path="/logoff"
01   type="com.wrox.tourism.actions.LogoffAction"
01   input="/editAttraction.do">
01   <forward name="success" path="/home.do" redirect="true"/>
01 </action>

動作類別 LogoffAction 會將登入狀態取消,網頁轉往首頁。

01 try{
01     request.getSession().invalidate();
01     return mapping.findForward("success");
01 }


5 類別檔群

STRUTS 中,JAVA 語言主要是用在資料與邏輯運算的過程。 取得資料並且對資料做運算,再將資料轉送給 JSP 檔做成網頁是 JAVA 程式碼的功能。 這支程式共有 30 個類別檔分散在四個目錄中。

所有的 JAVA 類別檔都放在 C:\Tomcat 5.5\webapps\begjsp-tourism\WEB-INF\classes。 類別檔是從 JAVA 原始碼檔編譯而來,這裡列出所有的原始碼檔名。

依據不同的目錄與功能,將 JAVA 程式碼檔分類為動作檔、業務檔、資料存取檔、資料物件檔、資料庫管理檔。

動作檔:
位於目錄 WEB-INF\classes\com\wrox\tourism\action,共有 12 個類別檔。 由 JSP 檔透過動作映射的方式,觸發動作檔的執行。映射的參數是設定在 STRUTS-CONFIG.XML,由 STRUTS 的 ACTION SERVLET 解析並執行之。

檔案功能說明
BeanNames.java以介面的方式儲存幾個名稱字串。
DeleteEventAction.java事件刪除的動作類別。
DeregisterAttractionAction.java註銷 ATTRACTION。
EditAttractionAction.java編輯 ATTRACTION。
EditEventAction.java編輯事件。
HomeAction.java回到首頁。
ListEventsAction.java事件列表。
LogoffAction.java登出。
RegisterAttractionAction.java註冊 ATTRACTION。
UpdateAttractionAction.java更新 ATTRACTION。
UpdateEventAction.java更新事件。
ViewAttractionAction.java檢視 ATTRACTION。

業務檔:
位於目錄 WEB-INF\classes\com\wrox\tourism\business 有 5 個類別檔,包括各資料物件的業務處理和例外處理,是資料存取檔的上層。

檔案說明
AttractionBO.javaATTRACTION 的業務類別。
EventBO.javaEVENT 的業務類別。
Role.java角色類別。
AttractionException.javaATTRACTION 的例外。
EventException.javaEVENT 的例外。。

資料物件檔:
在這個網站中有三個資料物件 ATTRACTION、EVENT、ROLE。 位於目錄 WEB-INF\classes\com\wrox\tourism\entity 有 3 個類別檔。

檔案說明
Attraction.java
Event.java
UserRole.java

資料存取檔:
用於存取資料庫的資料物件,資料物件的格式由資料物件檔描述。 位於目錄 WEB-INF\classes\com\wrox\tourism\db 有 8 個類別檔,包括各物件存取檔以及例外處理檔。

檔案說明
AttractionDAO.java
EventDAO.java
UserRoleDAO.java
CreateException.java
DuplicateKeyException.java
FinderException.java
NoSuchEntityException.java
ObjectNotFoundException.java

資料庫管理檔:
屬於資料物件與資料庫的底層連結功能。 位於目錄 WEB-INF\classes\com\wrox\tourism\db\util 有 2 個類別檔。

檔案說明
ConnectionPool.java資料庫連結池檔。
DBInitServlet.java資料庫初始化伺服器檔。

6、標籤描述檔群

所有標籤檔都要放在 WEB-INF 目錄,並以 classes 為標籤類別檔的根目錄。 例如標籤類別 com.example.DepartmentTag 的路徑就是 WEB-INF\classes\com\example\DepartmentTag.class。

檔案說明
struts-bean.tldSTRUTS BEAN 標籤檔,標籤函式庫存放在 struts.jar。
struts-html.tldSTRUTS 類 HTML 標籤檔,標籤函式庫存放在 struts.jar。
struts-logic.tldSTRUTS 邏輯標籤檔,標籤函式庫存放在 struts.jar。
struts-template.tldSTRUTS 樣板標籤檔,標籤函式庫存放在 struts.jar。

7、資源檔

所有資源檔都要放在 WEB-INF\classes 目錄。 這支程式有一個資源檔 TourismResources.properties。 這裡設定了一些錯誤訊息的字串內容,透過 error.xxx.xxx 的方式引入 JAVA 程式碼。

   WEB-INF\classes\TourismResources.properties
01 error.removed.attraction=<font color="red">Selected attraction has been removed</font>
01 error.create.attraction=<font color="red">Unable to create the attraction</font>
01 error.duplicate.attraction=<font color="red">Specified attraction already exists</font>
01 error.removed.event=<font color="red">Selected event has been removed</font>
01 error.create.event=<font color="red">Unable to create the event</font>
01 error.duplicate.event=<font color="red">Specified event already exists</font>
01 error.removed.userRole=<font color="red">Selected user role has been removed</font>
01 error.create.userRole=<font color="red">Unable to create the user role</font>
01 error.duplicate.userRole=<font color="red">Specified user role already exists</font>
01 error.missing.userId=<font color="red">Please enter the user id</font>
01 error.missing.password=<font color="red">Please enter the password</font>
01 error.missing.name=<font color="red">Please enter the name</font>
01 error.missing.description=<font color="red">Please enter the description</font>
01 error.missing.webSite=<font color="red">Please enter the web site</font>
01 error.missing.address=<font color="red">Please enter the address</font>
01 error.missing.ticketPrice=<font color="red">Please enter the ticket price</font>
01 error.unexpected=<font color="red">Unxpected error, Please contact webmaster</font>