Hiển thị các bài đăng có nhãn Frameworks. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Frameworks. Hiển thị tất cả bài đăng

Ứng dụng HelloWorld Spring Portlet MVC Framework

Cách tốt nhất để học một framework mới là viết một ứng dụng HelloWorld dùng nó. Tôi xin bắt đầu theo cách này, viết một HelloWorld portlet dùng Spring Portlet MVC Framework. Để đơn giản, ứng dụng HelloWorld của tôi chỉ có mode View. Bất cứ khi nào người dùng truy cập đến HelloWorld portlet, chúng ta chỉ hiển thị trang View.jsp.


portlet.xml


Hãy bắt đầu với file portlet.xml trong webapp/WEB-INF/ như sau:


<?xml version="1.0" encoding="UTF-8"?>
<portlet-app
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" >
<portlet>
<description>HelloWorld Portlet using Spring MVC</description>
<portlet-name>HelloSpringPortletMVC</portlet-name>
<display-name>Hello World Spring Portlet MVC Framework Portlet</display-name>
<portlet-class> org.springframework.web.portlet.DispatcherPortlet
</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>

<supported-locale>en</supported-locale>
<portlet-info>
<title>
Hello World Spring Portlet MVC Framework Portlet
</title>
<short-title>
HelloWorldSpringPortletMVC
</short-title>
<keywords>
spring portlet
</keywords>
</portlet-info>
</portlet>
</portlet-app>

DispatcherPortlet chịu trách nhiệm điều khiển request từ người dùng. Khi nhận được một request, nó sẽ tìm Controller chịu trách nhiệm xử lý cho request này, sau đó gọi handleActionRequest() hay handleRenderRequest() tùy thuộc vào kiểu sử lý của request. Controller class thi hành phần business logic và trả lại một View để trả kết quả xử lý cho người dùng.

Như đã thấy, DispatcherPortlet là trung tâm xử lý trong Spring Portlet MVC Framework. Ngoài ra, DispatcherPortlet còn chịu trách nhiệm loading application context (file cấu hình của Spring). Đầu tiên, kiểm tra giá trị của configLocation. Nếu không được khai báo, nó sẽ đặt giá trị mặc định là <portlet-name>-portlet.xml, và tìm trong thư mục /WEB-INF. Trong file portlet.xml, chúng ta không khai báo configLocation, vậy ta cần tạo file HelloWorldPortletMVC-portlet.xml.

HelloSpringPortletMVC-portlet.xml


Tạo file HelloSpringPortletMVC-portlet.xml trong webapp/WEB-INF như sau:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
\"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="viewController"
class="com.bds.example.springmvc.ViewController"></bean>
<bean id="portletModeHandlerMapping" class=
"org.springframework.web.portlet.handler.PortletModeHandlerMapping">
<property name="portletModeMap">
<map>
<entry key="view">
<ref bean="viewController"/>
</entry>
</map>
</property>
</bean>
</beans>

Đây là file cấu hình của portlet HelloSpringPortletMVC. Có 2 beans được định nghĩa:
  • viewController. Chỉ đến class com.bds.example.springmvc.ViewController.java.
    portletModeHandlerMapping. Như ta đã biết, khi DispatcherPortlet nhận được một request, nó sẽ tìm Controller thích hợp để điều khiển request đó. PortletModeHandlerMapping là nơi điều khiển view.

  • PortletModeHandlerMapping là một class đơn giản hiện thực từ interface HandlerMapping và được DispatcherPortlet dùng để tìm Controller thích hợp cho mọi request. Class PortletModeHandlerMapping dùng mode của Portlet để tìm Controller.


  • ViewController.java


    Ta biết rằng viewController bean sẽ xử lý tất cả View mode requests. Bước tiếp theo là tạo ViewController.java như sau:



    package com.bds.example.springmvc;

    import javax.portlet.RenderRequest;
    import javax.portlet.RenderResponse;
    import org.springframework.web.portlet.ModelAndView;
    import org.springframework.web.portlet.mvc.AbstractController;

    public class ViewController extends AbstractController{
    protected ModelAndView handleRenderRequestInternal(RenderRequest request,
    RenderResponse response) throws Exception {
    ModelAndView modelAndView = new ModelAndView("View");
    return modelAndView;
    }
    }


    Chú ý: Nếu bạn đã từng dùng Struts Framework, thì Controller class cũng giống như một Action class trong Struts.
    Mọi controller class trong Spring Portlet MVC Framework phải trực tiếp hoặc gián tiếp implement interface org.springframework.web. portlet.mvc.Controller. Để đơn giản hơn, Spring Framework cung cấp AbstractController, và một số extends phục vụ một số action cơ bản. Ta cũng có thể tự implement Controller class, nhưng phải quan tâm đến một số vấn đề như: reuse, thread-safe, và khả năng điều khiển nhiều requests trong suốt vòng đời của portlet.
    Trong code trên, tôi tạo ViewController class bằng cách extend từ AbstractController. Vì ta chỉ cần xử lý cho mode view, nên ta chỉ cần override phương thức handleRenderRequest() từ AbstractController. Với controller trên, HelloWorldPortletMVC sẽ chỉ render trang View.jsp đến người dùng khi người dùng truy cập đến portlet này.

    web.xml


    Theo Portlet Specification 1.0, mọi ứng dụng portlet sẽ theo chuẩn của Servlet Specification 2.3, và cần một file miêu tả cho deployment(web.xml). Tạo web.xml trong thư mục /WEB-INF/ như sau:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
    "http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>
    Hello Spring Portlet MVC Framework Application
    </display-name>
    <servlet>
    <servlet-name>ViewRendererServlet</servlet-name>
    <servlet-class>
    org.springframework.web.servlet.ViewRendererServlet
    </servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>ViewRendererServlet</servlet-name>
    <url-pattern>/WEB-INF/servlet/view</url-pattern>
    </servlet-mapping>
    <listener>
    <listener-class>
    org.springframework.web.context.ContextLoaderListener
    </listener-class>
    </listener>
    </web-app>

    File web.xml khai báo 2 việc:
  • ViewRendererServlet. ViewRendererServlet là một cầu nối thể hiện servlet cho portlet. Trong quá trình render, DispatcherPortlet đặt PortletRequest trong ServletRequest và chuyển điều khiển cho ViewRendererServlet để render. Bước này cho phép Spring Portlet MVC Framework dùng lại các thành phần View như trong phiên bản cho servlet, là Spring Web MVC Framework.

  • ContextLoaderListener. ContextLoaderListener chịu trách nhiệm phần load file cấu hình Web Application context tại thời điểm ứng dụng bắt đầu chạy. Web application context được chia sẻ cho tất cả các portlet trong cùng ứng dụng portlet. Trường hợp có 1 bean được định nghĩa 2 lần, bean được định nghĩa trong portlet application context được ưu tiên hơn trong Web application context.


  • ContextLoader đọc giá trị của contextConfigLocation. Nếu contextConfigLocation không được khai báo, sẽ dùng giá trị mặc định là /WEB-INF/applicationContext.xml.

    applicationContext.xml


    Như đã nói ở trên, nếu giá trị của contextConfigLocation không được khai báo, ViewRendererServlet sẽ lấy giá trị mặc định là /WEB-INF/applicationContext.xml file. Tạo applicationContext.xml có nội dung sau:


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

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    <bean id="viewResolver" class=
    "org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass">
    <value>
    org.springframework.web.servlet.view.JstlView
    </value>
    </property>
    <property name="prefix">
    <value>/WEB-INF/jsp/</value>
    </property>
    <property name="suffix">
    <value>.jsp</value>
    </property>
    </bean>
    </beans>

    Trong code trên, file applicationContext.xml định nghĩa 1 bean là viewResolver. viewResolver được dùng để khai báo nơi chứa các file JSP.
    Ta khai báo 2 giá trị cho viewResolver bean:
  • Prefix – thư mục chứ file jsp /WEB-INF/jsp/

  • Suffix – đuôi của file phải là JSP

  • Lấy một ví dụ, khi đối tượng ModelAndView trả về với giá trị là View thì file được view ra là /WEB-INF/jsp/View.jsp.
    Và cuối cùng, tạo một file View.jsp đơn giản với thông điệp "Hello from Spring Portlet MVC Framework" trong thư mục /WEB-INF/jsp.

    Đọc tiếp >>

    Cao Trong Hien

    ,

    Portlet và Spring Portlet MVC

    Và khi portal ra đời. Không theo chuẩn xây dựng website đang có, nó có thể thấy nhiều ứng dụng chỉ trong một trang portal. Không những thế, portal còn phục vụ cho sở thích, quyền hạn của người dùng. Nghĩa là mỗi người dùng sẽ có một phiên bản khác, được thiết kế cho riêng họ.

    Lịch sử hình thành Portlet


    “Thinks outsite the box” – Hãy suy nghĩ ra khỏi giới hạn

    Mặc cho lời khuyên này, chúng ta vẫn thường xuyên “suy nghĩ trong giới hạn”. Hollywood Square, bưu điện, niên giám của các trường đại học, game sodoku là những thứ mà ta có thể thấy được về vấn đề “suy nghĩ trong giới hạn”. Vậy chúng ta mong chờ gì về việc này khi mà chúng ta thường xuyên đối diện với các giới hạn này?

    Việc suy nghĩ này ta gặp thường xuyên nhất là với các hệ điều hành như Microsoft Windows hay Mac OS. Hãy nhớ lại những ngày mới ra MS-DOS, ta chỉ có thể chạy một ứng dụng tại một thời điểm. Nhưng các thế hệ của nó sau này, không những có thể chạy nhiều ứng dụng một lúc, mà ta còn có thể nhìn thấy chúng cùng lúc.

    Một ứng dụng web cũng như một ứng dụng chạy trên MS-DOS, chỉ có thể tương tác trên một ứng dụng tại một thời điểm. Nếu muốn muốn dùng một ứng dụng web khác, bạn phải dừng ứng dụng này, và truy cập đến ứng dụng đó, trên một cửa sổ mới(hay tab mới).

    Và khi portal ra đời. Không theo chuẩn xây dựng website đang có, nó có thể thấy nhiều ứng dụng chỉ trong một trang portal. Không những thế, portal còn phục vụ cho sở thích, quyền hạn của người dùng. Nghĩa là mỗi người dùng sẽ có một phiên bản khác, được thiết kế cho riêng họ.

    Hình H.1 : Ví dụ điển hình cho portal là iGoogle.

    Đến khi Sun công bố Java Portlet Specification (JSR 168), là lúc bắt đầu một xu hướng xây dựng portlet. Đó là một tiêu chuẩn dể xây dựng, và phát triển portlet với java. Nói một cách ngắn gọn, nó định nghĩa các quy ước giữa những portlet, và cách nó tương tác với portal server.

    Trong bài viết này, chúng ta sẽ có một cái nhìn đầu tiên về Spring Portlet MVC, một công cụ giúp ta xây dựng ứng dụng dựa trên chuẩn của Java Portlet API.

    Mặc dù Spring Portlet MVC là một thành phần riêng biệt với Spring MVC, nhưng chúng chia sẽ những tính năng tương tự nhau. Thực tế, khi làm việc với nó, bạn sẽ thấy rằng chúng có một sự tương đồng, và Spring Portlet MVC còn dùng lại một số class trong Spring MVC. Vì vậy, sẽ rất dễ dàng nếu như bạn có một kiến thức nhất định về Spring MVC.

    Bạn cần một kiến thức cơ bản về Java Portlet Specification và biết cách xây dựng một portlet đơn giản. Nếu bạn mới đến với Java Portlet , hay đơn giản là chỉ muốn tìm một cái gì mới, tôi nghĩ bạn nên đọc quyển Portlets and Apache Portals (Manning, 2005) hay Building Portals with the Java Portlet API (Apress, 2004).

    Suy nghĩ trong giới hạn


    Có nhiều điểm tương đồng giữa Portlet và Servlet. Ta có thể thấy ngay như sau:
    Một Servlet được viết bằng cách implements javax.servlet.Servlet hay thông dụng hơn là extends từ javax.servlet.http.HttpServlet. Tương tự, một portlet cũng được viết bằng cách implement javax.portlet.Portlet hay extends từ javax.portlet.GenericPortlet.
    Khi implement từ javax.servlet.Servlet , phương thức cần implement là service(). Khi implements từ javax.portlet.Portlet , có 2 phương thức cần implement là processAction() , và render().
    Nếu bạn chọn mở rộng từ javax.servlet.http.HttpServlet , bạn cần implement doGet(), doPost(), doPut(), hay doDelete() để xử lý request. Với javax.portlet.GenericPortlet bạn cần implement doView(), doEdit(), hay doHelp() để xử lý portlet request.

    Và cũng có nhiều điểm khác nhau :
    Servlet có đầu ra là một trang web, và chiếm hết diện tích của trình duyệt. Portlet thì ngược lại, nó phải chia sẽ trang với các portlet khác.
    Một portlet có thể có nhiều modes. Phần lớn những chức năng của portlet đều nằm trên view mode. Nhưng một portlet có thể cung cấp edit mode để cấu hình portlet, help mode cung cấp thông tin trợ giúp người dùng. Ngược lại, servlet không có khái niệm modes, mà chỉ có view mode.
    Vòng đời của portlet request phức tạp hơn servlet. Trong khi servlet chỉ xử lý một kiểu request, portlet xử lý cả ActionRequests và RenderRequests.

    Tại sao Portlet cần MVC?


    Dùng MVC Frameworks (Struts hay Spring MVC) cho ta một cách tương tác tốt khi phát triển một ứng dụng web với servlet, nhưng ta cũng có thể xây dựng mà không dùng chúng. Vì một ứng dụng servlet có thể dễ dàng chuyển trang, hay gọi một chức năng khác. Nhưng khi một ứng dụng chỉ muốn một servlet có thể điều khiển tất cả các tính năng, thì không thể xây dựng bằng servlet được.
    Một ứng dụng portlet thì khác. Nó có thể có nhiều portlet chỉ chứa trong 1 portal view. Nó chỉ có thể render trong space mà nó đang quản lý, còn việc dùng một portlet khác trong space của nó thì không thể.
    Nếu bạn đang xây dựng một portlet đơn giản, như dự báo thời tiết, RSS view hay một portlet chỉ có 1 đến 2 view, thì bạn chẳng cần một MVC.
    Nhưng nếu không dùng MVC, viết một ứng dụng hướng tính năng thì có thể làm bạn nản. Như bạn thấy trong hình H.2, phương thức processAction() và render() có thể trở thành một mớ những if/else để có thể điều hướng các thao tác trên portlet.
    Hình H.2: Không có MVC framework, một portlet sẽ gánh thêm trách nhiệm chuyển request.

    Khi có MVC, bạn có thể thấy trong hình H.3, một controller portlet có thể điều khiển các reequest đó, và gởi chúng đến một controller thích hợp để xử lý.
    Hình H. 3: Có MVC framework sẽ giúp ta điều hướng request dễ dàng.

    Một cách ngắn gọn, ứng dụng portlet cần MVC framework để điều khiển những tính năng phức tạp. Nhưng chúng ta tìm chúng ở đâu?

    Spring Portlet MVC


    Nhận thức được sự cần thiết của Portlet MVC Framework, Spring đã đưa Spring Portlet MVC (SPMVC) vào Spring 2. SPMVC được viết dựa trên Spring MVC Framework để xây dựng ứng dụng portlet. Dùng SPMVC, chúng ta có thể xây dựng một ứng dụng web hướng tính năng, chỉ chạy trên space của portlet mà portal cung cấp.

    Về vòng đời của portlet request trong SPMVC thì không khác gì so với Spring MVC. Như bạn thấy trong hình H.4, trạm dừng đầu tiên của một portlet request là DispatcherPortlet(1). DispatcherPortlet làm việc gần giống như DispatcherServlet, là chuyển quyền thực hiện đến controller.

    Handler Mapping(2) cho biết controller nào sẽ chịu trách nhiệm xử lý request này. Portlet Handler Mapping cũng tương tự như Spring MVC Handler Mapping, thay URL bằng Map Portlet Modes trong controller.

    Một controller thích hợp được chọn, DispatcherPortlet gởi request thẳng đến controller đó để xử lý (3).
    Hình H.4: DispatcherPortlet chuyển portlet request đến controller và view, dựa vào handler mapping và view resolver.

    Đây là nơi mà vòng đời của portlet request bắt đầu. Nhớ rằng có 2 kiểu của request là action request và render request. Nếu là 1 action request, vòng đời của nó được chuyển qua controller để xử lý. Nhưng nếu là render request, thì cần một số trạm xử lý khác.

    Để xử lý một render request, controller sẽ trả về một đối tượng(object) ModelAndView cho DispatcherPortlet. Nó sẽ dùng cùng một ModelAndView với Spring MVC controller. Nó sẽ chứa tên của view được trả về cùng với một số dữ liệu cần để view.

    Tại đây, DispatcherPortlet đã có tất cả những thứ cần để thể hiện lên portpet. Nhưng đầu tiên, nó cần tìm trang thể hiện bằng cách gọi đến view resolver(5).

    Bước cuối cùng cho một render request trang view(có thể là JSP). View sẽ dùng dữ liệu trong request để view trong portlet space, của portal page.

    Với những kiến thức trên, bạn đã có thể làm một ứng dụng portlet đầu tiên dùng SPMVC.

    Đọc tiếp >>

    Cao Trong Hien

    ,

    Mặt trái của Frameworks


    Từ Framework đã quá thông dụng. Nhờ nó mà số dòng code của chúng ta giảm đi thấy rỏ. Và các ứng dụng ngày nay, còn có thêm một yêu cầu về frameworks dùng. Tuy nhiên, có một điều mà hầu như ai cũng biết: mặt trái của nó.



    Tôi đã dùng JSF ngay từ khi bắt đầu đến với Java WEB. Một components base framework, giúp ta tối ưu hoá việc sử dụng MVC trong web. Tôi đã hầu như không gặp bất kỳ một trở ngại nào trong các ứng dụng thuộc loại cổ điển (các bài quản lý như: thư viện, sinh viên, nhân viên, thư viện, ...).

    Nhưng khi dùng Ajax thì thật sự nó là một gánh nặng. Muốn làm một component Ajax cho JSF quả là rất khó khăn - dĩ nhiên là không phải không làm được. Bạn cần có một kiến thức rỏ ràng về vòng đời các thành phần, biết cách vượt qua một số giai đoạn, và một thành phần không thể không biết là PhaseListener.

    Có một số hãng phát triển cho mình một số Ajax for JSF frameworks như IceSoft với icefaces, Jboss với Richfaces, và Oracle với Netadvantage. Tuy nhiên, việc dùng frameworks nào cần có một bước phân tích tỉ mỉ về tất cả các vấn đề cần phải dùng frameworks. Đó là chưa kể đến vấn đề về lỗi kỹ thuật của nó, và thường thì ta phải chờ fix, đó là một ảnh hưởng rất lớn đến dự án.

    Ở tầng business thì ta không thể phủ nhận những tính năng của Spring. Tuy nhiên, mặt tối của nó đã dẫn đến một bài viết về một cuộc phỏng vấn điển hình mà Giovani Salvador nhắc đến trong bài Java Architectural Knowledge for Job Interviews. Are we prepared? Nó cũng làm tôi nhớ đến vấn đề mà Robert Nocera đưa ra là gọi Stored Procedures từ JDBCTemplate của Spring.

    Và cuối cùng: “Một giải pháp hoàn hảo là giải pháp không dùng Frameworks”.

    Đọc tiếp >>

    Cao Trong Hien

    ,

    Dojo JSON-RPC + Java


    Dojo hỗ trợ JSON-RPC, là tính năng mở rộng của JSON dùng để triệu gọi phương thức từ xa. JSON-RPC có thể dùng như một phương tiện cho các ứng dụng cần giao tiếp với servers. Ví dụ sau sẽ chỉ ra cách tương tác với Java trên server và gọi một phương thức Java từ JavaScript dùng Dojo RPC services. Nó cũng cho thấy việc sử dụng JSON-RPC dễ dàng hơn khi dùng Java.

    Đầu tiên, ta tạo một servlet và viết phương thức POST sẽ đọc nội dung của request:


    public class JsonRPC extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    String content = streamToString(req.getInputStream());

    Ta dùng json.org’s Java library để phân tích và lấy ra các thuộc tính từ JSON object. Một JSON-RPC object gồm có 3 thuộc tính được định nghĩa để gọi: method, params, và id. Như sau:


    JSONObject rpcObject = new JSONObject(content);
    String methodName = rpcObject.getString("method");
    String id = rpcObject.getString("id");
    JSONArray paramsArray = rpcObject.getJSONArray("params");
    Object[] params = new Object[paramsArray.length()];
    for (int i = 0 ; i < paramsArray.length(); i++) {
    params[i] = paramsArray.get(i);
    }

    Ta có thể dùng những thuộc tính này để chạy một phương thức trên một object. Nếu ta đã có tạo sẵn một object dùng như một target cho RPC, ta có thể gọi phương thức và thi hành nó:

    for (Method method : target.getClass().getMethods()) {
    // we could use getMethod(methodName, paramTypes) here but
    // we would need to create a list of parameters types
    if (method.getName().equals(methodName)) {
    Object result = method.invoke(target, params);
    ...

    Sau khi trả kết quả lại cho client. Response object cũng có 3 thuộc tính: id, result, và error. Ta sẽ dùng JSON library để dựng lại object này:

    response.put("id",id); // id from the request
    response.put("result",result); // result of the invocation
    response.put("error",JSONObject.NULL); // no error
    // send the response to the client
    resp.getOutputStream().print(response.toString());

    Đó là tất cả những gì cần để xây dựng class để gọi JSON-RPC. Cũng có thể dùng try/catch để kiểm soát lỗi:

    try {
    ...
    Object result = method.invoke(target, params);
    ...
    } catch (Throwable e) {
    response.put("id",id); // id from the request
    response.put("result",JSONObject.NULL);
    response.put("error",e.getMessage());
    }

    Tôi sẽ tạo một class rất đơn giản để gọi JSON-RPC:

    public class Echo {
    public String sayHi(String from) {
    return "Hi " + from;
    }
    }

    Và hiện thực:

    target = new Echo();

    Tiếp theo là khai báo một servlet trong file web.xml:

    <servlet>
    <servlet-name>JsonRpc</servlet-name>
    <servlet-class>com.sitepen.JsonRPC</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>JsonRpc</servlet-name>
    <url-pattern>/jsonrpc/</url-pattern>
    </servlet-mapping>


    Gọi phương thức với Dojo



    Trên client side, chúng ta có thể dùng simple method description (SMD) object để khai báo JSON-RPC và Dojo sẽ tạo phương thức thích hợp.

    dojo.require("dojox.rpc.Service");
    dojo.require("dojox.rpc.JsonRPC");

    echoService = new dojox.rpc.Service({
    envelope:"JSON-RPC-1.0",
    transport:"POST",
    target:"/jsonrpc/echo",
    services:{
    sayHi:{
    parameters:[{type:"string"}] // this is optional
    }
    }
    });

    Giờ thì echo đã có phương thức sayHi thi hành tương ứng với phương thức trên Echo class. Chúng ta có thể gọi sayHi chỉ với một phương thức trên JavaScript. Tham số sẽ bao gồm trong JSON-RPC và dùng để gọi phương thức trên server. Ví dụ:

    var deferred = echoService.sayHi("Kris");

    Thi hành sayHi với đối số “Kris”. Giá trị trả về là Deferred object, và bạn có thể dùng bình thường, hay dùng callback:

    deferred.addCallback(function(result) {
    alert(result); // executed when the RPC is finished
    });

    Khi RPC response được trả về, callback function sẽ trả kết quả trên alert. Và “Hi Kris” hiện ra.

    Multiple Target Objects


    Chúng ta đã thực hiện xong phần triệu gọi RPC cho một object. Ta cũng có thể thực hiện cho nhiều objects. Một trong những điều kiện để có thể dùng multiple objects là tạo nhiều URL paths cho các target objects:

    static Map rpcTargets = new HashMap();
    public static void registerRpcTarget(String path, Object target) {
    rpcTargets.put(path,target);
    }

    Sau đó là tìm đến target objects bằng URL path:

    String targetPath = req.getPathInfo();
    Object target = rpcTargets.get(targetPath);

    Thêm một target objects là vần đề đơn giản nhất. Cho echo object, ta có thể khai báo như sau:

    registerRpcTarget("/echo",new Echo());

    Còn nếu bạn đã tạo một object mới, khai báo theo cấu trúc:

    registerRpcTarget("/myObject",new MyClass());

    Và thêm vào client side một service object khác từ SMD:

    myObject = new dojox.rpc.Service({
    envelope:"JSON-RPC-1.0",
    transport:"POST",
    target:"/jsonrpc/myObject",
    services:{
    foo:{
    parameters:[{type:"string"}] // this is optional
    }
    }
    });

    Bạn có thể download đầy đủ source tại đây. Chúc thành công!

    Đọc tiếp >>

    Cao Trong Hien

    , ,

    Dependency Injection với Abstract Factory


    Bài viết này, chúng ta cùng tìm hiểu ưu và nhược điểm của Dependency Injection (DI - “tiêm ” phụ thuộc) với một số cách khác nhau dùng Abstract Factory design pattern. Đặt trong ngữ cảnh của một số vấn đề như: Tạo stateful objects với tham số, điều khiển kiểm tra exceptions khi tạo object, và gắn động các objects. Mà các IoC frameworks, như Spring IoC container, PicoContainer và Guice, không đưa ra giải pháp tốt nhất cho các trường hợp này.

    Abstract Factory Design Pattern cho Dependency Injection


    GoF* Abstract Factory design pattern được tóm gọn theo 2 bước:
    Một factory interface thay thế cho một class abstract factory (Optional),
    Mọi factory method chịu trách nhiệm tạo một object và tiêm phụ thuộc vào nó.
    Xét ví dụ đơn giản sau:

    ComponentA phụ thuộc vào ComponentB. Để có thể unit-testing class ComponentAImpl, hiện thực của interface ComponentB phải tiêm vào ComponentAImpl. Đoạn code sau minh hoạ cách dùng Abstract Factory design pattern để tiêm phụ thuộc.

    //Abstract Factory for Dependency Injection
    //Factory interface
    public interface Module1ServiceFactory {

    ComponentA getComponentA();

    ComponentB getComponentB();
    }

    //Concrete factory
    public class Module1ServiceFactoryImpl implements Module1ServiceFactory {

    private ComponentA componentA;
    private ComponentB componentB;
    private Module1Servicefactory instance;

    private Module1ServicefactoryImpl() {
    }

    public static synchronized Module1ServiceFactory getInstance() {
    if (null == instance) {
    instance = new Module1ServicefactoryImpl();
    componentA = new ComponentAImpl();
    componentB = new ComponentBImpl();
    componentA.setComponentB(componentB);
    }
    return instance;
    }

    public ComponentA getComponentA() {
    return componentA;
    }

    public ComponentB getComponentB() {
    return componentB;
    }
    }

    //Client
    public class Client {

    public static void main(String[] args) {
    Module1ServiceFactory m1sf =
    Module1ServicefactoryImpl.getInstance();
    ComponentA componentA = m1sf.getComponentA();
    componentA.operationA1();
    }
    }


    Lazy-instantiation


    Lazy-instantiation có thể thực hiện bàng cách thay đổi phương thức để có thể xem như objects đã được tạo và chỉ lấy ra khi cần. Để làm điều này, getComponentA() có thể chỉnh lại như sau:

    public synchronized ComponentA getComponentA() { if (null == componentA) { componentA = new ComponentAImpl(); } return componentA; }

    Dĩ nhiên Module1ServiceFactoryImpl.getInstance() phải được sửa lại không tạo objects.
    Ta có thể thêm parameter cho Module1ServiceFactoryImpl .getInstance() để cho biết nó có cần tạo objects hay không.

    Non-Singleton Scope


    Đoạn code trên sẽ tạo singleton objects. Nếu một object mới chỉ cần khi gọi phương thức getComponentA() và getComponentB(), thì ta sửa lại như sau:


    //Concrete factory
    public class Module1ServiceFactoryImpl {
    private Module1ServiceFactory instance;

    private Module1ServiceFactoryImpl() {}

    public static synchronized Module1ServiceFactory getInstance() {
    if (null == instance) {
    instance = new Module1ServiceFactoryImpl();
    }

    return instance;
    }

    public ComponentA getComponentA() {
    ComponentA componentA = new ComponentAImpl();
    ComponentB componentB = getComponentB();
    componentA.setComponentB(componentB);
    return componentA;
    }


    public ComponentB getComponentB() {
    return new ComponentBImpl();
    }
    }


    Ta có thể tiêm một singleton object vào một non-singleton object. Ví dụ, nếu ComponentB là một singleton, và ComponentA là một non-singleton, ta có thể làm như sau:

    //Concrete factory
    public class Module1ServiceFactoryImpl {
    private Module1ServiceFactory instance;
    private ComponentB componentB;

    private Module1ServicefactoryImpl() {}

    public static synchronized Module1ServiceFactory getInstance() {
    if (null == instance) {
    instance = new Module1ServiceFactoryImpl();
    componentB = new ComponentBImpl();
    }

    return instance;
    }

    public ComponentA getComponentA() {
    ComponentA componentA = new ComponentAImpl();
    componentA.setComponentB(componentB);
    return componentA;
    }

    public ComponentB getComponentB() {
    return componentB;
    }
    }

    Ngược lại ta cũng có thể tiêm một non-singleton object cho một singleton, tuy nhiên trường hợp này rất ít gặp.

    Tạo Local Stateful Objects với tham số cho Singletons


    Đây là vấn đề đặt trưng cho tất cả các IoC frameworks. Được minh hoạ bằng phương thức cho ComponentA singleton:

    public void operationA2() {
    String s = aPrivateMethod();
    int i = anotherMethod();
    ComponentC componentC = new ComponentCImpl(s, i);
    //do something else.
    }

    Ở đây, ComponentAImpl dùng ComponentC. Hiển nhiên là chúng ta cần tiêm cho nó một hiện thực của ComponentC là ComponentCImpl. Tuy nhiên, chúng ta không thể tạo biến componentC khi nó là một stateful object với trạng thái chỉ dùng được trên client và sẽ không thể dùng trong clients khác thông qua threads. Vậy, nó không thể tiêm dùng setter hay constructor.
    Có 2 giải pháp để giải quyết vấn đề này dùng Abstract Factory pattern. In both schemes, chúng ta cần thay đổi Module1ServiceFactory và thêm một tham số cho factory method:

    ComponentC getComponentC(String s, int i);

    Và hiện thực phương thức này trong Module1ServiceFactoryImpl:

    public ComponentC getComponentC(String s, int i) {
    return new ComponentCImpl(s, i);
    }

    Giải pháp thứ nhất là tiêm factory object vào class có local stateful objects cần:

    private Module1ServiceFactory factory;
    public void setModule1ServiceFactory(Module1ServiceFactory factory) {
    this.factory = factory;
    }

    ComponentAImpl.operationA2() trở thành:

    public void operationA2() {
    String s = aPrivateMethod();
    int i = anotherMethod();
    ComponentC componentC = factory.getComponentC(s, i);
    //do something else.
    }

    Bất lợi của cách này là ComponentAImpl phụ thuộc vào Module1ServiceFactory và chúng ta cần cung cấp một mock hiện thực Module1ServiceFactory để unit-test ComponentAImpl. Mặc dù có nhược điểm như vậy, nhưng tiêm factory object vào một class dùng trong việc tạo local stateful objects là đơn giản nhất. Và được dùng nhiều nhất trong J2EE technology. Ví dụ, trong JPA (Java Persistence API), một entity manager factory có thể tiêm vào code, và một application-managed entity manager được tạo từ entity manager factory.
    Cách thứ 2 để tạo local stateful objects là chuyển phương thức sang abstract class, có thể dùng trong unit-tested, và có objects phụ thuộc cụ thể trong subclass.

    public abstract class AbstractComponentA implements ComponentA {
    public void operationA2() {
    String s = aPrivateMethod();
    int i = anotherMethod();
    ComponentC componentC = getComponentC(s, i);
    //do something else.
    }

    public abstract ComponentC getComponentC(String s, int i) ;

    }

    public class ComponentAImpl extends AbstractComponentA {
    public ComponentC getComponentC(String s, int i) {
    return new ComponentCimpl(s, i);
    }

    }

    Cách này được dùng nhiều trong Method Injection của Springframework chỉ hỗ trợ trường hợp không cần tham số khi tạo stateful objects. Unit-testing code không cần hiện thực mock factory. Tuy nhiên, đó là một giải pháp nguy hiểm. Giả sử chúng ta có 10 local stateful objects trong, chúng ta cần 10 abstract methods cho mục đích dùng unit-testing. Đúng là có thể dùng unit-testing và dùng rất đơn giản – at the expense of cluttered application code.
    Springframework đưa ra phương thức có thể giải quyết vấn đề này dùng Java reflection. Nhưng nó là một cách rất phức tạp và không phù hợp cho một application bình thường.

    Kiểm tra Exceptions Thrown khi tạo Object


    Đây là vấn đề mà một IoC container thường làm không tốt. Nếu kiểm tra exceptions được tung ra trong quá trình tạo object, ứng dụng có thể muốn bắt và thu lại. Lấy ví dụ một web service client, nếu web service chưa được tạo khi client gọi, client có thể bắt exception mà nó tung ra, hiện messages phù hợp và đề nghị thực hiện lại. Trường hợp này khó cho IoC containers để tung ra lổi đã được khai báo trong ứng dụng. Factories có thể dể dàng điều khiển trạng thái – chúng ta có thể đơn giản khai báo exceptions cần kiểm tra và tung lổi trong factory methods và cho phép application code điều khiển và thu lại ngay tại đó.

    Gắn linh động


    Là trường hợp có nhiều hiện thực khác nhau của cùng một interface cần tiêm vào một object khác nhau. Một ví dụ hay là Strategy design pattern. Một tham số có thể dùng để cho biết class cụ thể nào cần được tiêm. Trường hợp này khó hiện thực theo kiểu tiêm phụ thuộc trong file XML hỗ trỡ trong IoC containers, chỉ đơn giản cung cấp một cách để lắp các objects theo cách tĩnh. Lập trình IoC containers có thể cho phép giải quyết cấn đề phụ thuộc động. Tuy nhiên, tiêm phụ thuộc bằng tay với Abstract Factory cung cấp một cách đơn giản nhất và và là một giải pháp đơn giản nhất trong trường hợp này, như ta thấy trong ví dụ sau:

    //Concrete factory
    public class Module1ServiceFactoryImpl {
    ...

    public ComponentA getComponentA(int strategy) {
    ComponentA componentA = new ComponentAImpl();
    ComponentB componentB = getComponentB(strategy);
    componentA.setComponentB(componentB);
    return componentA;
    }


    public ComponentB getComponentB(int strategy) {
    switch(strategy) {
    case STRATEGYA:
    return new StrategyA();
    case STRATEGYB:
    return new StrategyB();
    default:
    return null;
    }
    }
    }

    Trong ví dụ này, cả StrategyA và StrategyB đều là hiện thực của interface ComponentB.

    Tổng kết


    Các trường hợp đã được đưa ra cho vấn đề tiêm phụ thuộc với Abstract Factory design pattern bao gồm tạo local stateful objects từ tham số động, điều khiển kiểm tra exceptions trong quá trình tạo object, và gắn linh động. Bên cạnh đó, vấn đề là một cách tốt nhất để so sánh với các IoC containers từ dùng Java code và gắn cứng (XML). Bất lợi ở chổ developers cần viết nhiều code hơn để bắt đầu. Một hạn chế khác là hiện thực của factory code thay đổi đáng kể nếu chúng ta đổi giựã lazy-initialization và eager-initialization hay từ singletons đến non-singletons. Tuy nhiên, nó có thể chứng tỏ rằng it is rare that we need to make these kinds of changes.
    Vấn đề chính là cho phép unit-testing là lập trình dùng interfaces để classes không dựa vào classes cụ thể trong ứng dụng. Để mock objects có thể tiêm vào một class khi test và thi hành tất cả sự phụ thuộc.
    Phụ thuộc đã được giải quyết, theo một cách nào đó trong ứng dụng. Một ý tưởng thường thấy phía sau tất cả IoC containers và các giải pháp cho DI là phụ thuộc được giải quyết theo cách dể hình dung – XML configuration files (Spring IoC), đặc biệt Java classes (Google Guice), hay factory classes cụ thể. Trong trường hợp này, chúng ta tránh sự lan rộng của phụ thuộc thông qua code ứng dụng và một ít class cụ thể có thể dễ dàng. Mock objects có thể dùng để unit-testing.
    Và cuối cùng, với IoC containers, chúng ta loại trừ phụ thuộc vào classes cụ thể và có thể tạo unit-testing, theo cách mà họ cung cấp – thông qua các thirty-party APIs hay cấu hình trên file XML. Khi không có một chuẩn cho IoC containers và mổi framework cung cấp các tính năng và chức năng khác nhau chung quanh vấn đề tiêm phụ thuộc, thì ta khó mà thay thế một IoC framework để dùng một cái khác.

    *GoF: Gang of Four. Người ta thường gọi quyển sách Design Patterns: Elements of Reusable Object-Oriented Software với cái tên này vì có 4 tác giả viết nó.

    Dịch từ TheServerSide

    Đọc tiếp >>

    Cao Trong Hien

    ,

    AOP xử dụng Spring Framework


    Trong khoảng 5 năm trở lại đây, một khuynh huớng lập trình mới xuất hiện. Nó được gọi là AOP để phân biệt với kiểu lập trình OOP đã có sẵn. Nguời viết muốn cung cấp cho bạn đọc một kiến thức cơ bản về kiểu lập trình mới mẻ này, và xử dụng springframework, một open-source phổ biến, để minh hoạ. Nếu bạn muốn tìm hiểu chi tiết về cách xử dụng springframework, thì bài viết này không nhằm mục đích đó, mà chỉ trình bày khái quát về những thuật ngữ và nguyên tắc trong việc xử dụng 1 chức năng phổ biết của spring: declarative transaction .

    Một điểm quan trọng nữa cần đuợc nêu ra, AOP đuợc xem là cái bổ sung cho OOP, chỗ mà OOP còn thiếu sót trong việc tạo những ứng dụng thuộc loại phức tạp. AOP khônng phải là cái thay thế OOP. Nguời lập trình bắt buộc phải quen thuộc với OOP để bắt đầu với AOP. Cũng vì lí do này mà AOP thuờng đuợc xem là dành cho các bạn đã có kinh nghiệm, chứ không dành cho các bạn mới bắt đầu làm quen với Java. Bạn có thể làm quen với OOP qua lí thuyết và sách vở từ truờng học. Và nếu bạn thấy vẫn còn lạc lối trong việc tìm kiếm những danh từ, tính từ, hay trả lời những câu hỏi ai, cái gì, làm gì... đại loại như vậy, trong việc tìm kiếm class, thì bạn có thể đọc lại những bài viết về OOP mà nguời viết đã trình bày truớc đây xem có giúp ích hơn không. Dù học OOP theo kiểu nào thì kết quả cuối cùng của OOP là chia ứng dụng thành nhiều phần nhỏ với những chức năng riêng biệt, theo kiểu các hộp đen (black box). Các hộp đen này làm cho việc tái xửng dụng và bao quản chúng đuợc dễ dàng hơn....và còn thêm nhiều ưu điểm khác nữa không kể ra ở đây.

    Lấy ví dụ về transaction trong việc truy cập database. Trong Java, truy cập database đòi hỏi nhiều buớc: tạo ra Connection, bắt đầu transation, commit, clean up connection... Để đơn giản hoá, ta hãy tạo ra 1 class hay black box chuyên làm việc này.

    public class DBTransaction {
    public DBTransaction () {
    //mã nguồn tạo ra Connection và bắt đầu 1 transaction
    }

    public Connection getConnection () {
    //mã nguồn return Connection đã tạo ra
    }

    public void commit () {
    //mã nguồn commit transaction và clean up resources
    }
    }

    Giả sử trong 1 ứng dụng, có 1 class (hay blackbox) XuatNhapHang làm chức năng xuất nhập hàng và nó chứa 1 method làm chức năng nhập hang.

    public void nhapHang ( int monHang, int soLuong )
    {
    //xử dụng blackbox để bắt đầu 1 transaction
    DBTransation tx = new DBTransactiơn ();

    // ...mã nguồn truy cập database để nhập hàng...

    tx.commit ();
    }

    Bạn dễ dàng hình dung đuợc mục đích xử dụng của blackbox này.
    Mỗi khi cần truy cập database, nguời xử dụng chỉ việc gọi các public methods của nó mà không cần biết tới bên trong nó hoạt động ra sao. Việc thay đổi bên trong của blackbox cũng không làm ảnh huởng tới nguời xử dụng nó. Đó là cái đẹp của việc tạo ra blackbox. Mọi nguời hình như đều vui vẻ và độc lập với công việc của mình. Thực tế không hoàn toàn như vậy. Giả sử ứng dụng sau này đòi hỏi phải theo dõi tất cả mọi hoạt động liên quan đến việc truy cập database. Mỗi khi database đuợc truy cập, tên nguời xử dụng và thời gian sẽ đuợc lưu trữ lại. Ta có thể tạo ra 1 blackbox đơn giản chuyên làm việc này.

    public class DBTruyCapTheoDoi {
    public DBTruyCapTheoDoi (Connection con, String tenNguoi) {
    //mã nguồn lưu lại trong database nguời xử dụng và thời gian
    .....
    }
    }

    Method nhapHang hay những nơi trong ứng dụng đã truy cập database đều phải sửa đổi như sau

    public void nhapHang ( int monHang, int soLuong )
    {
    //xử dụng blackbox để bắt đầu 1 transaction
    DBTransation tx = new DBTransactiơn ();
    //theo doi truy cap
    DBTruyCapTheoDoi (tx.getConnection (), tenNguoi);

    // ...mã nguồn truy cập database để nhập hàng...

    tx.commit ();
    }

    Sửa đổi không nhiều nhưng nó rộng khắp trong toàn ứng dụng. Đây chính là điểm làm cho nhiều nguời không hài lòng. Những nguời lập trình kì cựu đều đồng ý với nhau rằng, trong thực tế, đòi hỏi của ứng dụng luôn thay đổi theo thời gian. Cứ mỗi 1 yêu cầu mới của ứng dụng thì một (hay nhiều) blackbox đuợc tạo thành, và mã nguồn sẽ bị thay đổi rộng khắp trong ứnng dụng để xử dụng blackbox mới này. Thay đổi mã nguồn rộng khắp ứng dụng là điều tối kị vì nó đòi hỏi phải testing lại toàn bộ . Những bug xuất hiện lúc này thuờng rất khó tìm vì nguời lập trình thuờng cho rằng "mình sửa có 1 chút thì không sao..." .

    Câu hỏi đặt ra là làm sao tạo đuợc một kiểu lập trình uyển chuyển với sự phức tạp của ứng dụng . Mô hình này phải cho phép tạo dựng nhanh chóng ứng dụng khi nó ở trong giai đọan đầu đơn giản, và thay đổi nhanh chóng để thích ứng kịp thời với đòi hỏi mới . OOP cho phép tạo ra những blackbox và điều này vẫn đuợc xem là không thể thiếu trong việc lập trình . Bản thân những blackbox không phải là vấn đề, mà chính việc xử dụng chúng mới là vấn đề . Việc gọi trực tiếp những public method của 1 blackbox khi phải xử dụnng nó trong mã nguồn, tạm gọi là nối cứng (hard wired) blackbox vào mã nguồn . Có cách nào để xử dụng 1 blackbox mà khônng cần gọi trực tiếp nó trong mã nguồn hay không? Cần có 1 cách nào đó để gọi gián tiếp những public method, hay tạm gọi là nối mềm (soft wired) khi xử dụng những blackbox. Đây là chỗ OOP bỏ sót, và AOP ra đời để đáp ứng nhu cầu này .

    Đến đây bạn có thể thấy rằng, OOP là buớc đầu tiên cần phải có để tạo thành các blackbox. AOP là buớc kế tiếp để nối mềm những blackbox tạo thành 1 ứng dụng hoàn chỉnh . Việc nối mềm đòi hỏi nguời thiết kế phải có kinh nghiệm trong việc chia ứng dụng thành nhiều lớp (layer) để ứng dụng đuợc tạo ra và chạy 1 cách hữu hiệu . Điều này sẽ đuợc nói kĩ hơn sau đây. Một rắc rối nữa của việc nối mềm là nó thuờng đòi hỏi tạo nhiều cấu hình (configuration) phụ trợ .

    Làm thế nào để nối mềm 1 ứng dụng? Đây cũng chính là câu hỏi mà AOP cần phải trả lời . Java cho phép là đuợc việc này .

    Nối mềm là cho phép xử dụng 1 class (hay blackbox) mà không cần gọi trực tiếp public methods của nó trong mã nguồn, và bạn có thể ngạc nhiên nếu biết rằng điều này thực sự đã đuợc làm từ lâu . Nó đuợc làm bởi các container chẳng hạn như của EJB, portlet, servlet. Lấy EJB container làm ví dụ, vì nó rất gần với khái niệm AOP . Muốn xử dụng 1 EJB, bạn phải gọi create... chứ không bao giờ xửng dụng new . EJB trong thực tế chạy bên trong 1 container. Container trực tiếp tạo ra instance của EJB và chuyển cho nguời xử dụng khi method create... được gọi . Mỗi lần nguời xử dụng gọi method của EJB, container sẽ đón đầu (intercept) cú gọi này, rồi mới chuyển giao cho instance của EJB . Truớc khi chuyển giao cho EJB, thuờng container có thể sẽ làm nhiều việc khác nữa .... Container có thể bắt đầu 1 transaction, hay kiểm tra xem nguời gọi có đuợc phép gọi hay không, tùy theo cách cấu hình của EJB đó bên trong file ejb-jar.xml . Nếu xem EJB là 1 blackbox thì việc nối giữa EJB và nguời xử dụng nó mặc dù là gián tiếp nhưng chưa đuợc xem là nối mềm . Bản thân EJB container có thể chứa sẵn bên trong nó 1 blackbox làm công việc bắt đầu 1 transaction, hay 1 blackbox khác làm công việc kiểm tra mức độ cho phép nguời gọi . Việc nối kết giữa EJB và những blackbox bên trong của container mới chính xác là mềm ... Bản thân EJB hay nguời viết nó hoàn toàn không biết đến có tồn tại 1 blackbox có sẵn bên trong container làm công việc transaction . Nguời viết EJB chỉ việc thông tin với container qua file cấu hình ejb-jar.xml rằng phải bắt đầu 1 transaction hay làm thêm những chuyện khác mỗi khi method của nó đuợc gọi . Cũng dựa trên ý tuởng này, spring framework chính là 1 container thuộc loại nhẹ và nó có thể chứa đựng những Java object thông thuờng chứ không phức tạp như EJB .

    Trở lại ví dụ xuất nhập hàng ở trên, nếu class XuatNhapHang chạy bên trong spring framework thì method nhapHang ở trên có thể viết lại như sau

    public void nhapHang ( int monHang, int soLuong )
    {
    // ...mã nguồn truy cập database để nhập hàng...
    }

    Không hề có 1 đọan mã nguồn nào bên trong method nhapHang nói đến việc tạo ra transaction hay theo doi truy cap . Bản thân các blackbox transaction và theo dõi try cập cũng đuợc chạy trong spring. Nguời viết class XuatNhapHang chỉ việc truyền đạt cho spring, qua 1 file cấu hình, rằng mỗi khi method nhapHang đuợc gọi , hãy gọi blackbox transaction và theo dõi truy cập truớc khi chuyển nó đến nhapHang . Bằng cách nối mềm này, việc bao gồm hay loại bỏ những blackbox đuợc thực hiện qua việc thay đổi file cấu hình, chứ không cần phải thay đổi mã nguồn của class XuatNhapHang . Đây chính là lập luận mạnh nhứt trong việc cho ra đời AOP . Những nguời ủng hộ AOP trong ví dụ này lí luận rằng class XuatNhap hay method nhapHang chỉ nên lo lắng việc xuất nhập hàng . Việc bắt đầu 1 transaction hay theo dõi truy cập dữ liệu , đành rằng phải có do yêu cầu của ứng dụng, cũng không nên chen lấn 1 cách thô bạo vào trong mã nguồn của class XuatNhap, hay bất kì vào trong những blackbox nào khác khi chúng cũng cần truy cập dữ liệu . Có như vậy thì 1 blackbox mới giữ đuợc tính độc lập của nó với những thay đổi của yêu cầu của ứng dụng, dẫn đến việc tái xử dụng blackbox đuợc triệt để hơn .
    Những khía cạnh như transaction hay theo dõi truy cập, nếu cần phải 'chen lấn ' thì mã nguồn không phải là chỗ tốt để làm . Có những chỗ tốt hơn chẳng hạn như dùng file cấu hình, và spring framework làm đúng như vậy . Công việc của spring framework là đón đầu các cú gọi và thực hiện các chức năng thể hiện qua file cấu hình . Đây cũng chính là nguyên tắc cơ bản của AOP: thay các cú gọi trực tiếp, bằn các cú gọi qua file cấu hình ..

    Đọc đến đây, có thể bạn không mấy hào hứng với AOP . Thực tế các file cấu hình cho spring hay các AOP framework khác như AspectJ không đơn giản chút nào . Gọi trực tiếp method chỉ mất 1 dòng và cũng dễ theo dỏi logic của ứng dụng . Thay đổi file cấu hình liệu có an toàn hơn thay đổi mã nguồn hay không ? Những nguời ủng hộ AOP thì cho rằng nó rất nên làm . Những nguời còn hoài nghi thì luỡng lự . Các AOP framework khác nhau đều có cách tạo file cấu hình khác nhau. Không giống như EJB có thể chạy trong mọi EJB container, ứng dụng viết cho AOP framework này không chạy đuợc trong AOP framework khác . Những cố gắng để thống nhứt các AOP framework cho đến bây giờ vẫn chưa kết thúc . Việc container đón đầu các cú gọi có thể ảnh huởng đến tốc độ chạy của ứng dụng . Thêm nữa, không phải mọi class trong ứng dụng đều cần đuợc quản lí bởi AOP framework cũng như không phải mọi class đều trở thành EJB. Nếu ứng dụng thiết kết kém cỏi có thể dẫn đến số luợng class mà AOP container phải quản lí cùng với các file cấu hình tăng lên nhanh chóng . Có lẽ cũng chính vì những lí do này mà AOP vẫn chưa cất cánh nhanh chóng như OOP . Điểm khó khăn cuối cùng nữa của AOP là lí thuyết hoá nó để truyền đạt . AOP đưa ra những khái niệm lạ hoắc khó nắm bắt, dễ làm nản lòng những nguời mới bắt đầu . Phần kế tiếp nguời viết sẽ trình bày những khái niệm cơ bản của AOP, và nguyên tắc Inversion of Control hay còn gọi là Dependency Injection của springframework trong việc triển khai AOP .

    Khái niệm đầu tiên thuờng gặp trong AOP là concern (1 mối lo âu) hay aspect ( 1 khía cạnh của vấn đề). Concern hay aspect trong AOP chỉ là 1, và nó tuơng đuơng với chức năng của 1 blackbox trong OOP. Lấy ví dụ trong ứng dụng cho 1 cửa hàng mua bán . Những yêu cầu của ứng dụng bao gồm : chi thu tài chính, xuất nhập hàng, kết toán cuối tháng, cuối năm, quản lí nhân viên ... Và có thể thay đổi trong tuơng lai theo yêu cầu của thực tế . Việc chia nhỏ 1 yêu cầu cùng với OOP sẽ đưa đến các blackbox như xuất nhập hàng, transaction, hay theo dõi truy cập như ở trên . Chức năng của mỗi blackbox có thể đuợc xem là 1 khía cạnh của ứng dụng mà nguời thiết kế phải lo lắng . Tới đây bạn có thể thấy vì sao 2 chữ concern và aspect mang nghĩa không mấy ăn nhập với nhau lại đuợc dùng để chỉ chung 1 khái niệm . Có những khía cạnh, như transaction, cần phải 'chen lấn ' vào các khía cạnh khác . Những khía cạnh đó đuợc gọi là crosscutting aspect, tạm gọi là những khía cạnh cắt ngang . Trong thực tế, điểm chen lấn của những crosscutting aspect không tùy tiện mà nó thuờng là các điểm đặc biệt . Ví dụ như khía cạnh trasaction chỉ chen vào khi bắt đầu method nhapHang để khởi tạo 1 transaction và lại chen vào 1 lần nữa khi method nhapHang return để commit transaction. Những điểm đặc biệt có thể kể ra là bắt đầu hay kết thúc 1 method, khi xảy ra exception. Spring chỉ cho phép chen vào các điểm đặc biệt, còn AspectJ thì cho phép hầu như vào bất cứ điểm nào trong mã nguồn . Việc chen lấn tuỳ tiện cần được cân nhắc kĩ lưỡng và thuờng đuợc khuyên nên tránh . Điểm chen lấn đuợc gọi là joinpoint (điểm nối ) . Tập họp các điểm nối đuợc gọi là pointcut (cắt điểm). Bản thân blackbox XuatNhapHang bị chen lấn nên nó đuợc gọi là target object . Bản thân blackbox transaction, triển khai transaction aspect, làm công việc chen lấn, đuợc gọi là advice . Vì có nhiều điểm chen lấn đặc biệt kể trên, nên có nhiều loại advice.
    _ Around advice: là loại chen vào truớc khi cú gọi đuợc chuyển tới method và sau khi method thực hiện xong. Transaction advice chính là loại này
    _ Before advice: chen vào truớc khi cú gọi đuợc chuyển tới method
    _ Throws advice: chen vào khi bản thân method thows excpetion
    _ After returning advice: chen vào sau khi method thực hiện và không có exception .

    Ta có thể hình dung như sau: mỗi khi ứng dụng gặp điểm 'chen lấn ', container sẽ đón đầu cú gọi và chạy mã nguồn của advice cho điểm 'chen lấn ' đó rồi mới chuyển giao cho method .
    Tới đây có lẽ khá đủ cho nguời mới làm quen với AOP. Để thực sự có thể lập trình theo kiểu AOP, bạn cần phải thông thạo với 1 AOP framework như spring hay aspectJ, đặc biệt là cách tạo file cấu hình cho mỗi framework.

    Trở lại spring framework, nó đuợc đề cập ở đây vì 1 chức năng phổ biến : declarative transaction, giống như trong EJB, nhưng đơn giản và dễ xử dụng hơn nhiều. Để xử dụng chức năng này, chỉ cần khái niệm của AOP ở trên là đủ . Khi xử dụng spring, 1 khái niệm luôn gặp phải là Inversion of Control (IoC) hay còn gọi là Dependency Injection, nó sẽ đuợc nói rõ duới đây .

    IoC (tạm dịch là đảo nguợc kiểm soát) thuờng được thực hiện bởi các loại container như servlet, portlet, hay EJB. Lấy ví dụ EJB container, nguời xử dụng không trực tiếp tạo ra instance của EJB, mà container tạo ra nó và chuyển giao nguời xử dụng khi cần tới . Khi nào instance đuợc tạo ra nằm ngoài sự kiểm soát của nguời xử dụng . Container có thể tạo instance ra truớc và chờ đến khi nguời xử dụng, hoặc tạo ra ngay vào lúc đuợc cần tới . Tên gọi IoC cũng nhằm chỉ lí do này: container giành sự kiểm soát từ nguời xử dụng (trong lập trình thông thuờng nguời xử dụng giành kiểm soát bằng cách gọi new để tạo ra instance). Khi instance của EJB đuợc tạo ra và trong truờng hợp session bean, container luôn gọi method setSessionContext(SessionContext sc) để cho EJB xử dụng SessionContext của nó . Tuơng tự trong servlet container, khi instance của servlet đuợc tạo ra, container luôn gọi method init(ServletConfig sc) để servlet xử dụng . Trong cả 2 truờng hợp, container đuợc xem là nạp (injection) cho instance cái mà nó cần . Cái đuợc cần như SessionContext hay ServletConfig gọi là dependency . Tên gọi Dependency Injection cũng từ đây mà ra. Có 1 điểm bất đồng giữa cách gọi tên Dependency Injection và định nghĩa về mối quan hệ giữa class trong UML . Dựa trên tài liệu "Mastering UML with Rational Rose" thì instance của EJB xử dụng SessionContext nên instance mới chính là dependency phụ thuộc vào SessionContext . Nếu bạn giải thích đuợc tại sao có sự bất đồng này thì làm ơn cho mọi nguời cùng biết . Dù sao thì chúng ta không nên mất quá nhiều thời giờ cho vấn đề định nghĩa và tên gọi ở đây .

    Muốn xử dụng spring, ít nhiều bạn phải làm quen với spring container . Class tiêu biểu cho spring container là ApplicationContext . Nguời xử dụng phải trực tiếp tạo ra instance của spring container truớc khi có thể xử dụng những object mà nó chứa . Có nhiều cách tạo ra spring container. Cách thông thuờng nhứt là (những ví dụ theo sau đuợc dựa trên tài liệu spring-reference.pdf vversion 1.2.8)

    ApplicationContext ac = new ClassPathXmlApplicationContext( new String[] {"applicationContext.xml", "applicationContext-part2.xml"});

    Spring container tạo ra theo cách này sẽ đọc những file config duới dạng xml để load tất cả các Java ojbect mà nó cần quản lí .
    Như ví dụ sau
    <bean id="exampleBean" class="examples.ExampleBean"/>
    <bean name="anotherExample" class="examples.ExampleBeanTwo"/>

    ExampleBean và ExampleBeanTwo có thể là những java object thông thuờng chứ không có gì đặc biệt . Muốn truy cập chúng qua container rất dễ dàng

    ExampleBean eb = (ExampleBean)ac.getBean("exampleBean");
    ExampleBeanTwo eb2 = (ExampleBeanTwo)ac.getBean("anotherExample");

    Theo mặc định thì container sẽ tạo ra singleton instance cho mỗi bean, có nghĩa là chỉ 1 instance của 1 bean đuợc tái xử dụng cho những lần gọi sau . Ta có thể xử dụng Dependency Injection của container như sau

    <bean id="exampleBean" class="examples.ExampleBean">
    <property name="beanOne"><ref bean="anotherExampleBean"/></property>
    <property name="beanTwo"><ref bean="yetAnotherBean"/></property>
    <property name="integerProperty"><value>1</value></property>
    </bean>
    <bean id="anotherExampleBean" class="examples.AnotherBean"/>
    <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

    public class ExampleBean {
    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;

    public void setBeanOne(AnotherBean beanOne) {
    this.beanOne = beanOne;
    }
    public void setBeanTwo(YetAnotherBean beanTwo) {
    this.beanTwo = beanTwo;
    }
    public void setIntegerProperty(int i) {
    this.i = i;
    }
    }

    Bản thân ExampleBean phải chứa những setter method để container gọi ngay sau khi tạo ra chúng và truớc khi chuyển cho nguời xử dụng .

    Nguyên tắc xử dụng spring container chỉ đơn giản như vậy thôi . Nó cho phép định nghĩa qua file cấu hình những logic phức tạp hơn mà bài viết không trình bày hết ra đây . Tới đây bạn có thể thắc mắc như vậy thì sức mạnh của spring nằm ở đâu ? Tiện lợi chỉ đơn giản như vậy thì có đáng đuợc xử dụng không ?

    Cần nhắc lại 1 nguyên tắc làm việc của mọi dạng container là đón đầu những cú gọi . Spring làm việc này bằng cách dựa trên file cấu hình để tạo ra những object phụ trợ khác vào lúc runtime . Cái return từ method getBean đuợc xem là 1 proxy đuợc tạo ra vào lúc runtime của java object mà spring quản lí . Đằng sau proxy này là 1 tập họp các phụ trợ object thực hiện các chức năng theo yêu cầu của file cấu hình truớc khi chuyển cú gọi tới cho java object . Chẳng hạn như các phụ trợ object có thể chạy mã nguồn của advice, hoặc bắt đầu 1 transaction, một chức năng đã đuợc làm sẵn trong spring, sẽ đuợc nói tới duới đây .

    Trong việc truy cập database, ứng dụng có thể xử dụng các phuơng pháp khác nhau như: trực tiếp xử dụng SQL, Hybernate, EJB Entity ... Để đơn giản hoá vấn đề, ta lấy truờng xử dụng SQL để cập nhựt database .

    Trong Java, connection thường tạo ra duới dạng connection pool hay DataSource, là 1 tập họp những connection để xử dụng chung cho toàn bộ ứng dụng . Giả sử apache datasource đuợc xử dụng ở đây . Để container có thể tạo ra datasource cho oracle, ta xử dụng cấu hình duới đây

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    <property name="url" value="jdbc:oracle:thin:@//localhost:1521"/>
    <property name="username" value="scott"/>
    <property name="password" value="tiger"/>
    </bean>

    Nếu datasource đã đuợc tạo ra xử dụng JNDI, ta có thể dùng cấu hình

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/jpetstore"/>>
    </bean>

    Bản thân datasource sẽ đuợc xử dụng bởi 1 transaction manager, 1 object quản lí transaction có sẵn trong spring như sau:

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    </bean>

    Trở lại class XuatNhapHang ở trên, muốn spring tự động bắt đầu và kết thúc 1 transaction mỗi khi 1 method của class này đuợc gọi , cần có những cấu hình cho pointcut hay advice khá phức tạp theo kiểu AOP . Rất may là spring đã gói gọn trong 1 class TransactionProxyFactoryBean đơn giản sau

    <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="txManager"/>
    <property name="transactionAttributes">
    <props>
    <prop key="*">PROPAGATION_REQUIRED, -MyException</prop>
    </props>
    </property>
    </bean>

    Chúng ta sẽ kiểm tra kĩ lưỡng cấu hình này .
    _ abstract="true" có nghĩa là ứng dụng không đuợc phép gọi method getBean("txProxyTemplate") . Cấu hình này chỉ đuợc dùng để các cấu hình khác extends . Giống như tính chất inheritance trong OOP
    _ property name="transactionManager" ref="txManager" cho biết class TransactionProxyFactoryBean sẽ đuợc nạp bean txManager bởi container.
    _ prop key="*" : áp dụng cho mọi method của những class extends cấu hình này
    _ PROPAGATION_REQUIRED: bắt đầu 1 transaction nếu chưa có hoặc tiếp tục 1 transaction có sẵn .
    _ -MyException: nếu method throw MyException, thì dấu - đằng truớc sẽ làm transaction bị rollback, và dấu + sẽ commit transaction truớc khi throw MyException.
    Cấu hình của class XuatNhapHang sẽ extends cấu hình này như sau:

    <bean id="xuatNhapBean" parent="txProxyTemplate">
    <property name="target">
    <bean class="XuatNhapHang"></bean>
    </property>
    </bean>

    Ứng dụng có thể xử dụng class XuatNhapHang như sau:

    XuatNhapHang xnh = (XuatNhapHang)ac.getBean("xuatNhapBean");
    xnh.nhapHang(...);

    Bằng cách extends cấu hình của txProxyTemplate qua việc sử dụng attribute parent, mỗi khi 1 method của class XuatNhapHang đuợc gọi, 1 transaction sẽ tự động bắt đầu và kết thúc . Transaction sẽ bị rollback trong truờng hợp exception MyException xảy ra . Spring còn giúp mô hình JDBC operations như những java object qua việc xử dụng class MappingSqlQuery, SqlUpdate, StoredProcedure .

    Bài viết đuợc kết thúc ở đây chỉ với mục đích giúp bạn làm quen với những khái niệm của AOP । Vì AOP là cái bổ sung cho những thiếu xót của OOP, AOP không dành cho những bạn mới bắt đầu lập trình . Spring framework mặc dù triển khai AOP đơn giản hơn so với AspectJ, cũng khác phức tạp với rất nhiều chức năng có thể dùng đuợc, và nguời viết chỉ tóm tắt 1 chức năng phổ biến nhứt của nó là declarative transaction. Dù sao thì bạn vẫn cần tham khảo những tài liệu huớng dẫn về spring truớc khi có thể dùng nó 1 cách hữu hiệu . Chúc bạn may mắn .

    Theo(Javavietnam)


    Đọc tiếp >>

    Cao Trong Hien

    ,

    Dùng Dojo và JSON xây dựng Ajax Applications


    "Ajax" đã là một từ khá thông dụng và ngày càng hữu dụng. GmailGoogle Suggest là các trang viết và giới thiệu thật hoành tráng cho JavaScript bất đồng bộ (asynchronous). Lợi ích của người dùng và những yêu cầu càng tăng của họ.

    Trong bài viết này, Tôi sẽ chỉ cách xây dựng một ứng dụng dùng DojoJSON—là 2 phần khác nhau nhưng bổ xung cho nhau thành một ứng dụng hoàn chỉnh। Chúng ta sẽ hiểu hơn về Dojo và JSON và xây dựng một Ajax applications. Bài viết này cũng sẽ cho bạn biết cách tạo một Dojo widgets bằng cách hiện thực tình năng "autocomplete" cho một text boxes. download



    Đặt vấn đề


    Ajax hiểu theo một cách ngắn gọn là xử lý trao đổi dữ liệu giữa web server và web browser dùng JavaScript khi trang đã được load. Trên thực tế, nó có nghĩa là chuyển đổi request trên browser, xử lý bất đồng bộ JavaScript, vậy xử lý request/response một cách trực tiếp đến user. Để hoàn thành nó, chúng ta có thể viết các đoạn code JavaScript hay dùng một libraries như Dojo.

    Dojo

    Dojo bao gồm nhiều chức năng đã được viết sẵn bằng JavaScript. Một trong những tính năng đó là tạo gởi HTTP requests và nhận responses. Và đây cũng là tính năng chính mà ta sẽ dùng. Về chức năng nó cung cấp về Ajax, Dojo cung cấp gói cho thao tác trên string, thao tác trên DOM, hỗ trợ kéo thả, và một số cấu trúc quen thuôc như lists, queues, và stacks.

    JSON

    JSON là một thư viện(library) Java giúp ta chuyển một đối tượng(object) Java sang biểu diễn dạng chuổi(string). Chuổi này khi eval()gọi trên JavaScript, nó sẽ tạo thành một mảng (array) chứa các thông tin về Java object. JSON là một kiểu đối tượng ghi chú, phù hợp cho mã hoá nhiều cấu trúc của đối tượng. Theo cách này ta sẽ tạo ra được một cấu trúc nhỏ hơn một file XML về thành phần, tiện lợi hơn khi dùng tính năng eval(), là một sự lựa chọn cho việc chuyển dữ liệu giữa browser và server.

    JSON được xây dựng dùng cùng với JavaScript để tạo requests. Code ở server, có thể viết trên nhiều ngôn ngữ khác nhau, như C#, Python, PHP, và đương nhiên, Java!


    Một HTTP Request đơn giản


    Trong ví dụ đầu tiên, tôi cùng Dojo để cung cấp dòng chào mừng khi người dùng click lên trên một button. Minh hoạ cho việc tạo và gọi một Ajax dùng Dojo. Vào http://localhost:8080/dojo_json/example1.jsp và nhập tên, Submit. Một JavaScript thông báo hiển thị dòng chào mừng được tạo trên server. Bạn có thể xem source trên cho example1.jsp


    <html>
    
    <body onLoad="onLoad();">
    <head>
    <title>Example 1</title>
    <script language="javascript" src="js/dojo.js"></script>
    <script language="javascript">
    dojo.require("dojo.io.*");
    dojo.require("dojo.event.*");

    function onLoad() {
    var buttonObj = document.getElementById("myButton");
    dojo.event.connect(buttonObj, "onclick",
    this, "onclick_myButton");
    }

    function onclick_myButton() {
    var bindArgs = {
    url: "welcome.jsp",
    error: function(type, data, evt){
    alert("An error occurred.");
    },
    load: function(type, data, evt){
    alert(data);
    },
    mimetype: "text/plain",
    formNode: document.getElementById("myForm")
    };
    dojo.io.bind(bindArgs);
    }
    </script>
    </head>
    <body>
    <form id="myForm">
    <input type="text" name="name"/>
    <input type="button" id="myButton" value="Submit"/>
    </form>
    </body>
    </html>

    Đầu tiên là import Dojo libraries, tên là dojo.js. Chứa tất cả class của Dojo cần cho phần lớn tính năng của nó. Dojo libraries được đóng gói như một Java code. Ví dụ, tôi cần import 2 gói. Gói dojo.io chứa các class cho phép tạo một HTTP requests dùng các giao thức như XMLHTTPTransport. Gói dojo.event cung cấp tập lệnh thao tác trên DOM và gán sự kiện.

    Tiếp theo, tôi gán onclick cho button. Điều mà trước đây ta phải làm như sau:

    <input type="submit" onclick="someFunc();"/>

    nhưng với đoạn này thì ta chỉ gán cho sự kiện onclick một sử lý nhất định. Phương thức dojo.event.connect() cho phép chúng ta liên kết một xử lý cho nhiều HTML elements. Ở đây, tôi gán onclick_myButton là một xử lý cho myButton.

    Khi onclick_myButton executes, tôi gọi đến welcome.jsp và nhận lại response. Dùng phương thức dojo.io.bind(). trong đó có đối số bindArgs, là một mảng name/value. Trong ví dụ trên tôi dùng 5 cặp:

    1. url: URL để gởi request đến.

    2. mimetype: Kiểu của response.

    3. load: Code gọi khi hoàn tất.

    4. error: Code gọi khi lỗi.

    5. formNode: ID của form nơi mà fields truyền tham số đến URL.

    Tất cả các tham số bạn có thể tìm thấy ở đây.

    Khi gọi dojo.io.bind(bindArgs), là bắt đầu quá trình xử lý, nó phụ thuộc vào request mà ta có thể nhận về errors,load hay error được gọi. Cả loaderror đều có 3 tham số:

    1. type: kiểu chức năng; luôn luôn cho loadload()errorerror().

    2. data: response nhận về. nếu mimetype thì khai báo như text/plain. Nếu text/json được dùng, dữ liệu sẽ có dạng eval('(' + dữ liệu nhận về + ')').

    3. evt: Sự kiện của đối tượng.

    Tiếp theo, tôi có thể khai báo một sự kiện dùng dojo.event.connect() bằng cách tạo một chức năng handleResponse(type,data,evt) và kết hợp với request:

    var req = dojo.io.bind(bindArgs); dojo.event.connect(req, "load", this, "handleResponse");

    Trường hợp này, tôi không cần khai báo thuộc tính load trong bindArgs.

    Chú ý: Vì lý do bảo mật, Firefox không cho phép giá trị cho url trong bindArgs liên kết đến một URL trên một domain khác. Internet Explorer thì cho phép.


    Dùng JSON chuyển đối tượng Java


    Như đã nói ở trên, JSON giúp chuyển Java objects thành chuổi eval()của JavaScript. Trong ví dụ này, người dùng sẽ nhìn thấy danh sách các quayển sách. Khi người dùng rê chuột qua tên sách, chúng ta sẽ tìm và hiển thị tất cả những thông tin về quyển sách đó dùng Ajax. Ví dụ 2, http://localhost:8080/dojo_json/example2.jsp, minh họa cho điều này.

    Tạo và Cung cấp Java Objects

    Class Book có 4 trường, BookManager có phương thức trả về một object Book object tuỳ thuộc vào bookId và một phương thức trả về tất cả sách có. Trong ví dụ này, tôi đã code cứng 5 quyển sách trong BookManager.

    Phương thức Book.toJSONString() chịu trách nhiệm chuyển object Book thành JavaScript eval()thành String dùng trong JSON API.

    public String toJSONString() throws JSONException {
    
    JSONObject jsonObj = new JSONObject();
    jsonObj.put("bookId", new Integer(this.bookId));
    jsonObj.put("title", this.title);
    jsonObj.put("isbn", this.isbn);
    jsonObj.put("author", this.author);
    return jsonObj.toString();
    }

    Class JSONObject chuyển object Book thành string bên trong dấu ngoặc, với dấu 2 chấm giữ tên và giá trị, và dấu ngoặc kép giựã giá trị và tên của trường khác, phù hợp String với eval()JavaScript. Còn class JSONArray có thể chuyển một danh sách Java objects sang JavaScript arrays.

    Chuyển và điều hướng JSON-Encoded Data

    Phụ thuộc vào bookId, book.jsp trả về một Book object trên form của JSON. Trang JSP tìm kiếm bookId dùng phương thức BookManager.getBook(bookId) và in ra kết quả qua phương thức Book.toJSONString(). Vì vậy http://localhost:8080/dojo_json/book.jsp?bookId=1 sẽ in ra JSON string cho Book với giá trị bookId1.

    Trở lại tầng client, thông tin book được hiển thị trên list. Mổi thành phần trên danh sách có mouseover (trMouseOver) và mouseout (trMouseOut) sự kiện được truyền vào . Mỗi lần sự kiên mouseover, ta gọi book.jsp và truyền vào bookId. Kết quả trả về là một JSON String của quyển sách này Book. Tôi khai báo mime-type như một text/json vì vậy, giá trị trả về sẽ được tự động gọi eval(). Nhìn vào đoạn code JavaScript chịu trách nhiệm gởi request và xử lý response:

    function trMouseOver(bookId) {
    
    getBookInfo(bookId);
    }

    function trMouseOut(evt) {
    var bookDiv = document.getElementById("bookInfo");
    bookDiv.style.display = "none";
    }

    function getBookInfo(bookId) {
    var params = new Array();
    params['bookId'] = bookId;
    var bindArgs = {
    url: "book.jsp",
    error: function(type, data, evt){
    alert("error");
    },
    mimetype: "text/json",
    content: params
    };
    var req = dojo.io.bind(bindArgs);
    dojo.event.connect(req, "load", this, "populateDiv");
    }

    function populateDiv(type, data, evt) {
    var bookDiv = document.getElementById("bookInfo");
    if (!data) {
    bookDiv.style.display = "none";
    } else {
    bookDiv.innerHTML = "ISBN: " + data.isbn +
    "
    Author: " + data.author;
    bookDiv.style.display = "";
    }
    }

    Trong một số ví dụ khác dùng thuộc tính content của bindArgs thay cho formNode. Thuộc tính content là một key/value liên kết đến thuộc tính được khai báo mapping of properties to be constructed into parameters passed with the data request.

    Khi dữ liệu truyền vào JavaScript object, tôi có thể dể dàng truy cập đến các thuộc tính của Book và hiển thị đến người dùng trong

    element, như bạn thấy.

    Mở rộng Dojo: Tạo Autocompletion Widget


    Trong 2 ví dụ trên, ta đã tìm hiểu các thành phần chính của một ứng dụng Ajax: chuyển đổi dữ liệu với web server dùng JavaScript. Một cách hiểu đơn giản, nó mang đến cho developer sử dụng một cách hiệu quả UIs dùng Ajax tools như Dojo. Dojo là một framework có thể mở rộng cho widget và phát triển module sẽ dễ dàng hơn. Trong sự lựa chọn này, tôi sẽ tạo một widget cho phép người dùng tạo một autocompletion cho một trường trên form.

    Dojo Widget

    Nói theo cách đơn giản, widget là tên của UI element như button, text box, scroll bar, v.v.v... Khi tạo Dojo widget, ngoài những thẻ HTML chúng ta còn có thể tạo một widgets bao gồm nhiều thành phần input, với của ta styles, và nhiều thứ khác. Bạn cần định nghĩa một trình điều khiển sự kiện cho widgets, tạo hành vi, và có thể dùng lại khi tạo một widget khác. Trong ví dụ này, tôi sẽ tạo một widget cung cấp tính năng autocompletion cho text box của HTML form.

    Thành phần của Widget

    Khi phát triển widgets, chúng ta cần quyết định UI elements nào và làm như thế nào. Khi dùng Dojo, có nghĩa là dùng HTML elements và viết code JavaScript để định nghĩa ứng sử. Ví dụ autocomplete dùng 4 files:

    • dojo.js: Tất cả Dojo libraries.

    • js/dojo/utils/templates/AutoComplete.html: Chứa UI elements, bao gồm HTML tags như

      <input>. File này có thể chứa nhiều đoạn HTML và giới hạn bởi một root element. Nếu nhiều hơn một element tìm thấy trên cùng, thì element đầu tiên sẽ là root.

    • js/dojo/utils/AutoComplete.js: JavaScript code cho widget. Thường là class mở rộng của một trong các Dojo Widget classes.

    • example3.jsp: File mà widget được dùng. Thường là thẻ được định nghĩa trong widget.

    Học và hiểu làm sao mà UI elements có thể quản lý và thao tác bằng JavaScript là điều quan trọng trong tạo widgets.

    Quản lý UI Elements

    Như đã nói trên, chỉ định element gốc. Trong ví dụ autocomplete, element gốc là một

    . Element này có thể quản lý torng class AutoComplete dùng this.domNode. Biến this.domNode lưu một liên kết đấn đối tượng HTML, xem ví dụ sau:

    this.domNode.style.display = "none";

    Để quán lý các element của UI từ JavaScript, bạn có thể dùng this.domNode cho đến khi bạn tìm ra , hay bạn có thể dùng thuộc tính dojoAttachPoint.

    <input type="text" name="someTextField" dojoAttachPoint="myTextField"/>

    Nếu bạn đã định nghĩa trong file HTML, nó có thể dễ dàng quản lý dùng this.myTextField trong JavaScript. Giống như this.domNode, this.myTextField cũng liên kết đến một đối tượng HTML.

    Với Dojo, bạn có thể dễ dàng gắn nhiều sự kiện cho UI elements, kể cả dùng JavaScript và trường của element. Như đã minh hoạ, thật dễ dàng gắn sự kiên cho element, tôi dùng dojo.event.connect():

    dojo.event.connect(this.domNode, "onclick", this, "myFunction")

    Đoạn trên sẽ gắn onclick sự kiện myFunction() từ class này đến element gốc của UI. Tiếp theo, tôi có thể chỉ định HTML code cho thẻ

    <div>:

    <div dojoAttachEvent="onclick: myFunction">
    
    <div dojoAttachEvent="onclick, onmouseover: myFunction">

    Lập trình Widget

    Giờ đã có cách quản lý và điểu khiển UI và các kiểu hành động của người dùng, và sau đó viết code JavaScript để tạo một hành vi ứng sử như ta mong muốn. Mỗi widget tương ứng với một class để quản lý UI elements và chịu trách nhiệm phản ứng với actions người dùng. Đoạn code Javascript sau được trích ra từ class AutoComplete sẽ minh hoạ cho một bộ widget.

    dojo.provide("utils.AutoComplete");  dojo.require("dojo.dom"); dojo.provide("utils.AutoComplete");
    

    dojo.require("dojo.dom");
    ...

    dojo.widget.tags.addParseTreeHandler("dojo:AutoComplete");

    utils.AutoComplete = function() {

    // call super constructor
    dojo.widget.HtmlWidget.call(this);

    // load template
    this.templatePath =
    dojo.uri.dojoUri("utils/templates/AutoComplete.html");

    this.widgetType = "AutoComplete";

    // Instance variables
    this.action = "";
    this.formId = "";
    this.form = {};
    ...

    this.postCreate = function() {
    this.form = document.getElementById(this.formId);
    this.textbox = document.getElementById(this.textboxId);
    dojo.event.connect(this.textbox, "onkeyup",
    this, "textboxOnKeyUp");
    ...
    }

    this.textboxOnKeyUp = function(evt) {
    if (this.isKey(evt.keyCode, 38, 40)) {
    this.checkUpDownArrows(evt.keyCode);
    } else {
    bindArgs = {
    url: this.action,
    mimetype: "text/javascript",
    formNode: this.form
    };
    var req = dojo.io.bind(bindArgs);
    dojo.event.connect(req, "load", this, "populateChoices");
    }
    }

    // Handler for "load" action of dojo.io.bind() call.
    this.populateChoices = function(type, data, evt) {
    ...
    }
    }

    // define inheritance
    dj.inherits(utils.AutoComplete, dojo.widget.HtmlWidget);

    Tôi đã định nghĩa một class JavaScript, AutoComplete, đoạn code ứng sử cho widget. Khi định nghĩa một widget, Dojo yêu cầu người phát triển khai báo một class dùng chức năng dojo.provide(). Đoạn inheritance này cho biết dùng chức năng dj.inherits(), và thực hiện thông qua việc gọi dojo.widget.HtmlWidget.call(this). File mẫu cho widget được load bằng cách gán giá trị cho biến this.templatePath cho class cha.

    Dojo widgets dùng trang HTML dùng custom tags. Tags có thể định nghĩa khi viết một class liên kết. Trong ví dụ trên, bộ thể xuất ra là dojo:AutoComplete, được dùng trong file HTML trong đoạn code sau:

      action="getBooks.jsp"  textboxId="bookName"  formId="bookForm"/>

    Đoạn output chứa đoạn template AutoComplete.html trên browser. Khai báo thuộc tính trong thẻ có thể quản lý dùng this. trong class JavaScript.

    Khi người dùng nhập, nó sẽ tự động tìm ra danh sách những giá trị hợp lệ. Trong khi nhập, textboxOnKeyUp được gọi, và truyền đến web server cùng với giá trị nhập. Web server trả về một danh sách giá trị để hiển thị cho người dùng chọn. Tôi hiển thị nó trên thẻ <div>

    .

    Các đoạn code được tôi lược bớt là các đoạn thao tác trên thẻ <div>

    , choices. Tìm ra phím tác động của người dùng, như Tab để chuyển sang trường khác để thao tác.

    Kết luận


    Dojo là một lựa chọn tốt khi phát triển Ajax trên một ứng dụng đòi hỏi JavaScript-intensive। Tuy nhiên, còn một số sản phẩm, như Scriptaculous và AjaxAnywhere, đem đến nhiều chức năng tuỳ theo yêu cầu của ứng dụng.

    Hiendt (Java.net)



    Đọc tiếp >>

    Cao Trong Hien

    ,

    Frameworks nào sẽ thay thế Struts

    Apache Struts là một Java web application framework được đánh giá là dùng nhiều nhất( cho đến nay). Tuy nhiên, sự phát triển đến hôm nay thì nó đã quá lỗi thời. Có một vài lựa chọn cho sự thay đổi, nhưng cuộc tranh chấp này vẫn chưa ngả ngũ! Bạn thì cho rằng cái nào xứng đáng? Bạn sẽ dùng framework nào nếu bạn làm một Java web application?

    Rich interfaces, AJAX hay RIA, cái nào sẽ chiến thắng và trở thành công nghệ web được dùng nhiều nhất. Dĩ nhiên, Java là sự lựa chọn ở mặt server. Một số kết hợp giữa AJAX với Java Server Faces (JSF), một generation web framework mới của Java. Một số thì dùng thư viện Java remoting, nguồn mở và thương mại, để gọi JavaScript và ActionScript từ server xuống.



    Ta có thể dùng một trong các frameworks như Apache Shale kết hợp với JSF. Hay ta sẽ dùng JavaScript gọi đến Servlets ở tầng web application. Hay dùng một framework cho tất cả - như JBoss Seam.



    Về phía Sun thì có giới thiệu 3 công nghệ, và họ cũng chẳng có một lời khuyên nào cho ta. . Hãy xem với tôi nhé:

    1. JSF - server side component dựa trên Servlets và JSF model

    2. Swing – Dựa trên giải pháp VM trên web. Đã được dùng hơn 10 năm và giờ thì đã lạc hậu. Như Swing Application Framework được dùng như một vị cứu tinh cho Swing, tôi cũng không nghĩ rằng nó sẽ bị bỏ rơi.

    3. JavaFX – Đây là một sự lựa chọn mới. Và có thể là một câu trả lời cho rich web frameworks dùng cho mobile, web hay những interface khác. Thời gian sẽ cho ta biết cái nào sẽ thích hợp với nó. Theo tôi thì Sun thích developers phải làm nhiều điều hơn.

    Chúng ta đã có những thông tin cần thiết hay những quan điểm khó hiều từ phía Sun, web application nào sẽ được lựa chọn. Theo tôi thì Swing là cái đầu tiên bị loại (công nghệ có thể tốt, nhưng có quá nhiều đều phải làm khi có sự thay đổi) và JavaFX mới chỉ là một lời hứa về kịch bản và có thể chỉ hiện thực sau một khoảng thời gian dài. Vì vậy, chỉ còn lại JSF. JSF cũng có một số vấn đề, nhưng hữu dụng khi dùng cho với các frameworks như Seam và kể cả các AJAX components như ICEfaces có thể là một sự lựa chọn tốt. Xa hơn, với các frameworks giống như Seam, bạn sẽ không cực khổ khi dùng JSF với các JavaScript applications (như DojoToolkit) hay Adobe Flex cho tầng server side.



    Trước khi giải thích vì sao tôi dùng Seam, còn có 2 lựa chọn – là web framework của Spring và Google Web Toolkit. Spring là một sự lựa chọn tốt nếu bạn đã từng dùng, và mọi người trong team của bạn đã biết về spring framework. Tuy nhiên, Spring lệnh so với chuẩn JavaEE và tôi cũng đề nghị là nên gắn thành chuẩn. Tuyệt vời khi Seam có thể làm việc với Spring và Spring beans có thể dùng với Seam components. Google Web Toolkit dịch code sang JavaScript, và phù hợp với sự lựa chọn. Tuy nhiên, nó chưa phải là một web framework hoàn chỉnh, ta cũng có thể kết hợp với Seam. Đó cũng là lý do mà Seam là một framework tốt.

    Nhưng đó chỉ là một ý của riêng tôi, còn bạn thì sao? hãy cho tôi biết trong comment.

    Đọc tiếp >>

    Cao Trong Hien

    ,

    Tôi học JAAS

    Kỳ 1: THẤT BẠI LÀ MẸ THÀNH CÔNG

    Bước đầu đối với ai cũng thật sự tệ, và tôi cũng không ngoại lệ. Đầu tiên cũng như bao lần học những công nghệ khác, tôi vào Google. Kết quả: tôi tìm ra một tài liệu Security for JavaSE6. Tôi mày mò trong mục tutorial, tìm cách run. Kết quả: Thất bại, vì lý do tôi không biết cách run. Trước giờ tôi chỉ sài eclipse, chạy command line đối với tôi thật khó khăn, và cũng vì ngày sưa tôi quá coi thường nó (thời nay ai mà còn dùng command line).
    Tài liệu thứ 2 mà tôi tìm thây là bài JAAS Security in Action trên devx.com. Cũng chỉ nói về hệ tư tửng, những định nghĩ, vv... Mà không chạy được làm sao mà hiểu được định nghĩa. Và tất nhiên kết quả không gì khác ngoài: THẤT BẠI.
    Tôi bắt đầu mày mò một số bài viết tiếng Việt, và dĩ nhiên là trên javavietnam.org. Qua đó tôi được biết có một bài viết của bác Thắng trên tincntt.com. Mừng hết lớn, đọc lấy đọc để... Kết quá cũng thất bại, vì đơn giản đó chỉ là một bài dịch trên DevArticles, chẳng có một ứng dụng, và còn tệ hơn khi tác giả chẳng mảy may chú ý đến sự tiếp thu của người đọc. Lên nguồn tiếng anh thì thấy họ viết còn dễ hiểu hơn, có lẽ đó cũng là lý do người ta nói: các bài viết tiếng Việt thường “chuối”. (Có gì không phải vui lòng bỏ qua cho)

    Kỳ 2: MỘT CHÚT KHẢ QUAN

    Đúng là cái gì cũng phải kiên nhẫn, và cuối cùng cũng được kết qủa như mong muốn. Quyển đầu tiên đã giúp tôi thu nhập được nhiều thứ nhất chính là JAAS in action. Giới thiệu một cách khái quát về jaas, từ căn bản, đến những webapp dùng jaas. Những web application mà cuốn sách này tôi vẫn chưa chạy được, nhưng cũng giúp tôi bước đầu trên con đường chinh phục JAAS.
    Để chạy được các ví dụ trong quyển sách này máy bạn cần có eclipse, và bạn cũng nên học một chút về ant trên eclipse.
    Tôi lại tiếp tục trên con đường tiềm kiếm một app hoàn chỉnh, và đã thấy trên JEE5 tutorial, và tôi đã run được ứng dụng web đầu tiên.

    Kỳ 3: KẾT QUẢ ĐẠT ĐƯỢC

    Trên con đường đi tìm, tôi đã gặp và nằm lòng những khái niệm
    User: Người dùng, và trong jaas nó không được biểu diễn bằng một Object
    Subject: là một thực thể đại diện cho user trên ứng dụng.
    Principal: Đại diện cho một (Administrator) hoặc nhiều (Group manager) Subject.
    Resource: Tất cả mọi thứ trong hệ thống của chúng ta.
    Permission: Quản lý Resource, Một permission class đại diện cho 3 thứ: resource, action và name. Nó có thể được gán cho một Principal.
    Authentication: Hành động kiểm tra một user, gán cho subject của user này một principal; bạn là ai
    Authorization: Hành động kiểm tra một user, và cấp phép truy cập resource; bạn có thể làm gì.
    Authentication module: Phương thức xác nhận user.
    Authentication scheme: Bao gồm nhiều Authentication module.

    Kỳ 4: CẤU HÌNH SUN APPLICATION SERVER

    Để Application Server xác định quyền users, ta làm theo các bước sau
    1.Start server
    2.Vào admin console( mặc định là http://localhost:4848/asadmin)
    3.Gõi username và password (thường là admin và adminadmin)
    4.Mở cây bên trái chọn Configuration\ Security\ Realms\. Ta có thể có 3 lựa chọn:
    file: tạo user cho phép truy cập vô ứng dụng
    admin-realm: tạo user cho phép tác động vào server admin
    certificate
    5.Chọn manage users, ở đó bạn có thể thêm, xoá, và sửa thông tin user.
    6.OK
    7.Logout

    Kỳ 5: EJB+JAAS HelloWorld APPLICATION

    Bạn nên dùng Netbeans 5.5.1 + Sun Application Server 9
    Bước 1: Tạo một Enterprise Application
    Proejct name: HelloWorld
    Bỏ check Create Web application module
    Check vào mục create application client module
    Sửa tên main class thành com.bds.test.Client
    Bước 2: Tạo session Bean
    EJB Name: HelloWorld
    Package: com.bds.ejb
    Check remote, bỏ check local

    HelloWorldBean.java
    @DeclareRoles("admin")
    @Stateless
    public class WorldHelloBean implements WorldHelloRemote {
    /** Creates a new instance of WorldHelloBean */
    public WorldHelloBean() {
    }
    @RolesAllowed("admin")
    public void sayHello(String name) {
    System.out.println("Hello "+name);
    }
    }

    HelloWorldRemote.java
    @Remote
    public interface WorldHelloRemote {
    void sayHello(String name);
    }

    Bước 3: Test
    Ta chỉ cần remote đến WorldHelloBean để dùng:

    public class Client {

    @EJB
    private static WorldHelloRemote worldHelloBean;

    /** Creates a new instance of Client */
    public Client(String[] args) {
    }
    /**
    * @param args the command line arguments
    */
    public static void main(String[] args) {
    Client c = new Client(args);
    c.doTest();
    // TODO code application logic here
    }
    public void doTest(){
    worldHelloBean.sayHello("Hien");
    }
    }

    Bước 4: Khai báo <security-role-mapping>
    Trong helloworld project: sửa file sun-application.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE sun-application PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Java EE Application 5.0//EN" "http://www.sun.com/software/appserver/dtds/sun-application_5_0-0.dtd">
    <sun-application>
    <security-role-mapping>
    <role-name>admin</role-name>
    <group-name>admin</group-name>
    </security-role-mapping>
    </sun-application>

    Bước 5: Run
    Để chạy được project này bạn cần có một user có ID:admin, password: admin, group: admin.
    Sau đó ta chỉ run project helloworld

    Kỳ 6: JAAS-WEB APPLICATION
    Writing...
    TÀI LIỆU THAM KHẢO
    http://java.sun.com/products/jaas/overview.html
    http://www.mooreds.com/jaas.html
    http://www.tincntt.com/tincntt/cms/vi/tutorials/java_tutorials/jaas_1.html
    http://java.sun.com/javaee/5/docs/tutorial/doc/Overview9.html

    Đọc tiếp >>

    Cao Trong Hien

    ,