Skip navigation.

Người tình trăm năm

Hoa có vàng nơi ấy

SAM SUNG CHINH HANG

NOKIA CHÍNH HÃNG

Tổng quan EJB (phần 3) Stateless Session Bean

I. Khái niệm

1. Stateless Session Bean
- Trạng thái giao dịch liên kết với một client, gọi là trạng thái đặc trưng cho client (client-specific state). Trạng thái này gọi là conversation state, thực tế còn bao gồm cả các kết nối socket, kết nối database, tham chiếu đến bean khác...
- Stateless Session Bean không lưu giữ trạng thái converation Container phân biệt Stateless Session Bean với Stateful Session Bean nhờ những mô tả trong tập tin deployment descriptor ejb-jar.xml.
- Stateless Session Bean được thiết kế để phục vụ cho nhiều client, với những session chỉ có một request.
- Stateless Session Bean chỉ có 2 trạng thái:
•Does Not Exist: không có thực thể bean tồn tại trong bộ nhớ.
•Method-Ready Pool: thực thể bean có mặt trong pool, sẵn sàng phục vụ các triệu gọi business method từ client.
- Stateless Session Bean yêu cầu ít resource nên còn gọi là lightweight bean.

2. Vòng đời của Stateless Session Bean
- EJB Container quản lý các thực thể bean. Khi cần thực thể, thực thể bean được khởi tạo bằng cách dùng phương thức Class.newInstance() trên lớp bean. Sau đó phương thức setSessionContext( sc ) của thực thể bean được triệu gọi để thực thể bean nhận tham chiếu đến đối tượng SessionContext. Thực thể bean cần SessionContext để truy xuất thông tin môi trường: identity và role của client, transaction context,… Cuối cùng phương thức ejbCreate() không đối số của bean được EJB Container triệu gọi (chỉ một lần trong vòng đời của bean). Lúc này bean đã ở trạng thái sẵn sàng thực hiện các yêu cầu của client do EJB(Local) Object ủy nhiệm (delegate) đến, gọi là trạng thái Method-Ready Pool.
- Do không cần lưu trữ trạng thái giao dịch, Stateless Session Bean không quan tâm đến việc kích hoạt (activation) bean, container không bao giờ gọi đến các phương thức callback ejbActivate() và ejbPassivate() của nó. Ra khỏi trạng thái Method-Ready Pool, bean sẽ rơi vào trạng thái Does Not Exits nghĩa là container không cần nó nữa. Khả năng lưu trữ của pool là cơ sở để container thu giảm số bean ở trạng thái Method-Ready Pool bằng phương thức ejbRemove(), giải phóng tài nguyên và hủy bean. Xem phần Instance swapping phía dưới để hiểu rõ cơ chế.

3. Các phương thức callback
- Phương thức void ejbCreate() chỉ có duy nhất một và không tham số vì không cần khởi tạo trạng thái conversation. Session bean phải có ít nhất một phương thức ejbCreate(). Thường rỗng do client không triệu gọi đến.
- Phương thức void ejbRemove() thường rỗng do client không  triệu gọi đến.
- Phương thức void ejbPassivate() rỗng vì container không bao giờ thụ động hóa thực thể bean.
- Phương thức void ejbActivate() rỗng vì container không bao giờ kích hoạt thực thể bean.
- Phương thức void setSessionContext( SessionContext ctx ) thường rỗng vì bean không có biến thực thể (biến lớp riêng) nên không cần lưu các tham chiếu đến các biến đó.

4. Hoạt động của Stateless Session Bean
1- Container đăng ký tất cả bean đã triển khai với JNDI với tên JNDI chỉ định trong deployment descriptor.
2- Container chịu trách nhiệm quyết định tạo thực thể bean tùy theo chính sách lưu (caching). Container lần lượt gọi các phương thức Class.newInstance(), setSessionContext()và ejbCreate().
3- Client tìm thấy Home(Local) interface của bean bằng cách lookup()thông qua JNDI.
4- Client dùng phương thức create() của Home(Local) interface để tạo ra EJB(Local) Object của bean. Chú ý phương thức này không triệu gọi ejbCreate() trên bean. 
5- Client gọi các business method thông qua EJB(Local) Object, lời gọi này sẽ được container ủy nhiệm đến bean.
6- Container chịu trách nhiệm quyết định loại bỏ thực thể bean bằng cách gọi ejbRemove(). Không loại bỏ thực thể bean bằng cách gọi phương thức remove() của Home(Local) interface, phương thức này không làm gì cả.
5. Quản lý thực thể với Stateless Session Bean
a) Instance Pool
- Các thực thể bean sẽ được tạo khi container hoạt động. Instance pooling là cơ chế để thu giảm số thực thể bean cần có mặt cho việc phục vụ các yêu cầu của client, giảm chi phí tài nguyên khi tạo và hủy bean bằng cách dùng lại các thực thể có trong pool. Như vậy thu giảm việc sử dụng tài nguyên.
- Các instance pool đều cố gắng quản lý các tập hợp bean sao cho có thể truy xuất nhanh các bean trong thời gian chạy. Để thiết lập instance pool, container tạo một số thực thể của lớp bean và giữ chúng cho đến khi cần đến. Khi client yêu cầu một business method, thực thể bean từ pool được gán đến EJB(Local) Object liên kết với client. Khi một EJB(Local) Object không cần đến thực thể, bean được trả về cho instance pool.
- Một EJB server duy trì nhiều instance pool cho mỗi kiểu pool được triển khai. Mọi thực thể trong instance pool là tương đương với nhau. Thực thể bean sẽ được chọn tùy ý để gán đến EJB(Local) Object cần đến nó. Số thực thể trong bean dao động do hoạt động vào/ra của bean.
- Sau khi thực thể bean được đặt trong pool, gọi là trạng thái Pooled, nó lấy tham chiếu đến một javax.ejb.EJBContext. EJBContext cung cấp một giao diện để bean có thể dùng liên lạc với môi trường EJB.
- Khi bean liên kết với một EJB(Local) Object, nó ở trạng thái Ready. Lúc này EJBContext có một ý nghĩa mới, nó cung cấp thông tin về client dùng bean, cần khi bean truyền tham chiếu đến chính bean hoặc các bean khác.

b) Instance swapping (hoán chuyển thực thể bean)
- Sau khi phục vụ yêu cầu của client, bean tách rời EJB Object và quay trở về instance pool. Nếu client yêu cầu đến EJB Object mà không có thực thể bean liên kết với nó, container sẽ tìm bean trong pool và lại gán cho EJB Object. Thao tác này gọi là instance swapping.
- Stateless Session Bean không cần lưu trữ trạng thái conversation trong nó nên mọi triệu gọi phương thức từ nó tiến hành độc lập. Điều này có nghĩa bất kỳ Stateless Session Bean nào cũng có thể phục vụ yêu cầu cho bất kỳ EJB Object nào có kiểu ủy nhiệm hợp lệ đến nó. Container có thể hoán chuyển các thực thể bean vào hoặc ra giữa các triệu gọi phương thức. 
- Hình trên mô tả cơ chế hoán chuyển thực thể khi triệu gọi phương thức của Stateless Session Bean: Bean A phục vụ một triệu gọi business method được ủy nhiệm từ EJB Object 1 (a). Khi bean A thực hiện xong yêu cầu nó chuyển ngược trở về instance pool (b). Khi client triệu gọi phương thức trên EJB Object 2, bean A lại liên kết với EJB Object 2 để phục vụ yêu cầu này (c). Khi bean A đang phục vụ, nếu có một triệu gọi trên EJB Object 1, bean B sẽ phục vụ yêu cầu này.
- Dùng chiến lược hoán chuyển trên, vài bean có thể phục vụ cho hàng trăm client. Khi một thực thể bean chấm dứt phục vụ, nó lập tức trở về trạng thái sẵn sàng trong instance pool cho một EJB Object bất kỳ cần ủy nhiệm đến nó.

6. Lớp và giao diện
- Hàm dựng (constructor) nói chung không dùng khi tạo lớp bean, nếu có phải là hàm dựng mặc định (không đối số) và public. Trong lớp bean không có các phương thức finalize.
+ void ejbActivate()

a) Client truy xuất từ xa (remote client view)
II. Thiết kế và triển khai
A. Tạo Stateless Session Bean
- Cần có gói javax.ejb trong CLASSPATH: nghĩa là cần có ejb-2_1-api.jar, hoặc j2ee.jar (nếu có cài đặt j2eesdk), hoặc jboss-j2ee.jar (%JBOSS_HOME%\client) trong CLASSPATH. Cần chú ý đến phiên bản của EJB đang triển khai.

1. Lớp EJB
- Lớp EJB cài đặt:
•Các phương thức quan trọng nhất của EJB là các business method (phương thức nghiệp vụ). EJB được thiết kế nhằm mục tiêu giúp người lập trình chỉ quan tâm đến việc thực hiện các business method. Các business method này cùng tên với phương thức khai báo trong EJB interface để có thể delegate từ đó được, nhưng không cần ném RemoteException vì được delegate, mà không triệu gọi từ xa. Các phương thức này không được bắt đầu bằng ejb, không được static hoặc final và phải public.
•Các phương thức bắt buộc cài đặt, dành cho container dùng quản lý EJB, gọi là các phương thức callback. Các phương thức này thừa kế từ giao diện javax.ejb.SessionBean.
Lớp EJB không chứa các tác vụ cấp hệ thống (persistence, security, transaction, …) nên dễ triển khai hơn RMI nhiều.
- Lớp EJB cần phải:
•import các giao diện: javax.ejb.SessionBean, javax.ejb.SessionContext.
•thừa kế giao diện javax.ejb.SessionBean.
•Nếu có constructor phải là constructor mặc định (không đối số) và không có phương thức finalize.
package myejb.session;

import javax.ejb.*;
import javax.naming.*;

public class ConvertBean implements SessionBean {
  private SessionContext ctx;
  private Context environment;

  // Callback methods
  public void setSessionContext( SessionContext sc ) {
    this.ctx = sc;
  }

  public void ejbCreate() throws CreateException {
    try {
      InitialContext ic = new InitialContext();
      environment = ( Context )ic.lookup( "java:comp/env" );
    } catch ( NamingException ne ) {
      throw new CreateException( "Could not look up context" );
    }
  }

  public void ejbRemove() { }
  public void ejbActivate() {}
  public void ejbPassivate() { }

  // Business methods
  public double dollarToVND( double dollars ) {
    Double rate = new Double( 0 );
    try {
      rate = ( Double )environment.lookup( "ExchangeRate" );
    } catch ( NamingException ne ) {
      System.out.println( "Invalid Exchange Rate" );
    }
    return dollars * rate.doubleValue();
  }
}
- Passivation và Activation chỉ thực hiện trên Stateful Session Bean, không thực hiện trên Stateless Session Bean nhưng có trong giao diện javax.ejb.SessionBean.
Business logic của bean trên là phương thức dollarToVND() chuyển đổi tiền từ USD sang VND. Tỷ giá hối đoái (exchange rate) được lưu và tham chiếu như một thuộc tính ENC trong tập tin DD ejb-jar.xml.

2. Các giao diện
- Tùy kịch bản sử dụng, một trong hai hoặc cả hai loại giao diện sau sẽ được triển khai. Có thể dùng XDoclet  để sinh các giao diện này một cách tự động.
a) Giao diện truy xuất từ xa (outside view, remote view)
- Cặp giao diện truy xuất từ xa dùng khi có triệu gọi các business method của EJB từ xa, ví dụ từ ứng dụng client độc lập, từ Web tier thuộc Application server khác. Gồm: Remote interface và Remote Home interface.
- Remote interface khai báo tất cả các business method của EJB mà client sẽ triệu gọi từ xa, vì vậy có ném RemoteException khi lời triệu gọi thất bại. Các business method khai báo ở đây sẽ được cài đặt trong lớp EJB và sẽ được Remote interface ủy nhiệm đến.
Remote interface là một giao diện “bộc lộ” các business method của EJB liên kết với nó cho phía client triệu gọi.
Remote interface cần phải:
•import các giao diện javax.ejb.EJBObject và java.rmi.RemoteException.
•Remote interface phải public (để có thể triệu gọi từ xa) và thừa kế giao diện javax.ejb.EJBObject.
•Các business method được định nghĩa ở đây có khả năng ném ra java.rmi.RemoteException khi gặp sự cố (mạng, server, …)
package myejb.session;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface Convert extends EJBObject {
  public double dollarToVND( double dollars ) throws RemoteException;
}
- Home interface định nghĩa các phương thức quản lý vòng đời của bean, hoạt động như một factory . Home interface được tạo ra cùng cặp với giao diện triệu gọi tương ứng. Với Remote interface, ta có Remote Home interface.
Giao diện này cần phải:
•import các giao diện: java.rmi.RemoteException, javax.ejb.CreateException và javax.ejb.EJBHome.
•Remote Home interface phải thừa kế giao diện javax.ejb.EJBHome.
•Có phương thức create() ném ra các exception: RemoteException và CreateException (cùng với các exception của người dùng nếu có) và trả về một EJB Remote Object có kiểu Remote interface.
package myejb.session;

import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface ConvertHome extends EJBHome {
  Convert create() throws java.rmi.RemoteException,CreateException;
}
Phương thức create() của Stateless Session Bean không nhận bất kỳ một đối số nào vì không cần lưu trữ trạng thái conversation giữa client và server.

b) Giao diện truy xuất cục bộ (inside view, local view)
- Giao diện truy xuất cục bộ tương tự giao diện truy xuất từ xa nhưng dùng khi triệu gọi EJB cục bộ từ một EJB khác hoặc từ JSP, servlet thuộc Web tier trong cùng ứng dụng J2EE. Giao diện truy xuất cục bộ không dựa trên RMI và thường được dùng trong một JVM. Gồm: Local interface và Local Home interface.
- Local interface cần phải:
•import giao diện javax.ejb.EJBLocalObject.
•Local interface phải public và thừa kế giao diện javax.ejb.EJBLocalObject.
•Các business method được định nghĩa ở đây không ném ra java.rmi.RemoteException khi gặp sự cố, vì được triệu gọi cục bộ.
package myejb.session;

import javax.ejb.EJBLocalObject;

public interface ConvertLocal extends EJBLocalObject {
  public double dollarToVND( double dollars );
}
- Local Home interface tương tự Remote Home interface, nhưng dùng chung cặp với Local interface, phương thức create() của nó được gọi cục bộ nên không ném ra RemoteException.
Local Home interface cần phải:
•import các giao diện: javax.ejb.CreateException và javax.ejb.EJBLocalHome.
•Local Home interface phải thừa kế giao diện javax.ejb.EJBLocalHome.
•Có phương thức create() ném ra exception CreateException cùng với các exception của người dùng nếu có và trả về một EJB Local Object có kiểu Local interface.
package myejb.session;

import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;

public interface ConvertLocalHome extends EJBLocalHome {
  ConvertLocal create() throws CreateException;
}

3. Deployment Descriptor (DD)
- Chuẩn đóng gói các component của J2EE quy định EJB được đóng vào gói .JAR, kèm theo tập tin mô tả triển khai (Deployment Descriptor) ejb-jar.xml. Ta dùng Ant để thực hiện việc đóng gói.
- JBoss cung cấp một XML Editor là ejx.jar (chỉ dùng cho EJB1.1) nhưng từ sau phiên bản 2.1 không thấy gói này nữa. Có thể dùng tiện ích soạn thảo bất kỳ để tạo tập tin XML trên. Nếu dùng XDoclet, các tập tin mô tả cũng được sinh ra tự động như các giao diện.
- Trong DD, người phát triển bean cần cung cấp các thông tin mà EJB container không thể nhận được bằng cách “nội soi” (introspection) các lớp.
<?xml version="1.0"?>
<ejb-jar version="2.1" 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/ejb-jar_2_1.xsd">
  <enterprise-beans>
    <session>
      <ejb-name>Convert</ejb-name>
      <home>myejb.session.ConvertHome</home>
      <remote>myejb.session.Convert</remote>
      <local-home>myejb.session.ConvertLocalHome</local-home>
      <local>myejb.session.ConvertLocal</local>
      <ejb-class>myejb.session.ConvertBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>

      <env-entry>
        <env-entry-name>ExchangeRate</env-entry-name>
        <env-entry-type>java.lang.Double</env-entry-type>
        <env-entry-value>15740.0</env-entry-value>
      </env-entry>
    </session>
  </enterprise-beans>

  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>Convert</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Supports</trans-attribute>
    </container-transaction>

    <security-role>
      <description>Users</description>
      <role-name>users</role-name>
    </security-role>
  </assembly-descriptor>
</ejb-jar> 
Với ví dụ trên, chỉ cần element <enterprise-beans>. Các element khác chỉ minh họa thêm phần transaction và security, sẽ thảo luận sau. 
- Mỗi component của ứng dụng J2EE có một JNDI ENC riêng (Enterprise Naming Context), cung cấp nơi đăng ký các đối tượng phân tán, thường dùng khi truy xuất cục bộ. Trong đó, java:comp là đặc tả component (component – specific) riêng của JNDI ENC. Một số thông tin được khai báo trong các DD chuẩn và được truy xuất thông qua JNDI:
•Các mục nhập (thuộc tính) môi trường (environment entry), khai báo trong các element <env-entry>. Xem ví dụ khai báo thuộc tính ENC ExchangeRate trong DD ejb-jar.xml trên.
•Các tham chiếu đến EJB, khai báo trong các element <ejb-ref> và <ejb-local-ref>.
•Các tham chiếu đến nguồn kết nối dữ liệu (resource manager connection factory), khai báo trong các element <resource-ref>.
•Các tham chiếu tài nguyên môi trường (resource environment), khai báo trong các element <resource-env-ref>.

B. Tạo client truy xuất Stateless Session Bean

1. Client là ứng dụng độc lập
- Sau khi triển khai, Home Object của EJB được đăng ký trong cây JNDI. Để truy xuất bean ta lần lượt thực hiện:

a) Định vị Home Object
- Client dùng tên đăng ký JNDI để định vị Home Object. 
•Thiết lập thuộc tính môi trường (Initial Context Factory, vị trí server cung cấp dịch vụ Naming, …) cho JVM của client. Các cách thiết lập đã được giới thiệu trong chương 1. Trong ví dụ minh họa này, ta tạo tập tin jndi.properties có nội dung:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=venus:1099
venus là hostname (cũng có thể dùng địa chỉ IP, địa chỉ vu hồi localhost – 127.0.0.1, …) của máy chủ chạy JBoss. Port 1099 là mặc định cho JNDI.

•Tạo JNDI naming context như một giao diện giữa client và JNDI.
Context ctx = new InitialContext();

b) Tìm (lookup) đối tượng thông qua JNDI
- Sau khi tạo JNDI context, phương thức lookup() của đối tượng lớp javax.naming.InitialContext được sử dụng để định vị đối tượng có tên JNDI chỉ định. Phương thức này trả về một đối tượng (kiểu Object chung).
Object obj = ctx.lookup( "Convert" );

c) Thu hẹp (narrow) tham chiếu
- Đối tượng trả về bởi phương thức lookup()trong trường hợp client truy xuất từ xa có thể là đối tượng CORBA hoặc Java.  Để tổng quát hơn, nên thu hẹp tham chiếu cho phép ép kiểu một cách tường minh đối tượng nhận được thành kiểu home interface. Điều này được thực hiện bởi phương thức PortableRemoteObject.narrow(), có hai tham số: đối tượng do lookup() trả về và tên tập tin class của home interface. Lớp javax.rmi.PortableRemoteObject được dùng thay cho lớp java.rmi.server.UnicastRemoteObject trong RMI để bảo đảm tính tương thích với các giao thức khác JRMP, ví dụ RMI/IIOP. 
ConvertHome home = ( ConvertHome )PortableRemoteObject.narrow( obj, ConvertHome.class );
- Trong trường hợp client truy xuất cục bộ, không cần đến phương thức này.

d) Sinh ra một thực thể EJB
- EJB sẽ triệu gọi phương thức create() của đối tượng remote home để trả về EJB Object (đối tượng remote interface).
Convert convert = home.create();

e) Triệu gọi các business method
- EJB Interface định nghĩa các business method thực hiện trong lớp EJB, client sẽ triệu gọi các phương thức này thông qua tham chiếu đến EJB Object do phương thức create() trả về. Khi EJB Object nhận lời triệu gọi, nó ủy nhiệm (delegate) cho bean bên trong liên quan thực hiện.
vnd = convert.dollarToVND( amt );

f) Client truy xuất từ xa
- Chương trình client sau có giao diện Swing, các thao tác chủ yếu được thực hiện trong phương thức actionperformed() của lớp ButtonListener. Chú ý tên JNDI của bean khi truy xuất từ xa.
package clients;

import myejb.session.Convert;
import myejb.session.ConvertHome;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;

public class ConvertClient extends JFrame {
  public static String value;
  public static double vnd;
  public static double amt;

  Container c;
  JTextField t = new JTextField( 10 );
  JButton b = new JButton( "Convert" );
  JLabel result = new JLabel();

  public ConvertClient( String s ) {
    super( s );
    c = getContentPane();
    c.setLayout( new GridLayout( 2, 2, 2, 2 ) );
    c.add( new JLabel( "Enter the amount in USD:" ) );
    c.add( t );
    c.add( b );
    c.add( result );
    value = t.getText();
    b.addActionListener( new ButtonListener() );
    setSize( 400, 95 );
    setVisible( true );
    setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  }

class ButtonListener implements ActionListener {
  public void actionPerformed( ActionEvent ev ) {
    value = t.getText();
    amt = Double.parseDouble( value );
    try {
      Context ctx = new InitialContext();
      Object obj = ctx.lookup( "Convert" );
      ConvertHome home = (ConvertHome)PortableRemoteObject.narrow(obj,ConvertHome.class);
      Convert convert = home.create();
      vnd = convert.dollarToVND( amt );
      convert.remove();
      if ( vnd >= 1E6 )
        result.setText( String.valueOf( vnd/1000000 ) + " million(s) VND" );
      else
        result.setText( String.valueOf( vnd ) + " VND" );
        
      ctx.close();
    } catch ( Exception e ) {
      e.printStackTrace();
    }
  }
}

  public static void main( String[] args ) {
    new ConvertClient( "Convert Tool" );
  }
}

2. Client là Web tier (truy xuất cục bộ)
  
- Servlet ConvertSevlet.java là client truy xuất EJB cục bộ, gồm hai phần:
•doGet(): dùng để hiển thị form ban đầu vì lúc đầu gọi trang bằng GET; hoặc dùng để hiển thị form phía trên kết quả, do doPost() gọi doGet() trước tiên.
•doPost(): dùng để hiển thị kết quả vì lúc này gọi trang bằng POST thông qua form. Kết quả có được từ việc triệu gọi cục bộ EJB.
- Khi triệu gọi EJB thông qua giao diện truy xuất cục bộ, không cần thiết lập các thuộc tính môi trường liên quan JNDI vì client đã lấy thông tin môi trường từ Container, ta chỉ sử dụng một thực thể InitialContext mặc định để lookup Local Home Object từ ENC hoặc tên JNDI. Cũng không cần thu hẹp tham chiếu nhận được bằng phương thức narrow() của lớp PortableRemoteObject.
import myejb.session.*;
import javax.naming.*;
import java.io.PrintWriter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.ejb.CreateException;

public class ConvertServlet extends javax.servlet.http.HttpServlet {
  private ConvertLocalHome home = null;

  public void init( ServletConfig conf ) throws ServletException {
    super.init( conf );
    try {
      Context ctx = new InitialContext();
      if ( home == null )
        home = ( ConvertLocalHome )ctx.lookup( "java:comp/env/Convert" );
    } catch ( NamingException e ) {
      e.printStackTrace ();
    }
  }

  public void doGet( HttpServletRequest req, HttpServletResponse res )
     throws ServletException ,IOException {
    res.setContentType( "text/html" );
    PrintWriter out = ( PrintWriter )res.getWriter();
    out.println( "<html><head>Convert" );
    out.println( "<style>input.std { width:200; }" );
    out.println( "div.frame { font-family:verdana; font-size: 9pt;}" );
    out.println( "</style></head><body>
"); out.println( "<form action=\"ConvertServlet\" method=\"post\">" ); out.print( "Enter the amount in USD: " ); out.println( "<input type=\"text\" name=\"amt\" class=\"std\" />
" ); out.println( "<input type=\"submit\" value=\"Convert\" /></form>
" ); out.println( "</body></html>" ); } public void doPost( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { double vnd = 0.0; doGet(req,res); PrintWriter out = ( PrintWriter )res.getWriter(); try { ConvertLocal convert = home.create(); vnd = convert.dollarToVND( Double.parseDouble( req.getParameter( "amt" ) ) ); convert.remove(); } catch ( Exception ex ) { out.println( "
Error: " + ex.toString() ); } String result = ""; if ( vnd >= 1E6 ) result = String.valueOf( vnd/1000000 ) + " million(s) VND"; else result = String.valueOf( vnd ) + " VND"; out.println( "<html><head><style>" ); out.println( "p { font-family:verdana;font-size:9pt; }</style></head>" ); out.println( "<body>

Convert result:
" ); out.println( req.getParameter( "amt" ) + "USD = " + result + ".

" ); out.println( "</body></html>" ); } } - Module Web cần DD web.xml cho gói .WAR của ứng dụng Web. Nếu dùng XDoclet, DD này cũng được sinh ra một cách tự động. <?xml version="1.0"?> <web-app 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" version="2.4"> <display-name>Convert</display-name> <welcome-file-list> <welcome-file>ConvertServlet</welcome-file> </welcome-file-list> <servlet> <servlet-name>ConvertServlet</servlet-name> <servlet-class>ConvertServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ConvertServlet</servlet-name> <url-pattern>/ConvertServlet</url-pattern> </servlet-mapping> <ejb-local-ref> <ejb-ref-name>Convert</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local-home>myejb.session.ConvertLocalHome</local-home> <local>myejb.session.ConvertLocal</local> <ejb-link>convert.jar#Convert</ejb-link> </ejb-local-ref> </web-app> - Tham chiếu cục bộ đến EJB được khai báo bằng cách dùng element <ejb-local-ref> trong DD web.xml trên. Element này chứa các element lồng: •Element tùy chọn <description> chứa mô tả. •Element <ejb-ref-name> chứa tên của tham chiếu liên quan đến context java:comp/env. •Element <ejb-ref-type> chỉ định kiểu của EJB. Phải là Entity hoặc Session. •Cặp element <local> và <local-home> chứa tên đầy đủ cặp giao diện truy xuất cục bộ của EJB cần tham chiếu. •Element tùy chọn <ejb-link> kết nối tham chiếu đến một EJB khác trong cùng gói EJB hoặc trong cùng ứng dụng J2EE. Trị của <ejb-link> là <ejb-name> của bean được tham chiếu. Nếu có nhiều EJB cùng <ejb-name> trị này sẽ là đường dẫn đến gói EJB (.jar), <ejb-name> của bean cần tham chiếu gắn phía sau tên gói EJB, tách ra bởi dấu #. Như vậy trong DD trên trị của <ejb-link> là Convert hoặc cụ thể hơn convert.jar#Convert đều được. Element <ejb-link> phải được chỉ định trong JBoss để so trùng tham chiếu cục bộ đến bean tương ứng. - Ứng dụng J2EE cũng cần DD application.xml cho việc lắp ráp ứng dụng J2EE, tạo thành gói .EAR. <?xml version="1.0"?> <application xmlns="http://java.sun.com/xml/ns/j2ee" version="1.4" 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/application_1_4.xsd"> <display-name>Convert</display-name> <module> <web> <web-uri>convert.war</web-uri> <context-root>Convert</context-root> </web> </module> <module> <ejb>convert.jar</ejb> </module> </application> 2. Tạo build.xml để triển khai nhanh bằng Ant - Chuẩn bị tập tin build.xml có nội dung như sau: <?xml version="1.0"?> <project name="JBoss" default="assemble" basedir="."> <property environment="env" /> <property name="src.dir" value="${basedir}/src" /> <property name="websrc.dir" value="${basedir}/servlet" /> <property name="src.resources" value="${basedir}/src/resources" /> <property name="jboss.home" value="${env.JBOSS_HOME}" /> <property name="build.dir" value="${basedir}/build" /> <property name="build.classes.dir" value="${build.dir}/classes" /> <property name="app.name" value="convert" /> <path id="classpath"> <fileset dir="${jboss.home}/client"> <include name="**/*.jar" /> </fileset> <pathelement location="${basedir}/lib/j2ee.jar" /> <pathelement location="${build.classes.dir}" /> <pathelement location="${basedir}/jndi" /> </path> <target name="prepare"> <delete dir="${build.dir}" /> <mkdir dir="${build.dir}" /> <mkdir dir="${build.dir}/servlet" /> <mkdir dir="${build.classes.dir}" /> </target> <target name="compile" depends="prepare"> <javac srcdir="${src.dir}" destdir="${build.classes.dir}" debug="on" deprecation="on" optimize="off" includes="**"> <classpath refid="classpath" /> </javac> </target> <target name="ejbjar" depends="compile"> <jar jarfile="${build.dir}/${app.name}.jar"> <fileset dir="${build.classes.dir}"> <include name="myejb/session/*.class" /> </fileset> <fileset dir="${src.resources}/beans"> <include name="**/*.xml" /> </fileset> </jar> </target> <target name="webwar"> <javac srcdir="${websrc.dir}" destdir="${build.dir}/servlet" debug="on" deprecation="on" optimize="off" includes="**"> <classpath refid="classpath" /> </javac> <war warfile="${build.dir}/${app.name}.war" webxml="${src.resources}/web/web.xml"> <fileset dir="${build.dir}/servlet" /> </war> </target> <target name="assemble" depends="ejbjar,webwar"> <ear earfile="${build.dir}/${app.name}.ear" appxml="${src.resources}/app/application.xml"> <fileset dir="${build.dir}" includes="*.jar,*.war" /> </ear> <delete file="${build.dir}/${app.name}.jar" /> <delete file="${build.dir}/${app.name}.war" /> <move file="${build.dir}/${app.name}.ear" todir="${jboss.home}/server/default/deploy" /> </target> <target name="run.client"> <java classname="clients.ConvertClient" fork="yes" dir="."> <classpath refid="classpath" /> </java> </target> <target name="clean"> <delete dir="${build.dir}" /> <delete file="${jboss.home}/server/default/deploy/${app.name}.ear" /> </target> </project> 3. Triển khai và chạy ứng dụng a) Chạy JBoss server - Chạy %JBOSS_HOME%\bin\run.bat trong một console. b) Chạy Ant - Dùng Ant để tự động biên dịch, đóng gói, lắp ráp ứng dụng và triển khai nhanh: ant - Ngay sau khi gói ứng dụng J2EE convert.ear được tạo trong thư mục build và được sao chép một cách tự động vào thư mục %JBOSS_HOME%\server\default\deploy\, lập tức thấy chi tiết triển khai gói này trong console chạy JBoss server. Đây là khả năng hot deployment của JBoss. - Cũng có thể theo dõi kết quả triển khai trong jmx-console quản lý JBoss:

Tổng quan EJB (phần 2)

1. Local và Remote interface

2. Quản lý tài nguyên
a) Instance Pool
- Các thực thể bean sẽ được tạo khi container hoạt động. Instance pooling là cơ chế để thu giảm số thực thể bean cần có mặt cho việc phục vụ các yêu cầu của client, giảm chi phí tài nguyên khi tạo và hủy bean bằng cách dùng lại các thực thể có trong pool. Như vậy thu giảm việc xử dụng tài nguyên.
- Các instance pool đều cố gắng quản lý các tập hợp bean sao cho có thể truy xuất nhanh các bean trong thời gian chạy. Để thiết lập instance pool, container tạo một số thực thể của lớp bean và giữ chúng cho đến khi cần đến. Khi client yêu cầu một business method, thực thể bean từ pool được gán đến EJB Object liên kết với client. Khi một EJB Object không cần đến thực thể, bean được trả về cho instance pool.
- Một EJB server duy trì nhiều instance pool cho mỗi kiểu pool được triển khai. Mọi thực thể trong instance pool là tương đương với nhau. Thực thể bean sẽ được chọn tùy ý để gán đến EJB Object cần đến nó. Số thực thể trong bean dao động do hoạt động vào/ra của bean.
- Sau khi thực thể bean được đặt trong pool, gọi là trạng thái Pooled, nó lấy tham chiếu đến một javax.ejb.EJBContext. EJBContext cung cấp một giao diện để bean có thể dùng liên lạc với môi trường EJB.
- Khi bean liên kết với một EJB Object, nó ở trạng thái Ready. Lúc này EJBContext có một ý nghĩa mới, nó cung cấp thông tin về client dùng bean, cần khi bean 
truyền tham chiếu đến chính bean hoặc các bean khác.

b) Instance swapping (hoán chuyển thực thể bean)
- Sau khi phục vụ yêu cầu của client, bean tách rời EJB Object và quay trở về instance pool. Nếu client yêu cầu đến EJB Object mà không có thực thể bean liên kết với nó, container sẽ tìm bean trong pool và lại gán cho EJB Object. Thao tác này gọi là instance swapping.
- Stateless Session Bean không cần lưu trữ trạng thái conversation trong nó nên mọi triệu gọi phương thức từ nó tiến hành độc lập. Điều này có nghĩa bất kỳ Stateless Session Bean nào cũng có thể phục vụ yêu cầu cho bất kỳ EJB Object nào có kiểu hợp lệ ủy nhiệm đến nó. Container có thể hoán chuyển các thực thể bean vào hoặc ra giữa các triệu gọi phương thức. 
- Hình dưới mô tả cơ chế hoán chuyển thực thể khi triệu gọi phương thức của Stateless Session Bean: Bean A phục vụ một triệu gọi business method được ủy nhiệm từ EJB Object 1 (a). Khi bean A thực hiện xong yêu cầu nó chuyển ngược trở về instance pool (b). Khi client triệu gọi phương thức trên EJB Object 2, bean A lại liên kết với EJB Object 2 để phục vụ yêu cầu này (c). Khi bean A đang phục vụ, nếu có một triệu gọi trên EJB Object 1, bean B sẽ phục vụ yêu cầu này.
- Dùng chiến lược hoán chuyển này, vài bean có thể phục vụ cho hàng trăm client. Khi một thực thể bean chấm dứt phục vụ, nó lập tức trở về trạng thái sẵn sàng trong instance pool cho một EJB Object bất kỳ cần ủy nhiệm đến nó.
c) Cơ chế activation và passivation
- Không giống như Stateless Session Bean, Entity Bean và Message-Driven Bean, Stateful Session Bean không tham gia vào instance pool. Thay vào đó nó dùng cơ chế passivation và activation để tiết kiệm tài nguyên. Khi container cần tiết kiệm tài nguyên, nó có thể loại bean ra khỏi bộ nhớ, lúc này trạng thái của bean sẽ được lưu trữ (serialize) xuống thiết bị lưu trữ. Khi client triệu gọi phương thức của bean, một thực thể bean mới lại được tạo ra, chứa trạng thái conversation do bean trước lưu trữ.
- Passivation (thụ động hóa) tách bean ra khỏi EJB Object liên kết với nó và lưu trạng thái chứa trong bean (trạng thái kết liên hệ với EJB Object và trạng thái conversation), sau đó loại bean ra khỏi bộ nhớ. Client không biết điều này và có thể vẫn liên lạc với EJB Object trong khi bean đã thụ động hóa.
- Activation (kích hoạt) hồi phục lại thực thể bean liên kết với EJB Object. Lúc này container sẽ tự động tạo một thực thể mới và thiết lập các field theo dữ liệu được lưu trữ khi passivation. Sau đó EJB Object có thể ủy nhiệm các triệu gọi phương thức từ client đến bean như bình thường.
- Hình bên trình bày quá trình passivation và activation một Sateful Session Bean. (a) bean B được thụ động hóa, trạng thái của bean B được lưu trữ; (b) bean B tách ra khỏi EJB Object, loại khỏi bộ nhớ; (c) bean được kích hoạt, một thực thể mới, bean C được tạo ra với thông tin lưu trữ từ bean B và liên kết với EJB Object từ các thông tin nhận được.
3. Session Bean
- Một Session Bean thực hiện một conversation giữa client và server. Session Bean thực hiện một tác vụ bussiness theo yêu cầu của client trong một session đơn. Chúng thực hiện các bussiness logic (logic nghiệp vụ) như: workflow (chuỗi công việc), algorithm (thuật toán cho một công việc), bussiness rule (kiểm tra các luật bussiness).
- Một Session Bean giống như một phần mở rộng của client trên server, như bussiness của client được tách ra và chuyển đến server. Thực tế Session Bean cũng có thể nằm phía client (client tier) như một application client J2EE.
- Ví dụ một Session Bean có thể gửi email, giúp quản lý workflow, thực hiện các thuật toán như nén, mã hóa, ...
- Session Bean có các tính chất sau:
•Thể hiện một conversation giữa client và server.
•Thực hiện đại diện cho một client đơn. Không thể chia sẻ cho nhiều client cùng lúc. Như vậy không thể gọi bởi cùng client bằng cách dùng multithread.
•Có thể thực hiện transaction và security.
•Không thể hiện dữ liệu trực tiếp trong cơ sở dữ liệu. Tuy nhiên chúng có thể truy xuất và cập nhập dữ liệu giống như client thường làm.
•Có đời sống ngắn. Nó bị loại bỏ khi client loại bỏ nó lúc chấm dứt session, hoặc khi container ngưng vì một lý do nào đó.
- Có hai kiểu session bean:
•Stateless Session Bean: (bean không lưu vết) không lưu giữ trạng thái của conversation liên kết với một client, gọi là client-specific state (trạng thái đặc trưng cho client. Client phải chiu trách nhiệm duy trì trạng thái conversation nếu cần. Container sẽ dùng lại cùng một thực thể bean để phục vụ cho nhiều client.
Ví dụ: một Stateless Session Bean thực hiện việc đăng ký để kiểm tra người dùng và password, cho phép người dùng tạo một account trong hệ thống. Có remote interface như sau:
public interface SignOn extends EJBObject {
  public void addUser( String username, String password ) throws RemoteException;
  public boolean validateUser( String login, String password )
  throws InvalidLoginException, RemoteException;
}
•Stateful Session Bean: (bean lưu vết) lưu trạng thái conversation của server với client cụ thể trong các field của nó. Trạng thái conversation này sẽ được lưu trữ xuyên qua nhiều yêu cầu của client trong cùng một session.
Ví dụ: một Steless Session Bean dùng mô phỏng phiếu đăng ký các môn học được chọn bởi một sinh viên cụ thể trong một session chọn khóa học. Có remote interface như sau:
public interface EnrollmentCart extends EJBObject {
  public void addCourses( String[] courseIds ) throws RemoteException;
  public Collection getCourses() throws RemoteException;
  public void empty() throws RemoteException;
}
dữ liệu đăng ký (trạng thái conversation) sẽ được lưu trong một field của lớp bean.
public class EnrollmentCartEJB implements SessionBean {
  ...
  private HashSet cart;
  ...
  public void addCourses( String[] courseIds ) {
    if ( courseIds == null ) return;
    for ( int i = 0; i < courseIds.length ; ++i ) cart.add( courseIds );
  }
  public void empty() { cart.clear(); }
}

Tổng quan EJB (phần 1)

Các cách cài đặt ứng dụng phân tán

Sockets DCOM( Distributed Component Object Model RPC ( Remote Procedure Call) CORBA (Common Object Request Broker Architechture – OMG) Java RMI Khái niệm - RMI cung cấp khả năng triệu gọi các phương thức của một đối tượng từ xa thông qua giao thức JRMP. Bằng cách này có thể liên lạc và thực thi các ứng dụng xuyên qua nhiều hệ thống trên một mạng. - RMI được hỗ trợ bởi các gói java.rmi, java.rmi.server, java.rmi.registry. - Java 2 yêu cầu cài đặt một chính sách bảo mật khi thực hiện RMI. Cách tiếp cận Chương trình server /đối tượng chạy trên một JVM khác với chương trình gọi (client) Không gian địa chỉ khác nhau Phương cách cài đặt : Định nghĩa các phương thức  interface Cài đặt các phương thức này  class
Thành phần của 1 hệ thống RMI Định nghĩa các giao diện cho các phương thức triệu gọi từ xa (remote interface). Cài đặt đối tượng cho việc triệu gọi phương thức từ xa (remote object). Các file Stub và Skeleton. Một server đặt(host) đối tượng truy xuất từ xa. Một dịch vụ RMI Naming cho phép client định vị được đối tượng truy xuất từ xa. Các lớp hỗ trợ nếu có. Stub Stub đại diện cho đối tượng remote ở phía client, có nhiệm vụ: Khởi tạo một lời gọi đến remote object, bằng cách gọi remote refererence layer. Sắp xếp (marshaling) các đối số cần chuyển thành một stream tuần tự nhờ remote reference layer. Báo cho remote reference layer lời gọi đã được triệu gọi. Lấy (unmarshaling) trị trả về hoặc exception từ stream tuần tự. Báo cho remote reference layer lời gọi đã được hoàn thành Skeleton Skeleton là một thực thể phía server chứa một phương thức chuyển các lời gọi đến remote object thật sự. Có nhiệm vụ: Lấy (unmarshaling) đối số từ stream tuần tự. Tạo một lời gọi (up-call) đến remote object cài đặt thật sự. Sắp xếp (marshaling) trị trả về hoặc exception thành một stream tuần tự, nhờ remote reference layer. Remote Interface Remote interface có nhiệm vụ bộc lộ các phương thức của đối tượng server để client có thể triệu gọi từ xa (triệu gọi từ JVM khác). Các yêu cầu của remote interface: Phải được khai báo public để client có thể “nhìn thấy” các phương thức khai báo trong interface, vì client thường không cùng gói với remote interface. Thừa kế giao diện java.rmi.Remote. Các phương thức triệu gọi từ xa phải ném (throws) ra exception java.rmi.RemoteException khi gặp các lỗi kết nối mạng hoặc lỗi server. Remote Interface Example import java.rmi.*; public interface Adder extends Remote { public int add(int x, int y) throws RemoteException; } Remote Class Example import java.rmi.*; import java.rmi.server.*; public class AdderImpl extends UnicastRemoteObject implements Adder { public AdderImpl() throws RemoteException { } public int add(int x, int y) throws RemoteException { return x + y; } } RMI Registry Client phải “định vị” được đối tượng remote để triệu gọi từ xa các phương thức, điều này được thực hiện dưới sự hỗ trợ của dịch vụ Remote Object Registry (RMI Registry). RMI Registry là dịch vụ đặt tên Naming, cho phép đăng ký đối tượng remote dưới một tên, sau đó client có thể nhận tham chiếu đến đối tượng remote thông qua tên đã đăng ký Naming.rebind(“rmi://localhost:1099/adder", adder); RMI Server try { AdderImpl adder = new AdderImpl(); Naming.rebind(“rmi://localhost:2000/aaa", adder); System.out.println("Adder bound"); } catch (RemoteException re) { re.printStackTrace(); } catch (MalformedURLException me) { me.printStackTrace(); } RMI Client // dùng interface lấy tham chiếu remote Object Adder a = (Adder) Naming.lookup(“rmi://localhost:2000/aaa"); // tìm thấy và thi hành phương thức int sum = a.add(2,2); System.out.println("2+2=" + sum); Câu hỏi 1. COM là gì  DCOM ? 2. Thế nào là Java RMI 3. Ích lợi của RMI 4. Các thành phần chính của một hệ thống RMI 5. Các bước cài đặt 1 hệ thống RMI

CÁCH SỬ DỤNG ANT TRONG JAVA

1.Giới thiệu:
Hãy tưởng tượng xem khi bạn đang thực hiện một đề án lớn bằng Java, nó bao gồm nhiều tập tin java và những lớp (class) phụ thuộc vào những lớp khác hay những lớp stub hoặc  driver chẳng hạn. Chúng được chứa trong nhiều thư mục khác nhau, và kết quả thu được cũng cần đặt trong nhiều thư mục … và kết quả là bạn phải dùng những công cụ biên dịch và phải tự mình thao tác tất cả, và tất nhiên điều đó sẽ làm cho bạn mất hàng giờ để thực hiện việc đó. Vậy tại sao ta không phát triển một chương trình có thể thực hiện được những việc mà chúng ta cần và công cụ đó chính là ANT ( Bạn có thể tham khảo thêm tại website http://jakarta.apache.org/ant/ ).
ANT ( Another Neat Tool ), là công cụ xây dựng hỗ trợ đặc biệt cho lập trình bằng Java nhưng cũng có thể sử dụng cho nhiều thứ khác, vì đây là công cụ được viết hoàn toàn bằng Java nên không phụ thuộc vào bất cứ nền ứng dụng nào. Ant rất hữu ích trong công việc phức tạp đòi hỏi phải lập đi lập lại nhiều lần, vì vậy nên thích hợp với việc xử lý một cách tự động và được chuẩn hóa. Ant dùng định dạng XML để làm cơ chế hoạt động cho công cụ dưới dạng những lời hướng dẫn ( instructions), do đó dễ dàng mở rộng và bảo trì.

2.Cài đặt:
-Tải công cụ Ant từ địa chỉ sau : http://jakarta.apache.org/ant/index.html, sau đó giải nén vào một thư mục.
-Thêm đường dẫn đến thư mục /bin trong thư mục đã giải nén vào biến môi trường PATH.
-Thêm vào biến môi trường CLASSPATH đường dẫn đến những tập tin .jar trong thư mục /lib trong thư mục giải nén. (để biết thêm chi tiết xin xem thêm tại thư mục /docs/manual/install.html )
-
3.Cơ bản:
Tập tin xây dựng Ant được viết dưới dạng XML, nên chỉ cần có một phần mềm soạn thảo văn bản là có thể xây dựng tập tin Ant. Sau đây là ví dụ đơn giản:
Ví dụ 1 : build.xml

<?xml version="1.0"?>
<project name="test" default="compile" basedir=".">
<property name="src" value="."/>
<property name="build" value="build"/>
<target name="init">
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init">
<!- - Compile the java code - ->
<javac srcdir="${src}" destdir="${build}"/>
</target>
</project>

Mô tả:
<?xml version="1.0"?>
Vì đây là tập tin xml nên luôn được bắt đầu bằng thẻ này.

<project name="test" default="compile" basedir=".">
Thẻ gốc (root element) của tập tin Ant là <project>, nó có 3 thuộc tính sau:
-name: đây là tên của của đề tài
-default: mục này chỉ định thẻ <target> mặc định khi không có thẻ <target> nào được chỉ định, đây là thuộc tính bắt buộc phải có.
-basedir: chỉ định thư mục gốc mà bên trong tập tin Ant tham chiếu đến, nếu mục này bị bỏ qua thì thư mục hiện hành chứa tập tin Ant sẽ được sử dụng.

<property name="src" value="."/>
<property name="build" value="build"/>

Thẻ property cho phép khai báo biến để sử dụng trong tập tin Ant, thuộc tính name dùng đặt tên cho biến, và thuộc tính value chứa giá trị của biến đó. Để tham chiếu đến giá trị của biến này ta dùng cú pháp sau ${name-property}, như trong ví dụ trên để tham chiếu đến biến src ta dùng như sau ${src}

<target name="init">
<mkdir dir="${build}"/>
</target>

Thẻ <target> dùng để bao bọc một nhóm các hành động, mỗi thẻ có một tên để có thể tham chiếu đến ở bất kì đâu, có thể từ bên ngoài dưới dạng dòng lệnh, hay từ bên trong thông qua từ khoá depends, hoặc có thể từ một lời gọi trực tiếp. Trong ví dụ trên ta có thẻ <target> đặt tên là “init”, nó sẽ tạo ra một thư mục bằng cách sử dụng thẻ <mkdir> với tên được chỉ định bởi biến build đã được khai báo ở trên
Thẻ <target> có những thuộc tính sau:
-name: đặt tên cho thẻ để có thể tham chiếu đến sau này, thuộc tính này bắt buộc phải có
-depends: đây là một danh sách chứa các target khác mà thẻ target này phụ thuộc, các target được phân cách bằng dấu phẩy, các target nằm trong danh sách này phải được thực thi trước thẻ <target> này.
-if: cho phép thêm điều kiện để xét xem có thực hiện các hành động trong thẻ <target> không dựa trên giá trị của biến. Ví dụ if=”dk” ta có thể hiểu như sau: nếu biến dk được gán giá trị bất kì thì sẽ thực hiện các hành động trong thẻ <target>.
-unless: thuộc tính này ngược lại với if
-description: dùng để mô tả ngắn gọn cho thẻ <target>

<target name="compile" depends="init">
<!- - Compile the java code - ->
<javac srcdir="${src}" destdir="${build}"/>
</target>

Như đã mô tả ở trên, vì thuộc tính depends=”init” nên thẻ <target> này phải được thực thi sau khi thẻ target có tên là “init” được thực thi.
Ta thấy có thẻ javac được sử dụng trong ví dụ trên, thẻ này có thể hiểu như một nhiệm vụ, những nhiệm vụ cần thực hiện được lồng trong thẻ target. Trong trường hợp này thư mục chứa mã nguồn (srcdir) được gán bằng biến src mà ta đã khai báo và thư mục đích mà ta cần xuất kết quả ra (destdir) được gán bằng biến build, và khi target thực thi thì javac sẽ biên dịch các tập tin java trong thư mục srcdir (tham chiếu đến biến src) và trả về kết quả là các tập tin class được đặt trong thư  mục destdir (tham chiếu đến biến build). Ngoài lệnh javax ra, ant còn hỗ trợ lệnh java để thực thi chương trình.

Sau khi hiểu rõ những tính năng của từng thẻ, hãy thực hiện ví dụ trên theo các bước sau:
-Chép mã nguồn ở ví dụ trên và lưu lại thành tập tin build.xml vào thư mục test 
-Tạo một file java với nội dung bất kì ví dụ như sau:

public class test {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

-Lưu tập tin java trên vào chung thư mục test
-Sau đó chạy dòng lệnh : ant –v
-Dòng lệnh này sẽ thực thi theo hướng dẫn trong tập tin build.xml, nó sẽ tạo ra thư mục build chứa tập tin class được biên dịch từ tập tin java. Tham số –v sẽ hiển thị thông tin trong quá trình lệnh ant thực thi (các tham số của lệnh ant tham khảo thêm tại phụ lục A).

4.Mô tả chi tiết về một đề án đặc trưng:
Khi bắt đầu tiến hành một đề án, thông thường ta sẽ tạo ra những thư mục sau :
-src: chứa các tập tin mã nguồn
-build: chứa các tập tin trả về sau khi biên dịch
-lib: chứa các tập tin dùng làm thư viện cho chương trình
Bây giờ ta sẽ tiến hành các bước sau để sử dụng Ant:
-Đầu tiên tạo tập tin AntDemo.java với nội dung như sau:

import javax.swing.*;
import java.awt.*;

public class AntDemo extends JFrame {
public AntDemo() {
super("Ant Demo");
ImageIcon icon = new ImageIcon("image.jpeg");
getContentPane().add(new JLabel(icon));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String args[]) {
new AntDemo();
}
}

-Tạo 3 thư mục src, build, lib trong thư mục test ta đã tạo ở trên, sau đó lưu tập tin AntDemo.java và image.jpeg vào thư mục src
-Bước tiếp theo ta sẽ tạo tập tin build.xml với nội dung như sau:

<?xml version="1.0"?>
<project name="AntDemo" default="all" basedir=".">
<property name="src" value="src"/>
<property name="build" value="build"/>
<property name="lib" value="lib"/>
<target name="all" depends="AntDemo" description="Builds the whole project">
<echo>Doing all</echo>
</target>
<target name="AntDemo" description="Builds the main AntDemo project">
<echo>Doing AntDemo</echo>
<copy file="${src}/image.jpeg" tofile="${build}/image.jpeg"/>
<javac srcdir="${src}" destdir="${build}"/>
</target>
</project>

-Lưu tập tin build.xml vào thư mục test, và chạy ant như phần trên. Kết quả thu được là:

Buildfile: build.xml
AntDemo:
[echo] Doing AntDemo
[copy] Copying 1 file to C:\docbook\docproj\src\items\ant\files\build
[javac] Compiling 1 source file to \blah\blah\blah\build
all:
[echo] Doing all
BUILD SUCCESSFUL
Total time: 2 seconds

Lưu ý: nếu muốn sử dụng các tham số trong câu lệnh java như : javac –v –listfiles … ta sẽ làm bằng cách gán cho các thuộc tính đó giá trị bằng “true”, ví dụ như muốn trong quá trình biên dịch muốn hiển thị danh sách các tập tin được biên dịch ta thêm vào thẻ javac ở trên thuộc tính sau: listfiles=”true”.
Trong ví dụ trên có một thẻ mới đó là thẻ <copy> , dùng để sao chép các tập tin vào các thư mục cần thiết, với thuộc tính srcdir chỉ định thư mục đang chứa tập tin cần sao chép, destdir chỉ định thư mục cần sao chép đến. Mặc định, ant chỉ sao chép lại những tập tin đã sao chép rồi khi chúng được thay đổi, nếu muốn ant luôn sao chép mỗi khi thực thi thì ta sẽ gán giá trị “true” cho thuộc tính overwrite cho thẻ copy.
Thẻ <echo> sẽ in nội dung ra màn hình thực thi.
Để hiểu rõ hơn ta sửa lại nội dung của tập tin AntDemo.java như sau:

public AntDemo() {
super("AntDemo");
ImageIcon icon = new ImageIcon("image.jpeg");
JButton exitButton = new JButton("Exit");
exitButton.addActionListener(new ExitControl());
JButton aboutButton = new JButton("About");
aboutButton.addActionListener(new AboutControl());
getContentPane().setLayout(new FlowLayout());
getContentPane().add(new JLabel(icon));
getContentPane().add(aboutButton);
getContentPane().add(exitButton);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}

Tiếp theo, tạo tập tin ExitControl.java với nội dung sau:

import java.awt.event.*;

public class ExitControl implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}

Tạo tập tin AboutControl.java:

import java.awt.event.*;
public class AboutControl implements ActionListener {
public void actionPerformed(ActionEvent e) {
new AboutPopup();
}
}

Tạo tập tin AboutPopup.java:

import javax.swing.*;
import java.awt.*;

public class AboutPopup extends JFrame {
public AboutPopup() {
super("About");
String message = "\n";
message+="Bye bye world ???";
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(new Dimension(300,300));
JTextPane messagePane = new JTextPane();
messagePane.setBackground(Color.BLACK);
messagePane.setForeground(Color.GRAY);
messagePane.setEditable(false);
messagePane.setText(message);
getContentPane().add(messagePane);
setResizable(false);
setVisible(true);
}
}

Lưu tất cả các tập tin java vào thư mục src và sửa đổi lại tập tin build.xml như sau:
-Ở phần thẻ target AntDemo:

<target name="AntDemo" depends="AboutControl,ExitControl"
description="Builds the main AntDemo project">
<echo>Doing AntDemo</echo>
<copy file="${src}/image.jpeg" tofile="${build}/image.jpeg"/>
<javac srcdir="${src}" destdir="${build}" includes="AntDemo.java"/>
</target>

Ta đã thêm vào depends="AboutControl,ExitControl" để báo cho ant biết rằng thẻ target này sẽ được thực thi sau khi thẻ target có tên AboutControl và ExitControl được thực thi. Ngoài ra trong thẻ javac có thêm thuộc tính includes để chỉ ra cho ant biết tập tin nào cần được biên dịch, trong ví dụ trên ta báo cho ant biết rằng chỉ biên dịch tập tin AntDemo.java trong thư mục được chỉ định trong thuộc tính srcdir. Giá trị của includes có thể là danh sách các tập tin cần được biên dịch, nếu bỏ qua includes thì ant sẽ biên dịch tất cả.
-Vì thẻ target AntDemo có thuộc tính depends=”AboutControl,ExitControl” nên ta sẽ thêm vào 2 thẻ target mới với tên tương ứng như sau:

<target name="AboutControl" depends="AboutPopup" 
description="Builds AboutControl">
<echo>Doing AboutControl</echo>
<javac srcdir="${src}" destdir="${build}" includes="AboutControl.java"/>
</target>

<target name="ExitControl" description="Builds ExitControl">
<echo>Doing ExitControl</echo>
<javac srcdir="${src}" destdir="${build}" includes="ExitControl.java"/>
</target>

Thẻ target AboutControl có thuộc tính depends=”AboutPopup” để chỉ ra rằng thẻ target này sẽ được thực thi sau khi thẻ target AboutPopup được thực thi. Vì thế ta sẽ tạo thêm thẻ target có tên là AboutPopup như sau:

<target name="AboutPopup" description="Builds AboutPopup">
<echo>Doing AboutPopup</echo>
<javac srcdir="${src}" destdir="${build}" includes="AboutPopup.java"/>
</target>

-Cuối cùng ta sẽ thêm vào thẻ target Clean, được dùng để xoá tập tin trong thư mục được chỉ định như sau:

<target name="Clean" description="Removes previous build">
<delete verbose="true">
<fileset dir="${build}"/>
</delete>
</target>

Trong thẻ target trên ta có thẻ <delete> để chỉ ra rằng ta muốn xoá, thuộc tính verbose=”true” sẽ in ra danh sách các tập tin được xóa khi thực thi thẻ target. Thẻ <fileset> dùng để chỉ ra thư mục chứa các tập tin cần xóa với thuộc tính dir chứa đường dẫn đến thư mục đó. Nếu muốn xóa cả thư mục đó thì gán giá trị “true” cho thuộc tính includeEmptyDirs. (tham khảo thêm về thẻ fileset trong phần phụ lục B)
Để xoá các tập tin đã biên dịch trước kia, ta chạy lệnh sau:
ant Clean

Kết quả như sau:

Buildfile: build.xml
Clean:
[delete] Deleting 2 files from \blah\blah\build
[delete] Deleting \blah\blah\blah\build\AntDemo.class
[delete] Deleting \blah\blah\blah\build\image.jpeg
BUILD SUCCESSFUL
Total time: 1 second

Tiếp theo ta sẽ thực thi lệnh ant không cần tham số nào cả, do đó ant sẽ thực thi thẻ target được quy định trong thuộc tính default được nêu ở trên
Kết quả như sau:

Buildfile: build.xml
AboutPopup:
[echo] Doing AboutPopup
[javac] Compiling 1 source file to \blah\blah\blah\build
AboutControl:
[echo] Doing AboutControl
[javac] Compiling 1 source file to \blah\blah\blah\build
ExitControl:
[echo] Doing ExitControl
[javac] Compiling 1 source file to \blah\blah\blah\build
AntDemo:
[echo] Doing AntDemo
[copy] Copying 1 file to \blah\blah\blah\build
[javac] Compiling 1 source file to \blah\blah\blah\build
all:
[echo] Doing all
BUILD SUCCESSFUL
Total time: 4 seconds

Có thể thấy được các thẻ target được thực thi theo thứ tự được quy định bởi thuộc tính depends trong các thẻ target.

5.Nâng cao:
Để hỗ trợ cho việc điều khiển sự lưu thông (flow of control) của tiến trình, ant cung cấp cú pháp gọi những thẻ <target> khác bằng cú pháp sau:
<antcall target=”target-name”>
Giả sử ta muốn tiến trình thực hiện theo cú pháp trong Java như sau:

if( condition ) {
if( inner-condition ) {
A
} else {
B
}
} else {
C
}

Trong ant ta có thể viết như sau:

<?xml version="1.0"?>
<project name="Flow.Of.Control" default="nested-if" basedir=".">
<target name="nested-if">
<condition property="condition">
<available file="fileone"/>
</condition>
<antcall target="then"/>
<antcall target="else"/>
</target>
<target name="then" if="condition">
<echo>THEN BODY EXECUTED</echo>
<condition property="inner-condition">
<available file="filetwo"/>
</condition>
<antcall target="inner.then"/>
<antcall target="inner.else"/>
</target>
<target name="inner.then" if="inner-condition">
<echo>INNER THEN BODY EXECUTED</echo>
</target>
<target name="inner.else" unless="inner-condition">
<echo>INNER ELSE BODY EXECUTED</echo>
</target>
<target name="else" unless="condition">
<echo>ELSE BODY EXECUTED</echo>
</target>
</project>
Trong cú pháp trên, ta sử dụng thuộc tính if và unless để xét điều kiện, đầu tiên “nested-if” được gọi, nó sẽ kiểm tra xem tập tin có tên “fileone” có tồn tại hay không, nếu tồn tại thì biến condition sẽ được gán giá trị, sau đó gọi thẻ target “then” và thẻ target “else”. Khi thẻ target “then” được gọi, nó sẽ kiểm tra điều kiện thông qua biến property có tên là “condition”, nếu biến này được gán giá trị thì sẽ thực hiện các hành động trong thẻ target này. Trong thẻ target “then” lại xét xem tập tin “filetwo” có tồn tại hay không, nếu tồn tại thì biến inner-condition sẽ được gán giá trị, sau đó gọi thẻ target “inner.then” và thẻ target “inner.else”. Quá trình trên có thể được mô tả bằng sơ đồ sau:

Phụ lục A

Tham sốMô tả
-vHiển thị thông tin trong quá trình thực thi ant
-projecthelpHiển thị thông tin về các thẻ target hiện có trong tập tin build.xml

Phụ lục B

FileSet được dùng như một bộ lọc để chỉ ra những tập tin thoả mãn những điều kiện nào đó bằng cách dùng một hay nhiều mẫu được chỉ định. FileList là danh sách những tập tin thoả mãn điều kiện, FileSet dùng PatternSets và Patterns để định nghĩa những hành động cần thực hiện.
-dấu ? đại diện cho 1 kí tự bất kì
-dấu * đại diện cho nhiều kí tự bất kì hay không có kí tự nào cả 
-dấu ** đại diện cho nhiều thư mục bất kì hay không có thư mục nào cả 
Fileset phải chỉ định thư mục gốc (Base directory) thông qua thuộc tính dir, để từ đó tính toán các đường dẫn có liên quan. 
<fileset dir="BASEDIR"/>
Hay:
<fileset dir="BASEDIR">
</fileset>
Ví du 1:
<fileset. dir="." includes="**/*.blah **/*.bleh"
Trong ví dụ trên, bao gồm tất cả các tập tin có đuôi là .blah hay .bleh trong thư mục hiện tại và trong các thư mục con của thư mục hiện tại.

Ví dụ 2:
<fileset. dir=".">
<include. name="**/*bl*"/>
<exclude name="**/blah/*"/>
</fileset>
Ta có thể viết cú pháp như trên để chỉ ra rằng sẽ bao gồm tất cả các tập tin có tên chứa chuỗi “bl” trong thư mục hiện tại và trong các thư mục con. Và sẽ loại bỏ những tập tin nằm trong thư mục có tên “blah” trong thư mục hiện tại hay trong các thư mục con. Chú ý rằng ta sử dụng thẻ <include> và thẻ <exclude> với thuộc tính name thay vì dùng thuộc tính includes và excludes trong thẻ <fileset>
Vậy khi ta cần dùng nhiều lần 1 mẫu (pattern) nào đó lại phải viết lại nhiều lần chăng, câu trả lời đó là dùng thẻ <patternset> với cú pháp sau:

<fileset dir=".">
<patternset id="blah">
<include name="**/*bl*"/>
<exclude name="**/blah/*"/>
</patternset>
</fileset>

Sau khi khai báo thuộc tính id của thẻ patternset, nếu sau này cần dùng đến ta chỉ cần tham chiếu đến thẻ patternset trên với cú pháp sau:
<fileset dir=".">
<patterset refid="blah"/>
</fileset>

Ta cũng có thể dùng thuộc tính if và unless trong thẻ include và exclude để đưa ra điều kiện thêm vào (include) hay bỏ đi (exclude).
Ví dụ 3:
<fileset dir=".">
<include name="**/extensions/*.java" if="version.professional"/>
</fileset>

Trong ví dụ này chỉ ra rằng sẽ bao gồm tất cả tập tin .java trong thư mục extensions nếu biến version.professional được gán giá trị bất kì.
Ví dụ 4:
<fileset dir=".">
<exclude name="chinese.lang" unless="language.chinese"/>
</fileset>

Trong ví dụ này sẽ loại bỏ tập tin chinese.lang nếu biến language.chinese không được gán giá trị nào cả.
Ngoài ra, nếu như giá trị của thuộc tính name của thẻ include hay exclude quá nhiều ta có thể tạo danh sách các tập tin trong một tập tin khác và tham chiếu đến tập tin đó bằng bằng thẻ <includesfile>,<excludesfile> với cú pháp sau:

<fileset dir=".">
<includesfile name="some.file"/>
</fileset>

Tập tin some.file có thể có nội dung như sau:
bl?h.bl?h
*.java
FileList: tương tự FileSet nhưng không hỗ trợ những kí tự đại diện ? và *. Ta phải liệt kê hết những tập tin ngăn cách nhau bởi dấu phẩy hay khoảng trắng trong thuộc tính files.Và ta cũng có thể đặt id và tham chiếu đến FileList đó thông qua thuộc tính refid như sau:
<filelist id="blah" dir="." files="blah.blah bleh.bleh"/>

Tham chiếu đến FileList:
<filelist refid="blah"/>

JSP Toàn tập (phần 2) - SESSION COOKIES

SESSION – KHÁI NIỆM

HTTP là một stateless protocol. Session tracking là cơ chế để hỗ trợ việc lưu trữ thông tin trạng thái qua các thành phần của Servlet API. Session là cách giải quyết việc lưu trữ thông tin – trạng thái trong Web application. Session hỗ trợ Web Server phân biệt các users khác nhau. Session được thiết kế như thành phần của Server giải quyết stateless theo 03 cách Cookies URL – Writing Hidden form fields Session được khởi tạo khi user truy cập hay login vào trang Web. Thời gian tồn tại của Session: Đóng Web browser. Logout khỏi ứng dụng Web. Time out.

COOKIES

Cookies là các thông tin text đơn giản để Web server gửi tới browser. Các request trong lần tiếp theo sẽ đính kèm cookies. Server dùng cookies để sử dụng các thông tin của clients. Mỗi browser có thể chứa 20 cookies/ site và tối đa là 300 cookies. Kích thước tối đa của cookies là 4Kb. Mỗi Cookies có 01 SessionID tương ứng. Ưu điểm Xác nhận user trong suốt quá trình giao dịch điện tử Hỗ trợ việc checkLogin (low security) Hỗ trợ việc cung cấp thông tin ưa thích cho người dùng Hỗ trợ cho việc quảng cáo trên trang web.Có tính bảo mật Nhược điểm Xâm phạm tự do riêng tư Gây ra các spam về mail và trang web. Browser phải hỗ trợ hay cho phép cookies.Kích thước giới hạn.

SERVLET COOKIES

Tạo cookies bằng cách gọi Cookies constructor Các phương thức API hỗ trợ get/setVersion (chuỗi): version của protocol Cookies int get/setMaxAge(int):thời gian cookies expires (s) String get/setName(tên cookies) String get/setPath(String path): đường dẫn chứa cookies. “/” tất cả trang String get/setValue(String):các giá trị kết hợp với cookies response.addCookies: insert cookies request.getCookies: đọc cookies từ client
VÍ DỤ Lưu cookies trên máy và hiển thị thông tin <%@ page import="javax.servlet.http.Cookie" %> <html.> <head.> <title.>Cookies Demo</title.> </head.> <body.> <h1.>Cookies</h1.> <% Cookie[] allCookies = request.getCookies(); Cookie ourCookie=null; if(allCookies!=null){for(int i=0; i<allCookies.length; i++) { if(allCookies.getName().equals("TestCookies")) { ourCookie = allCookies; }}} if(ourCookie==null){ Cookie cookie=new Cookie("TestCookies", "welcome Cookie"); cookie.setPath("/"); response.addCookie(cookie);%> A cookie has beean added to your machine! Select refresh to see the details of the cookie. <% } else {%> The following cookie was added earlier to your machine: Version: <%= ourCookie.getVersion() %> Name: <%= ourCookie.getName() %> Value: <%= ourCookie.getValue() %> MaxAge: <%= ourCookie.getMaxAge() %> <% } %> </body.> </html.>
SESSION TRACKING in SERVLETS Servlet cung cấp một nơi để lưu trữ các thông tin cần thiết về session. Session Tracking: Xác định đối tượng HttpSession có liên quan đến request hiện hành: Sử dụng phương thức getSession của HttpServletRequest Hệ thống thực hiện trích xuất userID từ cookie hay dữ liệu attach trong URL để tạo ra đối tương HttpSession Xác định xem user đã tồn tại hay chưa? Nếu chưa tồn tại thì thực hiện tạo mới dùng phương thức getSession(true). HttpSession tồn tại trên server và cho phép lưu trữ key và các giá trị Ví dụ: HttpSession session = request.getSession(true); Session Tracking (tt) Tìm kiếm và thiết lập các thông tin trong Session Sử dụng phương thức (Object) session.getValue(“tên thuộc tính”) (2.1) hay session.getAttribute(“tên thuộc tính”) (2.2) để lấy giá trị của thuộc tính lưu trữ. Sử dụng phương thức (String[]) session.getValueNames() hay session.getAttributeNames() để lấy tất cả tên thuộc tính có trong Session Các phương thức hỗ trợ khác setAttribute – putValue(“tên thuộc tính”, giá trị) removeAttribute – removeValue(“tên thuộc tính”) String getId() boolean isNew(): false nếu tồn tại Session long getCreateTime() hay long getLastAccessedTime() int getMaxInactiveInterval() và setMaxInactiveInterval(int seconds) VÍ DỤ Servlet resp.setContentType("text/html"); PrintWriter pw=resp.getWriter(); pw.println("<html><head>Cart</head><body>"); HttpSession session=req.getSession(true); CartBean shoppingCart=(CartBean)session.getAttribute("cart"); if(shoppingCart==null){ shoppingCart=new CartBean();} String action=req.getParameter("mode"); String bookTitle=req.getParameter("lstBook"); if(action.equals("add")){ shoppingCart.addBook(bookTitle); RequestDispatcher rd=getServletContext().getRequestDispatcher("/"); rd.forward(req, resp);} if(action.equals("view")){ int count=0; String booktitle; pw.println("Your Cart contents:
"); Vector bookList=shoppingCart.getContents(); Enumeration item=bookList.elements(); pw.println("<form action='CartServlet?mode=remove' method='post'>"); pw.println( "<table border='1' cellpadding='2' cellspacing='0' width='395' " ); pw.println( "bordercolor='#666666' style='border-collapse: collapse'>" ); pw.println( "" ); pw.println( "Order " ); pw.println( "" ); pw.println( "Books title" ); pw.println( "" ); pw.println( "Action " ); pw.println( "" ); while(item.hasMoreElements()){ ++count; booktitle=(String)item.nextElement(); pw.println( " " + count + "" ); pw.println( "" + booktitle + ""); pw.println( "" ); pw.println( "<input type='checkbox' name='rmv' value='" + booktitle + "'> " );} pw.println( " " ); pw.println( "Choose next" ); pw.println( " <input. type='submit' value='Remove'> </table.></form.>" );} if(action.equals("remove")){ String title[]=req.getParameterValues("rmv"); if(title==null){ title=new String[0];} try{ for(int i=0; i<title.length; i++) { shoppingCart.removeBook(title);}}catch(Exception e){} RequestDispatcher rd=getServletContext().getRequestDispatcher("/CartServlet?mode=view"); rd.forward(req, resp);} session.setAttribute("cart", shoppingCart); pw.println("</body.></html.>"); pw.close();

JSP Toàn tập (phần 1) - Servlet

CÁC THUẬT NGỮ Web page: là tập tin văn bản dùng ngôn ngữ đánh giấu bằng thẻ (markup) để trình bày/ hiển thị dữ liệu Web Browser: là chương trình phần mềm duyệt web bằng cách kết nối với web server để lấy web page và trình bày web page thông qua các thẻ (tag) đánh dấu. Các web browser sử dụng phổ biến là Internet Explorer, Netscape Navigator, FireFox, … Web Server: là chương trình phần mềm đáp ứng/ cung cấp web page được yêu cầu từ Web Browser Protocol: là cách thức tuân theo qui chuẩn để thực hiện kết nối giữa các đối tượng. HTTP – Hyper Text Transfer Protocol: protocol cho phép chuyển thông tin và dữ liệu giữa Web Server và Protocol
Web Application: là chương trình phần mềm tập hợp các trang web và các phương thức xử lý nhằm phục vụ công việc cụ thể. Chương trình phần mềm này có thể truy cập và đáp ứng yêu cầu của người sử dụng thông qua mạng
URL – Uniform Resource Locator: là địa chỉ truy cập trang Web server. Request: là việc gửi yêu cầu của Web Browser đến Web Server. Response: là kết quả hồi đáp của Web Server đến Web Browser.
ACTIVE SERVER PAGE – ASP Là ngôn ngữ dạng script có khả năng tích hợp JavaScript, các script của Microsoft (Response và Request ...), Html, JScript ... Tên file có định dạng *.asp (aspx) và được thực thi trên IIS và PWS. Khắc phục nhược điểm làm tốn bộ nhớ của CGI thông qua các thư viện dll. Hỗ trợ cho phát triển ứng dụng nhanh chóng, hiệu quả và có khả năng tái sử dụng các thành phần (component) Nhược điểm: chỉ thực thi trên môi trường của MS Windows
SERVLET
Chương trình Java thực thi trên Web Server và JVM. (khắc phục các nhược điểm của CGI) Lớp trung gian giữa Web Browser hay HTTP Client với các ứng dụng hay CSDL trên HTTP server. Thực hiện bằng cơ chế Web Application. servlet đón nhận request từ client thực hiện xử lý theo các phương thức/services được implement trong servlet và kết xuất response trả về theo định dạng của trang HTML.
Chức năng: đọc dữ liệu HTML form, kết nối CSDL, vận hành cookies, sessions, bảo mật ... Cung cấp các thành phần API để thực hiện tương tác với các ứng dụng Web
Nhược điểm: Phức tạp không uyển chuyển (flexibility) Hoạt động chậm (nhúng code HTML và cần biên dịch thông qua JVM)
CÁC PHƯƠNG THỨC CƠ BẢN
init: khởi tạo cho servlet
Thực hiện duy nhất trong lần gọi đầu tiên và không được thực hiện lại khi có request từ client. Khi không có tham số: servlet không cần đọc thông tin truyền giữa các server public void init() throws ServletException Có tham số ServletConfig: đọc các thông tin liên quan đến việc khởi tại như thông tin xác thực (username, password) … public void init(ServletConfig config) throws Servlet Exception
Service:
Được tạo khi có request từ client thông qua threads Phương thức service kiểm tra loại http request (GET, POST, DELETE, …) để gọi các services tương ứng như doXXX …
Các phương thức thường dùng:
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException public void doXXX(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException Destroy:
Hủy servlet trước khi giải phóng (unload) servlet ra khỏi bộ nhớ Cho phép xử lý việc đóng kết nối CSDL, đóng các tác vụ đang xử lý … public void destroy() PrintWriter:
Hỗ trợ việc kết xuất/ hiển thị ra HTML Đối tượng kết xuất được lấy từ response: response.getWriter()

CÁCH TẠO SERVLET

Step 1: import các gói hỗ trợ trong xây dựng và thực thi servlet (set CLASSPATH đến tập tin servlet-api.jar trong thư mục CATALINA_HOME[\common]\lib) javax.servlet.* Javax.servlet.http.* Step 2: Tạo java class extends HttpServlet (loại public) Step 3: Implement các phương thức init, destroy (nếu cần) và doXXX cùng với phương thức của đối tượng PrintWriter để kết xuất lên trang Web. Step 4: thực hiện tạo gói module Web, deploy và thực thi ứng dụng

VÍ DỤ

Xây dựng ứng dụng xuất màn hình Web Browser “Hello World” dùng Servlet import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Hello extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{ response.setContentType("text/html"); PrintWriter out=response.getWriter(); out.println("<html><head>Hello Servlet</head><body>"); out.println("

Hello Servlet

"); out.println("

Welcome to JSP course

"); out.println("</body></html>");} public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{ doGet(request, response);}}

THỰC THI ỨNG DỤNG WEB

Install JDK (phiên bản 1.4.x, 1.5.x, 1.6.x) (tải trên trang web của SUN) và set biến môi trường JAVA_HOME là tên thư mục được install JDK Install Web Application Server – Tomcat (phiên bản 4.x, 5.x, 6.x) (tải trên trang web của Apache) và set biến môi trường CATALINE_HOME là tên thư mục cài đặt Tomcat Thiết lập biến môi trường CLASSPATH đến các thư viện (tập tin jar) của các thư mục cài đặt (thông thường là thư mục lib) Thiết lập PATH đến các thư mục bin của chương trình đã cài đặt để có thể sử dụng lệnh trên màn hình console trong Windows Sử dụng công cụ soạn thảo chương trình như Notepad, JCreatorPro, Eclipse, MyEclipse …

TẠO ỨNG DỤNG WEB

Tạo cấu trúc thư mục theo cấu trúc của ứng dụng Web Tạo class Servlet, biên dịch và đặt tại thư mục classes Tạo cấu trúc file deployment descriptor web.xml theo cấu trúc Tạo file chạy trên Web Browser để gọi Servlet
web.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Hello World</display-name> <servlet> <servlet-name>Hello</servlet-name> <display-name>HelloServlet</display-name> <servlet-class>Hello</servlet-class> </servlet> <servlet-mapping> <servlet-name>Hello</servlet-name> <url-pattern>/Hello</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app>

index.html

<html> <head> Hello Servlet </head> <body>

First Servlet

<form action="/Hello/Hello" method="get"> <input type="submit" value="Submit"> </form>
</body> </html>
Khởi tạo giá trị ban đầu cho Servlet Dùng tag <init-param> cùng với tag <param-name>và <param-value> trong thành phần của tag <servlet> trong web.xml <servlet> … <servlet-class>tên class</servlet-class> <init-param> <param-name>tên tham số</param-name> <param-value>giá trị</param-value> </init-param> </servlet> Dùng phương thức getInitParameter(“tên tham số”) của đối tượng config của phương thức init để lấy tham số.
Ví dụ: config.getInitParameter(“pInit”);

JSP Toàn tập (phần 1) - Servlet và JSP

1. Triển khai ứng dụng Web Servlet
- Các lớp Servlet phải cài đặt giao diện javax.servlet.Servlet. Servlet API cung cấp hai lớp cài đặt từ giao diện này, ta phải thừa kế chúng để tạo ra servlet.
•GenericServlet cung cấp các chức năng cơ bản, độc lập giao thức, để tạo Servlet. Thừa kế lớp này để tạo các lớp Servlet cho dịch vụ không phải HTTP.
•HttpServlet thừa kế GenericServlet và thêm vào các chức năng cho riêng HTTP. Đa số các lớp Servlet của chúng ta sẽ thừa kế HttpServlet.
Để biên dịch các lớp Servlet, cần gói servlet-api.jar (trong <TOMCAT_HOME>/common/lib) trong đường dẫn.
- Triển khai ứng dụng Web Servlet trên Eclipse với cấu trúc thư mục sau (xem trong tài nguyên của bài tập).

- Hiểu rõ Servlet giúp hiểu rõ JSP. Cần chú ý các vấn đề sau khi triển khai project:
+ Giao diện RequestDispatcher dùng để forward (hoặc include) đến trang khác:
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/NextServlet");
dispatcher.forward( req, res );
hoặc:
// req kiểu HttpServletRequest là đối số truyền cho phương thức
RequestDispatcher dispatcher = req.getRequestDispatcher( "/NextServlet" );
dispatcher.forward( req, res );
+ Khi forward, đối tượng request được Servlet trước truyền đến Servlet sau. Với các trị khác, truyền như sau:
Servlet truyền:
ServletContext context = getServletContext();
context.setAttribute( "accountID", "3478217" );
Servlet nhận:
ServletContext context = getServletContext();
Object obj = context.getAttribute( "accountID" );
String value = obj.toString();
+ Cách ánh xạ giữa lớp Servlet và URL gọi Servlet trong tập tin DD:
<servlet>
<servlet-name>ConvertServlet</servlet-name>
<servlet-class>examples.ConvertServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ConvertServlet</servlet-name>
<url-pattern>/ConvertServlet</url-pattern>
</servlet-mapping>
+ Lớp HttpServlet thừa kế lớp GenericServlet. Đa số các lớp Servlet ta phát triển đều thừa kế lớp HttpServlet, cần định nghĩa lại phương thức service() hoặc một hay nhiều phương thức xử lý yêu cầu chỉ định HTTP sau:
•doGet
•doPost
•doPut
•doDelete
•doHead
•doOptions
•doTrace
Mỗi phương thức này nhận các đối số sau:
•HttpServletRequest, chứa thông tin HTTP header và thông tin yêu cầu khác của client.
•HttpServletResponse, cho phép trả về HTML đến browser.
Khi gọi ConnvertServlet lần đầu, service() sẽ điều hướng đến doGet() và hiển thị form nhập. Sau đó, lời gọi từ form nhập dùng phương thức POST nên ConvertServlet sẽ điều hướng đến doPost(), giải quyết yêu cầu.

2. Quan hệ giữa JSP và Servlet
- Bài tập sau tách rời các phần khác nhau của Servlet, nhằm làm nổi bật vai trò của JSP, đồng thời giới thiệu những khái niệm ban đầu cho mô hình MVC:
Trong ServletProjectTrong JSPProject
Hiển thị form trong doGet()Form nhập HTML đầu vào (front-end) của ứng dụng.
Các helper method.Tách thành lớp riêng, sau này sẽ chuyển thành các business method chứa trong các đơn thể riêng gọi là Bean.
Tạo kết quả xuất động trong doPost() Trang JSP kết hợp giữa HTML template tĩnh và dữ liệu động tính được nhờ helper method.
Giao diện RequestDispatcherDùng action chuẩn <jsp:forward>
- Triển khai ứng dụng Web JSP trên Eclipse với cấu trúc thư mục sau (xem trong tài nguyên của bài tập).

- Mặc dù có thể gọi trực tiếp các trang JSP, nhưng bài tập này dùng cách ánh xạ trang JSP với URL gọi, nhằm nhấn mạnh mối liên hệ chặt chẽ giữa JSP và Servlet.
<servlet>
<servlet-name>convert</servlet-name>
<jsp-file>/convert.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>convert</servlet-name>
<url-pattern>/Convert</url-pattern>
</servlet-mapping>

Dữ liệu nhập đúngDữ liệu nhập sai
- Sau khi gọi các trang JSP ta có thể xem các tập tin servlet sinh từ JSP tương ứng, trong source folder work. Đồng thời có thể đặt BreakPoint trong tập tin này để tiến hành debug:

JSTL Cơ Bản (Phần 2)

I.Expressions Datasource kết nối loại 4 <sql:setDataSource var="hieu" driver="com.microsoft.jdbc.sqlserver.SQLServerDriver" url="jdbc:microsoft:sqlserver://localhost:1433;databaseName=AAL;" user="sa" password=""/> Truy vấn không tham số <sql:query var="categories" dataSource="${hieu}">
select *
from Categories </sql:query>

Truy vấn có tham số <sql:query var="categories" dataSource="${hieu}">
select * from Categories where CateID= ?
<sql:param value="${param.CatID}"/> </sql:query>

Khởi tạo một biến:
<c:set var='user' value='royBoy'/> Update <sql:update [var] [scope] [dataSource]> SQL Statement </sql:update> ví dụ: <sql:update var="updateResult"> UPDATE CUSTOMERS SET PHONE_NUMBER = ? WHERE NAME = ? <sql:param value='${param.phone}'/> <sql:param value='${param.name}'/> </sql:update> Insert <sql:update [var] [scope] [dataSource]> SQL Statement </sql:update> ví dụ: <sql:update var="updateResult"> INSERT INTO CUSTOMERS(phone,name) VALUES(?,?) <sql:param value='${param.phone}'/> <sql:param value='${param.name}'/> </sql:update> Transaction
<sql:transaction [dataSource] [isolation]>
<sql:query> and <sql:update> actions
<sql:transaction>