aBạn cần cài đặt m2Eclipse và SakaiAppBuilder cho tutorial này. Nếu có thể bạn nên cái thêm bộ JBossTool, việc đó cung cấp một số dao diện thân thiện khi bạn bCác bước tạo một tool HelloWorld trong SAKAI
Nhập tên Project này tùy ý, ở đây tôi ngẫu nhiên nhập tên MT06BK01. Chọn Project Type là JSF, Implementations là HelloWorld app(yên tâm tool bạn viết ra có thể có đầy đủ chức năng tùy bạn implement, ở đây lựa chọn này sẽ đơn giản hóa cấu trúc project). Chọn K1 là Sakai version.
Chú ý: Project tạo ra phải là con của folder Sakai-src hiện hành vì khi build project bằng maven, theo định nghĩa trong tập tin pom project MT06BK01 sẽ cần
• Ta cần sửa lại nội dung tập tin H:\sakai-src-2.7.1\MT06BK01\pom.xml ở phần Version của parent từ 2.6.0 sửa thành 2.7.1 phù hợp với tập tin
/master/pom.xml (vì Sakai chúng tôi sử dụng là 2.7.1).
<parent> <artifactId>master</artifactId> <groupId>org.sakaiproject</groupId> <!--<version>SNAPSHOT</version>--> <!--<version>M2</version>--> <version>2.7.1</version> <relativePath>../master/pom.xml</relativePath> </parent>
• Để ý khi bạn chạy mvn install, maven sẽ dựa trên tập tin pom.xml trong folder bạn đang làm việc để thự thi quá trình build. Tuy nhiên trong mỗi tập tin pom.xml sẽ có thể định nghĩa parent là một tập tin pom khác, lúc này nội dung 2 tập tin pom (hoặc nhiều hơn nữa nếu parent pom lại có parent …) sẽ được kết hợp. Cách định nghĩa parent pom là chỉ ra các thông số artifactId, groupId và có thể là relativePath … trong 1 entry <parent>…</parent> mà cụ thể bạn thấy ở trên. Nếu một trong các thông số sai lệch sẽ dẫn đến quá trình build không thực hiện được vì không xác định được (hoặc không tồn tại) parent cần tìm kiếm.
Bây giờ bạn vào folder H:\sakai-src-2.7.1\MT06BK01\tool. Trong này lại có một tập tin pom.xml, trong tập tin pom.xml có khai báo parent là
H:\sakai-src-2.7.1\MT06BK01\pom.xml mà bạn vừa chỉnh sửa <parent> <groupId>org.sakaiproject</groupId> <artifactId>mt06bk01</artifactId> <version>0.1</version><!--Mt06bk01.version--> </parent>
Tìm đến mục dependencies của tập tin pom này và thay nội dung của nó bằng:
<version>${sakai.jsf.version}</version> </dependency> <dependency> <groupId>org.sakaiproject.jsf</groupId> <artifactId>jsf-app</artifactId> <version>${sakai.jsf.version}</version> </dependency> <dependency> <groupId>org.sakaiproject.jsf</groupId> <artifactId>myfaces-widgets-depend</artifactId> <version>${sakai.jsf.version}</version> <type>pom</type> </dependency>
<version>${sakai.jsf.version}</version> </dependency> <dependency> <groupId>org.sakaiproject.kernel</groupId> <artifactId>sakai-kernel-api</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.sakaiproject.kernel</groupId> <artifactId>sakai-kernel-util</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>commons-validator</groupId> <artifactId>commons-validator</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>org.apache.myfaces.core</groupId> <artifactId>myfaces-api</artifactId> <version>1.1.5</version> </dependency>
version>1.1.5</version> </dependency> <dependency> <groupId>org.apache.myfaces.tomahawk</groupId> <artifactId>tomahawk</artifactId> <version>1.1.6</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.0.4</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.1.1</version> </dependency>
<version>1.0</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.1</version> </dependency> <dependency> <groupId>commons-validator</groupId> <artifactId>commons-validator</artifactId> <version>1.1.4</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>oro</groupId> <artifactId>oro</artifactId> <version>2.0.8</version> </dependency> <dependency> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>org.sakaiproject.samigo</groupId> <artifactId>samigo-api</artifactId> <version>2.7.1</version> <type>jar</type> <scope>provided</scope> </dependency> <dependency> <groupId>org.sakaiproject.samigo</groupId> <artifactId>samigo-services</artifactId> <version>2.7.1</version> <type>jar</type> <scope>provided</scope> </dependency>
<version>1.0.4</version> <type>jar</type> <scope>provided</scope> </dependency> <dependency> <groupId>org.sakaiproject.kernel</groupId> <artifactId>sakai-component-manager</artifactId> <version>1.1.10</version> <type>jar</type> <scope>provided</scope> </dependency>
• Danh sách dependency này lẽ ra có sẵng khi project được tạo tuy nhiên các artifactId , groupId cũng như version của các dependency đã thay đỗi theo phiên bản của Sakai. Sự thay đổi được thống kê thông báo rải rác trên cộng đồng Sakai, thí dụ:https://jira.sakaiproject.org/browse/SAK-17362
• Danh sách dependency trên bạn có thể copy paste vào entry <dependencies> ... </dependencies>
Hoặc bạn có thể ngồi dò artifactId, groupId và version. Chú ý rằng đôi khi các thông số này thay đồi rất nhỏ.
• Đa số các dependency khai báo ở trên được chép vào WEB-INF/lib của project của bạn sau khi chạy và đóng gói thành .WAR nhưng không phải tất cả, các thành phần được khai báo với <scope> provided sẽ không đóng gói trong tập tin sản phẩm của bạn mà được cung cấp bởi môi trường, do classloader của tomcat hoặc Sakai. Thí dụ nếu bạn sử dụng mysql thì driver jdbc là do tomcat cung cấp , tập tin mysql-connector-java-5.1.14-bin.jar được lưu trong
tomcat/commons/lib.
Sau khi đã đảm bảo danh sách dependency, chạy command line vào H:\sakai-src-2.7.1\MT06BK01\tool
Chạy lệnh
• Lúc này bạn chọn folder H:\sakai-src-2.7.1\MT06BK01\tool và bấm OK. Chú ý không chọn folder MT06BK01. Trong folder tool sẽ có một project con và một tập tin pom.xml, trong tập tin pom.xml này sẽ khai báo “cha” của nó là tập tin pom.xml nằm trực tiếp trong MT06BK01. Và bạn chỉ thực sự quan tâm đến project tool là con của project MT06BK01.
• Đối với cấu trúc chuẩn thì lẽ ra MT06BK01 sẻ có nhiều project con, mỗi project phụ trách một vài trò riêng về model, api, implementation và cuối cùng là webapp (hay tool) là project kết hợp các project khác thành sản phẩm cuối cùng.
• Ở đây chúng ta sẽ implement tất cả chỉ trong một project tool để giảm độ phức tạp.
• Trong đa số trường hợp gợi ý của m2Eclipse là đúng tuy nhiên trong trường hợp này cách mà tôi sử dụng là sửa kiểu UserNotDefineException thành
Exception:
public String getCurrentUserDisplayName() { try {
return
userDirectoryService.getCurrentUser().getDisplayName();
} catch (Exception e) {
log.warn("Cannot get current user display name"); return "---";
}} }
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://sakaiproject.org/jsf/sakai" prefix="sakai" %>
<%-- This file generated by Sakai App Builder --%> <f:view>
<sakai:view_container title="Hello World">
<style type="text/css">
@import url("/mt06bk01/css/Mt06bk01.css");
</style>
<sakai:view_content>
<sakai:view_title value="Hello,
#{HelloWorldBean.getCurrentUserDisplayName}"/>
<h:outputText value="It is now
#{HelloWorldBean.currentDate}"/>
</sakai:view_content>
</sakai:view_container>
</f:view>
Nhìn sơ bạn thấy ngoài các directive trên đầu trang thì bao toàn bộ nội dung là <f:view>. Do JSF sẽ lưu toàn bộ trạng thái của component trên trang (thí dụ nội dung mà bạn đã nhập vào textbox, option mà bạn đã chọn trên dropdownlist …) dưới dạng cây mà root chính là <f:view>. Tương tự tất cả các component mang taglib <sakai:> sẽ phải nằm trong <sakai:view> .Trong code phía trên bạn thấy <sakai_view_container> là một tag đã cũ và được thay thế bằng <sakai:view>. • Sửa toàn bộ nội dung của trang này như sau:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://sakaiproject.org/jsf/sakai" prefix="sakai" %>
<f:view>
<sakai:view>
<h:outputText value="This should be easy"></h:outputText>
<% /* MOT SO COMPONENT COMPONENT */ %>
<h:form>
<h:outputText value="Ten Cua Ban: "></h:outputText> <h:inputText></h:inputText>
<h:outputText value="Mat ma cua ban: ">
<h:inputText></h:inputText>
<h:commandButton value="Click Me!"></h:commandButton> </h:form>
</sakai:view>
</f:view>
Lúc này bạn có thể thực hiện việc deploy tool
H:\sakai-src-2.7.1\MT06BK01>mvn clean install sakai:deploy
Sau khi deploy thành công bạn chạy tomcat/bin/startup.bat, login vào Sakai bằng account administrator thực hiện việc add tool như sau:
• Vào My Work Space>Worksite Setup:
Có vẻ layout không được đúng cho lắm. Lúc này bạn vẫn giữ nguyên tình trạng đang chạy của Tomcat server, mở eclipse, vào edit lại trang HelloWorld.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://sakaiproject.org/jsf/sakai" prefix="sakai" %>
<f:view>
<sakai:view>
<h:outputText value="This should be easy"></h:outputText>
<% /* MOT SO COMPONENT COMPONENT */ %>
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Ten Cua Ban: " /> <h:inputText></h:inputText>
<h:outputText value="Mat ma cua ban: " /> <h:inputText></h:inputText>
<h:commandButton value="Click Me!"/>
</h:panelGrid>
</h:form>
</sakai:view>
</f:view>
• Ở đây tôi bao tất cả phần tử trong form lúc nãy bằng một component gọi là panelGrid với attribute columns là 2. Bạn mở command line vào project
MT06BK03 và chạy command
• Lệnh build của bạn đóng gói toàn bộ project thành tập tin WAR và khâu deploy là chép ile WAR này vào tomcat/webapp. Tomcat có cơ chế quản lí và nhận ra sự thay đổi của các trang JSP, khi đó tomcat sẽ tự động dịch và triển khai lại các tập tin JSP bị thay đổi. Việc tomcat nhận ra sự thay đổi này và dịch lại tập tin JSP có thể mất vài giây, bạn quan sát và chờ nội dung LOG như trên
• Lưu ý: Không phải thay đổi nào cũng có thể sử dụng cơ chế tự động cập nhật của tomcat như trên
• Component PanelGrid có tác dụng trình bày các component con dạng thành bảng, ở đây bạn thấy có 2 cột. Chúng ta có tất cả 5 phần tử 2 outputText, 2 inputText và một Button. 5 phần tử tự động được xếp vào vị trí tương ứng sao cho số phần tử tối đa mỗi hàng là 2. Ô cuối cùng trống do số phần tử lẻ và columns là 2. Bạn cũng có thể gom nhiều phần tử vào một cột bằng cách sử dụng <h:PanelGroup> bên trong thân của <h:panelGrid>.
Bạn đã tạo một form trên Sakai, tuy nhiên khi bạn bấm nút trên form thì không có gì xảy ra, bởi vì đó chỉ là những component chưa được gán trị và chưa được gắn hàm xử lí sự kiện.
bean> và kết thúc bằng </managed-bean>). Trong đó các thành phần bắt buộc là: • <managed-bean-name> HelloWorldBean </managed-bean-name>. Bạn sẽ truy suất đối tượng này trên trang JSP bằng expression #{ HelloWorldBean } bất chấp tên thật của Class. Nếu managed-bean-name bạn khai báo là sinhvien thì bạn sẽ truy suất properties của đối tương đó bằng #{sinhvien.mssv} với điều kiện mssv phải có method getter tầm vực public.
• <managed-bean-class>…</managed-bean-class> tên đầy đủ của Class của đối tương này, bao gồm package như bạn thấy ở trên.
• <managed-bean-scope>...<managed-bean-scope> tầm vực đối tượng này tồn tại, có thể là application, session hoạc request.
• Ở đây tôi chọn đặt tên package là :org.sakaiproject.mt06bk01.tool.beans • Tên class là: PVHoa
Bấm Finish để tiếp tục. Chọn mã nguồn của lớp PVH vừa tạo và khai báo hai thuộc tính (property) String userName, String password. Lưu ý khi khai báo thuộc tính cho bean tên của biến phải bắt đầu bằng kí tự thường (không in hoa), quên điều này sẽ gây ra rắc rối về sau vì sau này các biểu thức cần truy suất các thuộc tính tự động hiểu rằng bạn đặt tên của thuộc tính đúng chuẩn (kí tự đầu không in hoa).
package org.sakaiproject.mt06bk01.tool.beans;
public class PVHoa { String userName; String passWord;
public PVHoa(){}
public String getUserName() {
return userName; }
public void setUserName(String userName) {
this.userName = userName; }
public String getPassWord() {
return passWord; }
public void setPassWord(String passWord) {
this.passWord = passWord; }
• Trong các tài liệu chuẩn của Java thì Java bean phải có một constructor không tham số. Nếu lớp mà bạn tạo ra không có khai báo constructor nào thì mặc định nó có một default constructor chính là constructor không tham số.Tuy nhiên nếu bạn định nghĩa một constructor có tham số thì luật trên không được áp dụng, tức là bạn sẽ không thể gọi constructor không tham số nếu không khai báo trong mã nguồn. bạn cần chú ý điều này vì các bean trong JSF được tạo ra tự động bởi nhà máy(managed-bean facility) và việc không tạo được bean do chương trình không gọi được constructor không tham số là khá phổ biến.
Mở file faces-config.xml thêm vào một mục <managed-bean > như sau
<managed-bean>
<managed-bean-name>PhanVanHoa</managed-bean-name>
<managed-bean-class>
org.sakaiproject.mt06bk01.tool.beans.PVHoa
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
Cũng trong faces-config.xml sửa mục <navigation-rule > lại như sau:
<navigation-rule>
<from-view-id>/mt06bk01/HelloWorld.jsp</from-view-id> <navigation-case>
<from-outcome>Naruto</from-outcome>
<to-view-id>/mt06bk01/Home.jsp</to-view-id>
</navigation-case>
</navigation-rule>
• Nội dung trên quy định từ View có id /mt06bk01/HelloWorld.jsp khi có sự kiện xảy ra (thí dụ lúc người sử dụng bấm ào một Command Link hoặc
Command Button) nếu kết quả của sự kiện đó là Naruto(thật ra ở đây tôi đặt một cụm từ ngẫu nhiên) thì chuyển đến View có id là /mt06bk01/Home.jsp
<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD
JavaServer Faces Config 1.0//EN"
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <!-- This file generated by Sakai App Builder -AZ --> <faces-config>
<!-- Navigation Rules -->
<navigation-rule>
<from-view-id>/mt06bk01/HelloWorld.jsp</from-view-id> <navigation-case>
<from-outcome>Naruto</from-outcome>
<to-view-id>/mt06bk01/Home.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<!-- Backing Bean -->
<managed-bean>
<description>Mt06bk01 Tool: HelloWorld Bean</description>
<managed-bean-name>HelloWorldBean</managed-bean-name>
<managed-bean-class>
org.sakaiproject.mt06bk01.tool.jsf.HelloWorldBean </managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<description>User Directory Service</description>
<property-name>userDirectoryService</property-name>
<value> #{org_sakaiproject_user_api_UserDirectoryService} </value> </managed-property> </managed-bean> <managed-bean>
<managed-bean-name>PhanVanHoa</managed-bean-name>
<managed-bean-class>
org.sakaiproject.mt06bk01.tool.beans.PVHoa </managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Rightclick vào thư mục mt06bk01 và chọn tạo một tập tin JSP mới đặt tên là Home.jsp.Lúc nãy ta có khai báo một trường hợp chuyển trang (navigation - case) từ HelloWorld.jsp sang Home.jsp).
lựa chọn đó chỉ tạo ra một số code mẫu cho trang JSP của bạn, bạn sẽ không sử dụng code này mà copy code từ trang HelloWorld.jsp đã có ở trên để sử dụng lại các khai báo chỉ thị ở đầu trang (page directive) và phần bao bên ngoài của nội dung trang
<F:VIEW> <SAKAI:VIEW> … </SAKAI:VIEW> </F:VIEW>
Sửa nội dung trang Home.jsp như sau
Vào Folder project chạy lệnh: mvn clean install sakai:deploy. Chạy tomcat/bin/startup.bat, login vào Sakai để kiểm tra lại:
Ở đây bạn thấy chương trình in ra thông tin mà bạn đã nhập vào hai ô nhập liệu ở trang trước thông qua <h:outputText> với value =
#{PhanVanHoa.userName} và value = #{PhanVanHoa.passWord}. Do
PhanVanHoa được lưu trong phiên(session), bạn có thể truy suất biến này trên mọi trang JSP trong phiên làm việc.
• Chú ý: một lỗi thường gặp ngay cả đối với bản thân tôi là dùng Expression truy suất thuộc tính của một đối tượng mà quên mất khai báo hai hàm getter và setter cho thuộc tính đó. Lỗi thứ hai thường gặp ở các bạn mới làm quen với JavaBean và các chuẩn đặt tên, nếu bạn đặt tên UserName thay vì username, bạn sẽ không truy xuất được thuộc tính này bằng biểu thức
#{PhanVanHoa.UserName}, trong cả 2 cách đặt tên getter và setter của bạn đều là getUserName và setUserName.
Sửa mục <navigation-rule> lại như sau:
<!-- Navigation Rules -->
<navigation-rule>