Bảng 6.1 Mô hình Record Store Mỗi record có một trường Record ID có kiểu dữ liệu integer, đóng vai trò như khóa chính trong bảng dữ liệu và một trường “Data” là một mảng các bytes để ch
Trang 1Java Mobile
Thay thế cho hệ thống file, thiết bị MIDP lưu trữ thông tin qua các record stores Chúng ta có thể xem các record store này như một bảng dữ liệu gồm nhiều dòng record Mỗi dòng record có một số ID và gồm một mảng các bytes Người ta thường xem record store như một cấu trúc file phẳng (flat-file)
Bảng 6.1 Mô hình Record Store
Mỗi record có một trường Record ID có kiểu dữ liệu integer, đóng vai trò như khóa chính trong bảng dữ liệu và một trường “Data” là một mảng các bytes để chứa
dữ liệu Một tập hợp các record này được gọi là một record store (tương ứng với một bảng dữ liệu) Một ứng dụng MIDlet có thể có bao nhiêu record store tùy ý (kể
cả không có), mỗi record store được phân biệt bởi tên định danh duy nhất Mở rộng
ra, các Record Stores của các ứng dụng MIDlet cùng chung một bộ (suite) thì cũng phải có tên phân biệt
Tên của record store có thể chứa đến 32 ký tự Unicode (16 bits) và tên này phân biệt hoa thường
Các ứng dụng MIDlet không những truy xuất được các record stores do nó tạo ra
mà còn truy xuất được các record stores do các ứng dụng khác trong cùng một bộ (MIDlet Suite) tạo ra Đây là một lý do khiến các nhà phát triển đặt ra khái niệm MIDlet Suite, tuy nhiên các MIDlet không cùng một suite không thể truy xuất Record Suite của nhau Đây là một hạn chế của Record Store khi so sánh với hệ thống file trên các thiết bị khác
Một record store quản lý hai thông số để quản lý việc truy cập dữ liệu, hai thông
số này tự động được cập nhật khi có record được thêm, xóa hay sửa:
Trang 2Java Mobile
• Số Version : đây là một giá trị integer Tuy nhiên, có một điều không may là giá trị khởi điểm của version không được định nghĩa bởi API Nếu chúng ta cần biết giá trị ban đầu này, ngay sau khi tạo record mới ta có thể gọi hàm getVersion()
• Ngày tháng: là một số long, thể hiện số millisecond tính từ nửa đêm ngày 1/1/1970 Chúng ta có thể lấy ra thông số này bằng hàm getLastModified() Các yêu cầu xử lý record store được thiết bị đồng bộ hóa tự động: khi có hai hay nhiều yêu cầu xử lý thì yêu cầu đến trước được xử lý trước và các yêu cầu xử lý khác sẽ được đặt vào hàng đợi
Các hàm xử lý Record Store thông dụng:
Mở một record store, tùy chọn tạo mới nếu record store chưa tồn tại
static void deleteRecordStore(String
rcdStoreName)
Xóa record store
static String[] listRecordStore() Liệt kê các record stores trong MIDlet
Suite
int addRecord(byte[] data, int offset, int
numBytes)
Thêm một record
void setRecord(int recordID,byte[]
newData,int offset, int numBytes)
Thay đổi nội dung một record
void deleteRecord(int RecordID) Xóa một record khỏi record store
byte[] getRecord(int RecordID) Lấy mảng byte chứa nội dung record
Trang 3Java Mobile
int getRecord(int RecordID, byte[]
buffer, int offset)
Lấy nội dung record từ vị trí offset đưa vào mảng byte
int getRecordSize(int RecordID) Lấy kích thước của record
int getNextRecordID() Lấy ID của record tiếp theo int getNumRecords() Lấy số lượng Record trong Record
Store
int getSizeAvailable() Lấy dung lượng còn lại (bytes) được
phép dùng bởi Record Store
Ghi chú: Record ID được tính bắt đầu từ 1 chứ không phải từ 0 như chỉ số mảng
Sau đây là một ví dụ về việc nhập xuất dữ liệu thông qua record store bằng kỹ thuật stream Ví dụ đã được tinh giản và chỉ trình bày những phân đoạn chính
//Import các thư viện cần thiết
import java.io.*;
import javax.microedition.midlet.*;
Trang 4Java Mobile
import javax.microedition.rms.*;
public class ReadWriteStreams extends MIDlet {
private RecordStore rs = null; // Record Store
static final String REC_STORE = "db_1"; // Tên của Record Store
public ReadWriteStreams() {
openRecStore(); // Hàm tạo Record Store
writeTestData(); // Hàm ghi dữ liệu
readStream(); // Hàm đọc dữ liệu
closeRecStore(); // Đóng Record Store
deleteRecStore(); // Xóa Record Store
} …
public void startApp() {
// Không có giao diện, chương trình sẽ ghi, đọc dữ liệu và thoát
destroyApp(false);
notifyDestroyed();
} public void openRecStore() {
try {
// Tạo record store mới nếu chưa tồn tại
rs = RecordStore.openRecordStore(REC_STORE, true );
} catch (Exception e) {
//Xuất thông báo lỗi
} }
public void closeRecStore() {
rs.closeRecordStore();
Trang 5Java Mobile
} catch (Exception e) {
//Xuất thông báo lỗi
} }
public void deleteRecStore(){
if (RecordStore.listRecordStores() != null){
try{
RecordStore.deleteRecordStore(REC_STORE);
} catch (Exception e){
//Xuất thông báo lỗi
} }
} /* -
* Tạo 3 mảng để mô phỏng việc ghi dữ liệu
* -*/
public void writeTestData() {
String[] strings = {"String 1", "String 2"};
boolean[] booleans = {false, true};
int[] integers = {1 , 2};
writeStream(strings, booleans, integers);
} /* -
* Viết vào record store dùng stream
* -*/
public void writeStream(String[] sData, boolean[] bData, int[] iData) {
try{
Trang 6Java Mobile
/* Tạo stream để viết dữ liệu, ở đây ta tạo ra 2 streams, một stream có tác dụng như buffer, stream còn lại để ghi dữ liệu vào Record*/
//Buffer
ByteArrayOutputStream strmBytes = new ByteArrayOutputStream();
DataOutputStream strmDataType = new DataOutputStream(strmBytes);
byte[] record;
//Ba mảng có kích thước bằng nhau
for (int i = 0; i < sData.length; i++) {
Trang 7Java Mobile
catch (Exception e){
//Xuất các thông báo lỗi
} }
byte[] recData = new byte[50];
// Tạo một stream làm buffer lấy dữ liệu từ mảng recData
ByteArrayInputStream strmBytes = new ByteArrayInputStream(recData);
// Tạo stream để xuất dữ liệu theo đúng khuôn dạng đã ghi
DataInputStream strmDataType = new DataInputStream(strmBytes);
for (int i = 1; i <= rs.getNumRecords(); i++){
// Lấy dữ liệu đưa vào mảng
rs.getRecord(i, recData, 0);
// Đọc lại theo đúng thứ tự đã ghi
System.out.println("Record #" + i);
System.out.println("UTF:" + strmDataType.readUTF()); System.out.println("Boolean: " +
strmDataType.readBoolean());
System.out.println("Int: " + strmDataType.readInt()); System.out.println(" -");
// Xóa dữ liệu buffer để bắt đầu ghi các phần tử tiếp theo
strmBytes.reset();
Trang 8Java Mobile
} strmBytes.close();
strmDataType.close();
} catch (Exception e){
//Xuất các thông báo lỗi
} }
… }
Ví dụ trên đã nêu một cách cơ bản nhất các bước cần thiết để ghi dữ liệu và truy xuất dữ liệu bằng record store Ví dụ đã được tinh giản và chỉ nêu các hàm thật cần thiết Việc mở, ghi record, đọc record, đóng và xóa record được thể hiện thành từng hàm riêng biệt để tiện theo dõi Trong ví dụ trên có một số điểm ta cần chú ý:
Chỉ số RecordID bắt đầu từ 1 chứ không phải từ 0 như chỉ số của các phần tử trong mảng Nếu ta cố gắng truy xuất phần tử số 0 sẽ phát sinh lỗi
Tương tự như các hàm truy xuất I/O khác của Java, các hàm liên quan đến Record Store cần phải được đưa vào trong khối try – catch vì các hàm này có thể phát sinh các exceptions
Trong ví dụ trên chúng ta dùng stream để ghi và xuất dữ liệu có khuôn dạng; do đó khi chúng ghi theo trình tự nào thì xuất cũng phải theo trình tự đó
Trang 9Java Mobile
6.2 Duyệt danh sách Record với RecordEnumeration
Ngay trên ví dụ vừa rồi, chúng ta duyệt qua danh sách các records trong record store bằng dòng lặp Đây là các tiếp cận được những lập trình viên nghĩ đến ban đầu
vì chúng ta đã nhận định các record như các dòng trong một bảng của CSDL Tuy nhiên, MIDP cung cấp cho chúng ta một công cụ thuận tiện và chính xác hơn để
duyệt qua các record trong một record store Chúng ta đề cập đến khái niệm “chính
xác hơn” ở đây vì lý do khi duyệt bằng vòng lặp thực chất có thể gây nên một
exception trong chương trình Giả sử chúng ta có 3 record đánh số từ 1 đến 3, vì lý
do nào đó chúng ta xóa record số 2 bằng phương thức deleteRecord(int RecordID) thì số 2 không bao giờ được sử dụng để gán làm RecordID cho một record khác Vậy khi ta duyệt bằng vòng lặp, giả sử i từ 1 đến 3, khi giá trị i=2 sẽ gây ra một lỗi exception Khi đó, chúng ta bắt buộc phải duyệt bằng công cụ enumeration Ngoài
ra, khi chúng ta sử dụng các chức năng lọc (filter) và sắp xếp (sort) các record, giá trị trả về sẽ là một RecordEnumeration vì các RecordID lúc này không tuân theo bất
// lấy thông tin record tiếp theo ra buffer
String str = new String(re.nextRecord());
//xử lý tùy theo yêu cầu
}
Trang 10Java Mobile
Hàm enumerateRecord nhận vào ba tham số:
• Tham số đầu là bộ lọc (filter), nếu không muốn lọc record ta để null
• Tham số thứ nhì là bộ sắp xếp (sort), nếu không muốn sắp xếp ta cũng để null Nếu cả hai tham số trên đều là null thì các record được lấy ra và sắp xếp theo thứ tự bất kỳ
• Tham số cuối cùng là một biến boolean bUpdate, nếu bUpdate là true thì khi danh sách các record có sự thay đổi (bị thêm, xóa hay sửa) biến RecordEnumeration sẽ tự cập nhật, nếu là false chúng ta phải tự cập nhật bằng hàm rebuild()
Sau đây là các hàm thông dụng nhất của lớp RecordEnumeration
Lớp javax.microedition.rms.RecordEnumeration
int numRecords() Số lượng records trong tập hợp byte[] nextRecord() Lấy record kế tiếp, duyệt theo thứ tự tiến
(forward) hay không?
bolean hasPrevioussElement() Kiểm tra còn record để tiếp tục duyệt lùi
(backward) hay không?
Trang 11Java Mobile
void rebuild() Tự cập nhật lại enumeration
Sau đây là một ví dụ của lớp có cài đặt interface RecordComparator:
public class Comparator implements RecordComparator {
public int compare(byte[] rec1, byte[] rec2) {
String str1 = new String(rec1), str2 = new String(rec2);
int result = str1.compareTo(str2);
Chúng ta cài đặt interface này bằng cách cung cấp hàm int compare(byte[],byte[]) Hàm này trả về 3 giá trị đặc biệt đã được định nghĩa trước là:
Trang 12Java Mobile
• RecordComparator.EQUIVALENT
• RecordComparator.PRECEDES
• RecordComparator.FOLLOWS Khi đó, để bắt đầu quá trình sắp xếp và xử lý dữ liệu ta tạo ra một đối tượng Comparator (lớp có cài đặt RecordComparator) và một RecordEnumeration:
// Tạo đối tượng Comparator để sắp xếp
Comparator comp = new Comparator();
// Tạo đối tượng enum, tham số thứ nhì là comparator
int compare (byte[] r1, byte[] r2) Tùy theo giá trị trả về của hàm mà record r1 sẽ được xếp trước record r2, xếp sau r2 hay 2 records được xem là “bằng nhau”
EQUIVALENT Record r1 và r2 được xem là bằng nhau, thực chất lúc này r1
được xếp trên r2 vì không có sự thay đổi thứ tự FOLLOWS Record r1 được xem là “lớn hơn” r2 và sẽ xếp sau
PRECEDES Record r1 được xem là “nhỏ hơn” r2 và sẽ xếp trước
Bảng 6.4 Các giá trị hằng để sắp xếp record
Trang 13Java Mobile
Lúc này, khi ta dùng hàm enumerateRecords(…) của lớp RecordStore, kết quả trả về sẽ là một tập hợp (enum) đã được sắp thứ tự và ta có thể thao tác trên tập hợp này một cách bình thường
Nguyên lý sort thực chất cũng khá đơn giản: đối với những record gồm nhiều trường (compound record), khi chúng ta có nhu cầu chỉ search trên một trường bất
kỳ thì trước nhất phải đọc trường đó của 2 record cần so sánh ra 2 biến trung gian Sau đó việc so sánh 2 records trên thực chất là việc so sánh 2 biến trung gian Tùy theo nhu cầu sort ta sẽ gán giá trị trả về cho hàm compare của lớp Comparator một cách tương ứng
6.4 Lọc record với RecordFilter
Ngoài chức năng sắp xếp các record, J2ME còn hỗ trợ chúng ta công cụ lọc các record Phần vừa rồi chúng ta đề cập đến việc sắp xếp các record và đưa vào tập hợp, quá trình này ứng dụng sẽ sắp xếp tất cả các records có trong record store Nếu chúng ta chỉ có nhu cầu lấy ra và sắp xếp các record thỏa mãn một yêu cầu nhất định nào đó (ví dụ trường năm sinh trong record phải nhỏ hơn 1990) thì ta phải dùng thêm công cụ RecordFilter Cũng giống như RecordComparator, RecordFilter
là một interface, khi ta có nhu cầu lọc record thì ta phải cài đặt (implements) interface này qua hàm boolean matches(byte[] candidate)
Hàm matches(byte[] candidate) này nếu trả về giá trị true thì record candidate sẽ
có mặt trong RecordEnumeration, ngược lại nếu trả về giá trị false thì record sẽ bị loại
Giả sử ta có bài toán lọc theo năm sinh như sau: Trong một record sẽ có hai trường, trường HoTen được lưu dưới dạng String UTF8 và trường năm sinh lưu dưới dạng integer Ta cần lấy ra danh sách những người sinh trước năm 1990 Bài toán được giải quyết như sau:
public class YearFilter implements RecordFilter{
private int Year=1990;
Trang 14public class MIDletApp extends MIDlet { …
Trang 15Java Mobile
6.5 Nhận thông điệp khi Record Store thay đổi
Đôi khi chúng ta có nhu cầu cần được thông báo mỗi khi các records trong record store bị thay đổi, xóa hay được thêm vào Để phục vụ cho nhu cầu này, J2ME cung cấp cho chúng ta interface RecordListener Interface này hoạt động tương tự các interface chúng ta đã đề cập đến trong phần RMS này Khi lớp ứng dụng của ta cài đặt interface này, nếu có sự biến đổi nào trên các records của bộ record store chúng ta sẽ nhận được thông điệp thông qua các hàm recordAdded(), recordChanged() và recordDeleted()
Các hàm sự kiện trên đều có hai tham số:
• RecordStore recordStore: cho biết record store nào bị thay đổi
• int recordID: cho biết ID của record bị thay đổi
Trang 16Java Mobile
6.6 Xử lý lỗi khi thao tác với Record Store
Hãng Sun đã đặt ra tổng cộng 5 exceptions dành riêng cho việc xử lý RMS Tất cả các exception này đều kế thừa từ lớp java.lang.Throwable, sau đây là danh sách các exception trên và ý nghĩa của chúng:
InvalidRecordIDException:
Được dùng để thông báo người dùng truy xuất đến RecordID không hợp lệ Ví dụ như khi hàm RecordStore.getRecord(int ID) được gọi và tham số ID bằng 0 thì exception này sẽ phát sinh
Trang 17RecordStoreNotOpenException
Được phát sinh khi ta thực hiện một số công việc truy vấn trên record store như thêm record, xóa record, đếm số record mà record store chưa được mở trước đó Consturctor
public RecordStoreNotOpenException() public RecordStoreNotFoundOpen(String message)
Trang 18Các nhà phát triển không đưa ra mục tiêu phát triển một bộ thư viện cung cấp các lớp, phương thức hoàn toàn mới mà họ muốn đưa ra một bộ thư viện con của các thư viện đã được phát triển khá tốt trên môi trướng J2SE và J2EE Bộ thư viện con này sẽ có một số thay đổi nhỏ để thích ứng với các hạn chế trên thiết bị di động cài đặt MIDP
7.2 Lược đồ lớp
Mục tiêu chung đươc đề ra là chúng ta cần có một lớp chính: lớp Connector Lớp này sẽ có khả năng tạo các loại kết nối khác nhau: http, datagram, file… Phương thức mở kết nối sẽ có dạng:
Trang 19Java Mobile
Lúc này GCF cho thấy khả năng linh hoạt của mình Tùy theo các protocol khác nhau mà một kết nối loại tương ứng sẽ được mở và trả lại cho người dùng Lớp Connector sẽ tìm kiếm các lớp cài đặt cho loại protocol được yêu cầu, việc này được thực hiện thông qua phương thức Class.forName() Ví dụ khi có một yêu cầu
mở kết nối HTTP, lớp Connector sẽ thực thi hàm:
Class.forName(“com.sun.midp.io.j2me.http.Protocol”);
Sau khi kiểm tra, nếu lớp này tồn tại thì người dùng sẽ được trả lại một đối tượng cài đặt (implements) interface Connection và các thao tác truy cập sẽ được thực hiện thông qua đối tượng này Lớp Connector và interface Connection được định nghĩa trong CLDC
Lược đồ các lớp trong thư viện:
Hình 7.1 Lược đồ các lớp trong thư viện GCF
Thực chất CLDC chỉ cung cấp các định nghĩa cho các interface Quá trình cài đặt (implement) các protocols được thực hiện trong Profiles Ví dụ, trong MIDP 1.0 lớp HTTPConnection sẽ cài đặt (chỉ một phần) bộ giao thức HTTP 1.1 Lớp
Trang 20Java Mobile
HTTPConnection cài đặt interface ContentConnection và có hơn 20 phương thức để
hỗ trợ người dùng tương tác với giao thức HTTP
Mặc dù DatagramConnection được biểu diển trên lược đồ nhưng MIDP 1.0 chỉ yêu cầu bắt buộc hỗ trợ giao thức HTTP Các nhà phát triển có thể hỗ trợ thêm các giao thức khác nhưng điều này không bắt buộc
Trong MIDP 2.0, chúng ta được hỗ trợ các phương thức kết nối thông qua TCP
và UDP Tuy nhiên, ở phần này chúng ta chú trọng đến việc kết nối thông qua HTTP, đây là yêu cầu bắt buộc với các thiết bị MIDP
Sau đây là danh sách các lớp,interfaces và hàm chính trong bộ GCF:
Connection (public abstract interface Connection)
public void close() InputConnection (public abstract interface InputConnection extends Connection)
public InputStream openInputStream() public DataInputStream openDataInputStream() OutputConnection (public abstract interface OutputConnection extends Connection)
public OutputStream openOutputStream() public DataOutputStream openDataOutputStream() StreamConnection (public abstract interface StreamConnection extends InputConnection, OutputConnection)
ContentConnection (public abstract interface ContentConnection extends StreamConnection)
public long getLength() public String getEncoding() public String getType() HttpConnection (public interface HttpConnection extends ContentConnection)
//Hơn 20 phương thức hỗ trợ truy cập HTTP Connector (public class Connector)
Trang 21Java Mobile
public static Connection open(String name) public static Connection open(String name, int mode) public static Connection open(String name, int mode, boolean timeouts)
public static DataInputStream openDataInputStream(String name) public static DataOutputStream openDataOutputStream(String name) public static InputStream openInputStream(String name)
public static OutputStream openOutputStream(String name)
7.3.1 Khởi tạo kết nối:
Lớp Connector có 7 phương thức để khởi tạo kết nối với server
Lớp: javax.microedition.io.Connector
static Connection open(String name) Tạo connection dạng READ_WRITE
static Connection open(String name,
int mode)
Tạo connection với mode được chỉ định rõ (được đề cập ở bảng sau)
static Connection open(String name,
int mode, boolean timeouts)
Tạo connection với mode được chỉ định,
xử lý exception timeouts (được đề cập ở phần sau)
Trang 22openOutputStream (String name)
Tạo một output stream cho connection
static DataInputStream
openDataInputStream (String name)
Tạo một data input stream cho connection
static DataOutputStream (String
Bảng 7.2 Mode Kết Nối
Trong 7 phương thức trên có 3 phương thức open() khác nhau: Phương thức đầu tiên chỉ đòi hỏi địa chỉ của server, phương thức thứ hai cho phép người dùng chọn kiểu connection có hỗ trợ reading/writing Phương thức cuối cùng cho phép người dùng tùy chọn có xử lý timeouts exception hay không Các phương thức còn lại dùng để mở các dòng dữ liệu đọc/ghi
Sau đây là đoạn lệnh dùng để thiết lập một kết nối kiểu ContentConnetion (đây
là interface cài đặt interface Connection và chứa các định nghĩa cho lớp HttpConnection, xin xem lại lược đồ bên trên):
String url = "http://www.mydomain.com"
ContentConnection connection = (ContentConnection) Connector.open(url);
Trang 23InputStream iStrm = connection.openInputStream();
// ContentConnection hỗ trợ phương thức getLength
int length = (int) connection.getLength();
Thậm chí chúng ta còn có thể lấy ra dòng dữ liệu Input mà không cần tạo đối tượng connection như sau:
// Tạo một InputStream connection
String url = "http://www.mydomain.com/picture.png"
InputStream iStrm = (InputStream) Connector.openInputStream(url);
Trang 24Java Mobile
Tuy nhiên, ở cách tiếp cận vừa nêu chúng ta không tận dụng được phương thức getLength() của interface ContentConnection Việc chọn lựa cách tiếp cận nào là tùy thuộc vào hoàn cảnh và sở thích của người lập trình
Đến thời điểm này chúng ta chỉ mới khởi tạo kết nối và đọc, ghi dữ liệu mà chưa tận dụng các tính năng liên quan đến giao thức HTTP HTTP là một giao thức dạng yêu cầu / phản hồi: client gửi một yêu cầu (request), server sẽ gửi lại một thông điệp phản hồi Tiếp theo chúng ta sẽ nghiên cứu những chức năng hỗ trợ giao thức HTTP
7.3.2 Các đặc điểm của kết nối HTTP bằng J2ME:
Các phương thức Yêu Cầu (Request):
Một yêu cầu từ phía client gồm 3 phần: phương thức request, header và phần thân (body) HttpConnection hỗ trợ ba phương thức request: GET, POST, HEAD
javax.microediton.io.HttpConnection
POST Thông tin yêu cầu được gửi theo một stream riêng biệt HEAD Gói tin yêu cầu truy vấn thông tin về một tài nguyên
Bảng 7.3 Các Request Method chính
Cả ba phương thức trên thông báo cho server biết client cần truy vấn một thông tin Phương thức GET và POST khác nhau ở cách thông tin truy vấn được gửi lên server
Chúng ta có thể chỉ định rõ phương thức sử dụng trong HttpConnection bằng hàm setRequestMethod():
HttpConnection http = null;
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.GET);
Trang 25request (thuộc phần header)
Bảng 7.4 Các phương thức set/get Request Method
Sử dụng GET, phần thân của yêu cầu được đưa vào chung trong URL Điều này được gọi là URL encoding Ví dụ, chúng ta có một web form gồm 2 trường color và font Tên của hai trường này được đặt là userColor và userFont Khi user chọn giá trị cho hai trường này, giả sử lần lượt là userColor = blue và userFont = courier và submit lên trang web http://www.mydomain.com/formscript thì URL thực chất có dạng như sau:
http://www.mydomain.com/formscript?userColor=blue&userFont=courier (dấu ? dùng để phân cách vùng địa chỉ trang và phần thông tin yêu cầu gửi lên server) Sau đó, trang web trên server sẽ dùng các hàm riêng để tách vùng thông tin yêu cầu và thực hiện xử lý, sau đó sẽ trả kết quả cho client
Với phương thức POST, thông tin truy vấn không được ghép thêm vào vùng URL mà được chuyển lên server trong vùng BODY của gói tin HTTP
Phương thức POST có hai ưu điểm so với phương thức GET:
• Phương thức POST không giới hạn kích thước vùng dữ liệu request gửi lên server, ngược lại phương thức GET do gửi dữ liệu dạng gắn kèm vào URL nên không thể gửi kèm dữ liệu quá lớn
• POST gửi dữ liệu theo một dòng riêng (trong phần BODY) nên không thể hiện thông tin cho người dùng thấy trong URL
Trang 26Java Mobile
HEAD là một phương thức gần giống với GET Client gửi dữ liệu lên server và đính kèm nó với URL Tuy nhiên, trong gói tin hồi đáp của server cho gói tin dạng HEAD này sẽ không có vùng BODY HEAD được dùng chủ yếu để lấy thông tin về một tài nguyên nào đó trên server Ví dụ, chúng ta muốn lấy thông tin về lần cập nhật mới nhất của một tập tin trên server, do đó, trong gói tin trả lời chúng ta thực chất không cần nội dung của file trên (gói tin không có vùng BODY)
Ghi chú: Luận văn không có mục tiêu trình bày về giao thức HTTP mà chỉ giới thiệu HTTP như một phương thức kết nối được J2ME hỗ trợ, do đó phần lý thuyết giới thiệu về HTTP sẽ rất hạn chế Nếu người đọc muốn tìm hiểu hơn về HTTP có thể tìm đọc RFC 2616 về HTTP 1.1
Thông tin Header:
Phần thứ hai của một gói tin yêu cầu từ client là phần header Giao thức HTTP định nghĩa hơn 40 trường header Một số trường thông dụng như Accept, Cache-Control, Content-Type, Expires, If-Modified-Since và User-Agent Header được đặt giá trị bằng hàm setRequestProperty() đã được đề cập bên trên
Gói tin trả lời của Server:
Trang 27Java Mobile
Khi client đã đóng gói một gói tin gồm phương thức yêu cầu, phần header, phần body và gửi đến server, server có trách nhiệm phân tích gói tin, xử lý và hồi đáp client Phần hồi đáp của server cũng gồm 3 phần: status line, header và phần body
Status Line:
Phần status line cho biết kết quả của gói tin yêu cầu từ phía client Trong HttpConnection, chúng ta có hơn 35 mã status hồi đáp HTTP chia các mã hồi đáp này thành 5 mục lớn:
1xx – Thông tin 2xx – Mã báo thành công 3xx – Chuyển hướng (redirection) 4xx – Lỗi từ phía client
5xx – Lỗi tại server
Ví dụ, HTTP_OK (mã 200) thông báo yêu cầu từ phía client đã được xử lý thành công Khi gửi gói tin hồi đáp (response), server thường gửi cả version của giao thức kèm theo status line
Đây là một số ví dụ về status line:
HTTP/1.1 200 OK HTTP/1.1 400 Bad Request HTTP/1.1 500 Internal Server Error
Khi đọc mã status, ta có hai lựa chọn: lấy về mã hay câu thông báo:
Trang 28Java Mobile
javax.microedition.io.HttpConnection
String getHeaderField(int n) Lấy giá trị 1 trường header theo index
String getHeaderField(String name) Lấy giá trị 1 trường header theo tên
Bảng 7.5 Các phương thức truy vấn HTTP Header
Xét một ví dụ, giả sử trong vùng header ta có một cặp khóa/giá trị như sau: content-type=text/plain (“content-type” là một trường được HTTP định nghĩa,
“text-plain” để chỉ giá trị trả về có dạng text đơn thuần) Chúng ta giả sử thêm trường content-type này là trường đầu tiên (index=0) trong vùng header Để lấy giá trị của trường này ta có những cách sau:
Trang 29Ứng dụng Client sẽ trải qua một số bước:
//Gửi yêu cầu lên server:
//1) Xác định phương thức yêu cầu là GET
http.setRequestMethod(HttpConnection.GET);
//2) Gửi các thông tin header (không bắt buộc phải có)
http.setRequestProperty(“User-Agent”, “Profile/MIDP-1.0 1.0");
Configuration/CLDC-//3) Gửi dữ liệu, vì dùng Get nên không có phần body – data
Sau đó chúng ta phải xử lý phần thông tin trả về của server để lấy ra nội dung file
//Thông tin trả về của server //1) Lấy status line: