Download as pdf or txt
Download as pdf or txt
You are on page 1of 81

Đại Học Công Nghệ Thông Tin

ĐH Quốc Gia Tp HCM


____________***********_____________

Báo cáo môn học Lập Trình


Hệ Thống với Java

Đề tài số 7: Tìm hiểu Lập trình phân tán trong Java


(Nguồn Chương 5 cuốn Core Java™ 2 Volume II - Advanced Features, Seventh Edition)

GVHD: Bùi Thanh Hiếu(Msc)

Thành viên nhóm:


Huỳnh Thái Bình 08520031
Hoàng Minh Hải 08520105
Huỳnh Xuân Tâm 08520326
Phan Thanh Hưng 08520164

26/04/2011
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Lời nói đầu


Lập trình đối tượng phân tán là một trong những vấn đề nóng bỏng của
công nghệ phần mền ngày nay. Java là ngôn ngữ đi tiên phong trong
việc giải quyết vấn đề lập trình phân tán. Ngày nay nếu bạn không biết
đến kỹ thuật lập trình này thì quả là một thiếu sót lớn. Từ .NET của
Microsoft cho đến Java của Sun tất cả đều hướng đến môi trường hợp
tác và tận dụng nguồn tài nguyên phân tán trên mạng. Chương này sẽ
giới thiệu với bạn khái niệm và cách cài đặt các đối tượng phân tán
bằng kỹ thuật RMI trong Java.
Nội dung chính:
 The Roles of Client and Server.
 Remote Method Invocations.
 Setup for Remote Method Invocation.
 Parameter Passing in Remote Methods.
 Server Object Activation.
 Java IDL and CORBA.
 Remote Method Calls with SOAP.
Phân chia công việc trong nhóm:
 Bạn Huỳnh Thái Bình phụ trách phần:
The Roles of Client and Server.
Remote Method Invocations
 Bạn Hoàng Minh Hải phụ trách phần:
Setup for Remote Method Invocation
 Bạn Huỳnh Xuân Tâm phụ trách phần:
Parameter Passing in Remote Methods
Server Object Activation
 Bạn Phan Thanh Hưng phụ trách 2 phần cuối:
Java IDL and CORBA
Remote Method Calls with SOAP

1 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

I/. The Roles of Client and Server(Vai trò của Client và


Server):
- Lấy ý tưởng là thu thập thông tin trên một máy tính khách hàng và gửi
thông tin qua mạng cho máy chủ. Giả sử rằng một người sử dụng trên một máy
tính cá nhân sẽ điền vào mẫu yêu cầu thông tin. Dữ liệu được gửi đến máy chủ của
nhà cung cấp. Các máy chủ xử lý yêu cầu và gửi lại thông tin sản phẩm cho khách
hàng.

Figure 5-1. Transmitting objects between client and server

- Trong mô hình truyền thống client/server, khách hàng yêu cầu một định
dạng truyền tải và gửi dữ liệu yêu cầu đến máy chủ. Các máy chủ phân tích yêu
cầu định dạng và các định dạng phản ứng để truyền cho khách hàng. Khách hàng
sau đó phân tích những phản ứng và hiển thị nó cho người dùng.

- Nếu bạn thực hiện bằng tay, có một rắc rối lớn: Bạn phải thiết kế một định
dạng truyền tải, và bạn phải viết code cho việc chuyển đổi giữa dữ liệu và định
dạng truyền.

2 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Note:
Trong ví dụ này, chúng tôi giả định rằng khách hàng là một máy tính tương tác với
một người sử dụng. Tuy nhiên, khách hàng đều có thể chạy một ứng dụng web và
yêu cầu một dịch vụ từ một chương trình chạy trên máy tính khác. Có hai đối
tượng giao tiếp, đối tượng khách hàng trong ứng dụng web và các đối tượng máy
chủ thực hiện các dịch vụ. Đây là một mô hình rất phổ biến trong thực tế.

- Chúng tôi quan tâm vào mô hình truyền thống client/server bởi vì nó làm
cho vai trò của các máy khách và máy chủ trực quan hơn.
- Cơ chế mà các lập trình viên làm cho khách hàng một phương pháp gọi
thường xuyên, mà không lo lắng về việc gửi dữ liệu qua mạng hay phân tích phản
ứng.đối tượng đã thực hiện công việc đó không nằm trong cùng một máy ảo. Nó có
thể không được thực hiện trong các ngôn ngữ lập trình Java.
- Giải pháp là cài đặt một proxy(sử uỷ quyền) cho các đối tượng server trên
client. Proxy là một phương pháp phổ biến. Các proxy client liên lạc với server.
- Lập trình viên của đối tượng máy chủ không muốn phiền phức với các giao
tiếp khách hàng. Giải pháp là cài đặt một đối tượng proxy thứ hai trên máy chủ.
Các máy chủ proxy giao tiếp với các proxy của khách hàng, và nó làm cho phương
pháp này phổ biến.
Figure 5-2. Remote method call with proxies

3 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Vậy làm thế nào để các proxy giao tiếp với nhau?
-> Điều đó phụ thuộc vào công nghệ thực hiện.
- Có ba sự lựa chọn phổ biến:
 RMI, Java Remote Method Invocation Technology hỗ trợ phương thức gọi
phân phối giữa các đối tượng Java.
 CORBA, Common Object Request Broker Architecture hỗ trợ phương thức
gọi giữa các đối tượng của bất kỳ ngôn ngữ lập trình. CORBA sử dụng
Internet Inter-ORB Protocol, hoặc IIOP, để giao tiếp giữa các đối tượng.
 SOAP, Simple Object Access Protocol cũng là ngôn ngữ lập trình trung gian.
Tuy nhiên, SOAP sử dụng một định dạng truyền dẫn dựa trên XML.

Note:
Microsoft sử dụng COM. Trong quá khứ, Microsoft đặt COM là một đối thủ so với
CORBA. Tuy nhiên, tại thời điểm này, Microsoft tập trung vào SOAP.

- CORBA và SOAP hoàn toàn là ngôn ngữ trung gian. Chương trình client
và server có thể được viết bằng C, C + +, Java, hoặc ngôn ngữ khác. Bạn cung cấp
một giao diện để xác định tín hiệu của các phương thức và các loại dữ liệu đối
tượng của bạn có thể xử lý. Những mô tả được định dạng bằng một ngôn ngữ đặc
biệt, được gọi là ngôn ngữ định nghĩa giao diện (IDL) cho CORBA và ngôn ngữ
mô tả các dịch vụ Web (WSDL) cho SOAP.
- Khá ít người tin rằng CORBA là mô hình đối tượng của tương lai. Thành
thật mà nói CORBA đã có ích trong việc triển khai các vấn đê phức tạp và các vấn
đề tương thích.
- SOAP có thể đơn giản, nhưng nó cũng có thể trở nên khá phức tạp, vì nó
yêu cầu nhiều tính năng hơn mà CORBA đã có. Các giao thức XML có lợi thế là
ngôn ngữ gần gũi với con người nên giúp ích cho việc gỡ rối. Nhìn chung,
CORBA hiệu quả hơn, mặc dù SOAP là một phù hợp tốt hơn cho một kiến trúc
web.
- Nếu cả hai đối tượng giao tiếp được thực hiện trong code Java, thì việc
dùng CORBA và SOAP là không cần thiết. Sun đã phát triển một cơ chế đơn giản,
đó là phương pháp triệu gọi phương thức từ xa-Remote Method Invocation (RMI),
đặc biệt dùng cho giao tiếp giữa các ứng dụng Java.

4 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

II./ Remote Method Invocations(Gọi phương thức từ xa):


- Nếu bạn có quyền truy cập vào một đối tượng trên một máy tính khác, bạn
có thể gọi các phương thức của đối tượng từ xa. Tất nhiên, các tham số phương
thức bằng cách nào đó phải được gửi đến các máy khác, máy chủ phải được thông
báo để thực hiện các phương thức, và giá trị trả về phải được return trở lại. RMI xử
lý việc này.
- Ví dụ: client tìm kiếm thông tin sản phẩm có thể truy vấn một đối tượng
trên máy chủ. Nó gọi một phương thức từ xa, tìm kiếm, trong đó có một tham số:
một đối tượng khách hàng. Việc tìm thấy trả về một đối tượng cho client: đối
tượng của sản phẩm
Figure 5-3. Invoking a remote method on a server object

- Trong thuật ngữ RMI , các đối tượng của phương thức tạo ra lời gọi từ xa
được gọi là client object. Các đối tượng từ xa được gọi là server object. Điều quan
trọng là phải nhớ rằng thuật ngữ client/server chỉ áp dụng cho một lời gọi phương
thức duy nhất. Các máy tính đang chạy code Java mà gọi phương thức từ xa là
client và máy tính lưu trữ các đối tượng mà thực hiện lời gọi là server. Điều này
hoàn toàn làm cho vai trò được đảo ngược lại. Server của một lời gọi trước đó có
thể trở thành client khi gọi một một đối tượng bằng phương thức từ xa ở trên máy
tính khác.

5 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

1/. Stubs and Parameter Marshalling:


- Khi client muốn gọi một phương thức từ xa trên một đối tượng từ xa, thì nó
gọi 1 phương pháp thông thường trên một đối tượng proxy gọi là stub. Stub nằm
trên máy client, không phải trên server. Ví dụ, trong giao thức RMI các đối tượng
được mã hoá với cơ chế tuần tự. Quá trình mã hóa các thông số được gọi là tham
số marshalling(parameter marshalling). Mục đích của tham số marshalling là
chuyển đổi các tham số vào một định dạng thích hợp cho việc vận chuyển từ máy
ảo với nhau.

- Phương thức stub trên client xây dựng một khối thông tin bao gồm:
o Nhận dạng đối tượng từ xa sẽ được sử dụng;
o Một mô tả của phương thức được gọi ;
o Các tham số marshalled.

- Phương thức stub sau đó gửi thông tin này tới server. Về phía server, một
đối tượng thực hiện các hành động cho mỗi lời gọi phương thức từ xa:
o Nó không có các thông số marshals.
o Nó định vị đối tượng được gọi .
o Nó gọi phương thức mong muốn.
o Nó sẽ giữ và sắp đặt giá trị trả về hoặc ngoại lệ của cuộc gọi.
o Nó sẽ gửi một gói gồm các dữ liệu trả về marshalled tới stub trên client

6 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Figure 5-4. Parameter marshalling

- Quá trình này rõ ràng là phức tạp, nhưng nó hoàn toàn tự động và minh
bạch cho các lập trình viên. Hơn nữa, các nhà thiết kế của các đối tượng Java từ xa
đã cố gắng để cung cấp cho các đối tượng từ xa cùng "look and feel(xem và cảm
thấy)" như các đối tượng nội tại.

2./ Dynamic Class Loading:


- Khi truyền một đối tượng từ xa đến một chương trình khác, như là một
tham số hay giá trị trả về của một phương thức từ xa, thì chương trình đó phải có
các file class cho đối tượng đó. Ví dụ, hãy xem xét một phương thức với kiểu trả
về là các sản phẩm. Tất nhiên, chương trình client cần các lớp Product.class để
biên dịch. Nhưng bây giờ giả sử server khởi tạo và trả về một đối tượng sách, và
sách là một phân nhóm của sản phẩm. Các client có thể chưa bao giờ thấy class
Book, và nó có thể đã không biết nơi để tìm file lớp Book.class
- Do đó, một bộ nạp lớp đó sẽ được tải từ máy chủ. Quá trình này cũng
tương tự như quá trình nạp lớp của các applet chạy trong trình duyệt. Bất cứ khi
nào một chương trình tải mới mã từ một vị trí mạng, sẽ phát sinh vấn đề an ninh.

7 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Vì lý do đó, bạn cần sử dụng một chương trình quản lý bảo mật(security manager)
trong các ứng dụng client RMI. Đây là một cơ chế an toàn để bảo vệ các chương
trình từ virus trong mã stub. Đối với các ứng dụng chuyên ngành, các lập trình có
thể thay thế bộ tải lớp của mình và quản lý an ninh, nhưng được cung cấp bởi hệ
thống RMI là đủ cho sử dụng bình thường.

III/. Setup for Remote Method Invocation(Cài đặt cho


phương pháp truyền dẫn dữ liệu từ xa):
- Ngay cả khi chạy những ví dụ đơn giản nhất các đối tượng từ xa(remote
object) yêu cầu nhiều hơn những thiết lập không chạy một chương trình độc lập
hoặc applet. Bạn phải chạy các chương trình trên cả các máy tính máy chủ và máy
khách. Các thông tin object cần thiết phải được tách vào vào phía giao diện máy
khách và giao diện máy chủ phía triển khai. Ngoài ra, một cơ chế đặc biệt cho phép
client tra cứu để xác định vị trí các object trên máy chủ.
- Để bắt đầu, chúng ta đi qua từng yêu cầu, sử dụng ví dụ đơn giản. Trong ví
dụ đầu tiên của chúng ta tạo ra một vài đối tượng của một loại sản phẩm trên máy
chủ. Sau đó chúng ta chạy một chương trình trên một máy tính client để xác định
vị trí và truy vấn các đối tượng này.

1/. Interfaces and Implementations(Giao diện và Triển khai):


- Chương trình client cần để thao tác các đối tượng server nhưng nó không
thực sự copy chúng . Các đối tượng tự cư trú trên máy chủ. Mã client vẫn phải biết
những gì nó có thể làm với các đối tượng, khả năng của chúng được thể hiện trong
một giao diện được chia sẻ giữa client và server và để cư trú đồng thời trên cả hai
máy.
interface Product // shared by client and server
extends Remote
{
String getDescription() throws RemoteException;
}
- Cũng như trong ví dụ này tất cả các giao diện cho các remote object phải
mở rộng giao diện từ xa được xác định trong gói java.rmi. Tất cả các phương
pháp trong các giao diện cũng phải biểu thị một Remote Exception. Lý do cho việc
khai báo là cuộc gọi phương thức từ xa vốn ít tin cậy hơn cuộc gọi địa phương,
luôn luôn có thể có một cuộc gọi từ xa sẽ thất bại. Ví dụ, máy chủ hoặc kết nối
mạng có thể tạm thời không có hoặc có vấn đề mạng. Code máy client của bạn
phải được chuẩn bị để đối phó với những khả năng này. Đối với những lý do này.
Ngôn ngữ lập trình Java buộc bạn phải theo kịp Remote Exception với tất cả các

8 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

cuộc gọi phương thức từ xa và để xác định các hành động thích hợp để có khi cuộc
gọi không thành công. Các khách hàng truy cập các đối tượng máy chủ thông qua
stub để thực hiện giao diện này.
Product p = . . .; // see below how the client gets a stub
reference
String d = p.getDescription();
System.out.println(d);

- Trong phần tiếp theo, bạn sẽ thấy làm thế nào client có thể có được một
tham chiếu đến loại đối tượng từ xa.
- Tiếp theo, ở phía máy chủ, bạn phải thực hiện các lớp mà thực sự thực hiện
các phương pháp quảng cáo trong giao diện từ xa.
public class ProductImpl // server
extends UnicastRemoteObject
implements Product
{
public ProductImpl(String d)
throws RemoteException
{
descr = d;
}

public String getDescription()


throws RemoteException
{
return "I am a " + descr + ". Buy me!";
}

private String descr;


}

NOTE
constructor ProductImpl được khai báo để dịch chuyển RemoteException vì
UnicastRemoteObject có thể dịch chuyển ngoại lệ nếu nó không thể kết nối
với dịch vụ mạng theo dõi các đối tượng máy chủ

- Lớp này có một phương pháp duy nhất, geTDescription ,có thể được gọi
từ các khách hàng từ xa

9 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Bạn có thể nói rằng class là một máy chủ cho các phương pháp điều khiển
từ xa bởi vì nó mở rộng UnicastRemoteObject, mà là một lớp Java nền tảng cho
các đối tượng truy cập từ xa.
NOTE
Lớp ProductImpl không phải là một lớp máy chủ điển hình bởi vì nó làm việc quá
ít. Thông thường, bạn chỉ muốn có máy chủ làm một số công việc nặng mà một
client không thể thực hiện. Chúng tôi chỉ sử dụng các ví dụ sản phẩm để hướng
dẫn bạn qua các cơ chế gọi các phương thức từ xa.

- Các class server thường mở rộng lớp RemoteServer từ gói


java.rmi.server. nhưng RemoteServer là một lớp trừu tượng mà chỉ xác định
các cơ chế cơ bản trong giao tiếp giữa máy chủ và các đối tượng khai từ xa Lớp
UnicastRemoteObject mà đi kèm với RMI mở rộng lớp trừu tượng và là
RemoteServer bạn có thể sử dụng nó mà không cần viết bất kỳ mã nào ."path of
least resistance "cho một lớp máy chủ là có nguồn gốc từ UnicastRemoteObject,
và tất cả các lớp máy chủ trong chương này làm như vậy.
Hình 5-5 cho thấy mối quan hệ thừa kế giữa các lớp này.

10 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Một đối tượng UnicastRemoteObject nằm trên máy chủ. Nó còn chạy
được khi một dịch vụ được yêu cầu và phải được truy cập thông qua giao thức TCP
/ IP. Đây là lớp mà chúng tôi mở rộng cho tất cả các lớp máy chủ trong cuốn sách
này và là lớp máy chủ chỉ có trong phiên bản hiện tại của gói RMI. Sun(mặt trời)
hoặc bên thứ ba nhà cung cấp có thể, trong tương lai, thiết kế các lớp khác để sử
dụng bởi các máy chủ cho RMI.
NOTE
Thỉnh thoảng, bạn có thể không muốn có một lớp máy chủ mở rộng lớp
UnicastRemoteObject, có lẽ bởi vì nó đã được mở rộng một lớp khác. Trong tình
hình đó, bạn cần phải tự khởi tạo các đối tượng máy chủ và truyền cho phương
thức exportObject tĩnh. Thay vì mở rộng UnicastRemoteObject,ta gọi:
UnicastRemoteObject.exportObject(this, 0);
Trong xây dựng của các đối tượng máy chủ. Tham số thứ hai là 0 để chỉ ra rằng bất
kỳ cổng thích hợp có thể được sử dụng để lắng nghe các kết nối client

- Khi bạn sử dụng RMI (hoặc bất kỳ cơ chế đối tượng phân tán, cho từng
vấn đề), bạn sẽ cần phải nắm vững một chút bối rối tập các lớp. Trong chương này,
chúng tôi sử dụng một quy ước đặt tên thống nhất cho tất cả các ví dụ của chúng
tôi mà chúng tôi hy vọng làm cho nó dễ dàng hơn để nhận ra mục đích của từng
loại(xem Bảng 5-1)

11 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

2/. Stub Class Generation:


- Theo JDK 5.0, tất cả các lớp stub được tạo ra tự động, sử dụng cơ chế
proxy thảo luận trong Tập 1, chương 6. Tuy nhiên, trước khi JDK 5.0, bạn phải tự
tạo ra khai với công cụ rmic, như trong ví dụ sau đây
rmic -v1.2 ProductImpl

-Điều này gọi đến các công cụ rmic tạo ra một lớp tập tin
ProductImpl_Stub.class

- Nếu bạn sử dụng JDK 1.1 hãy gọi:


rmic -v1.1 ProductImpl

- Hai tập tin được tạo ra: các tập tin stubs và lớp một tập tin thứ hai tên là
ProductImpl_Skel.class.
NOTE
Hãy nhớ đến đầu tiên biên dịch tập tin nguồn với javac trước khi chạy rmic. Nếu
bạn đang tạo ra khai cho một lớp trong một gói, bạn phải cung cấp cho rmic tên gói
đầy đủ.

3/. Locating Server Objects:


- Để truy cập một đối tượng từ xa mà tồn tại trên máy chủ, khách hàng cần
có một đối tượng stub địa phương, Làm thế nào có thể yêu cầu client như một stub.
Phương pháp phổ biến nhất là để gọi một phương thức từ xa của một đối tượng
server và nhận được một đối tượng stub như là một giá trị trả về.

- Tuy nhiên,có một vấn đề "con gà và quả trứng” ở đây. Các đối tượng máy
chủ đầu tiên đã được đặt một cách khác. The Sun RMI thư viện cung cấp dịch vụ
đăng ký khởi động để định vị các đối tượng máy chủ đầu tiên, Một chương trình
máy chủ đăng ký các đối tượng với dịch vụ đăng ký khởi động, và client truy khai
cho các đối tượng. Bạn đăng ký một đối tượng máy chủ bằng cách cho cơ quan
đăng ký khởi động dịch vụ một tham chiếu đến các đối tượng và tên một. Tên này
là một chuỗi được (hy vọng) độc đáo
// server
ProductImpl p1 = new ProductImpl("Blackwell Toaster");
Context namingContext = new InitialContext();
namingContext.bind("rmi:toaster", p1);

12 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Mã khách hàng nhận được một stub đối tượng để truy cập máy chủ bằng
cách chỉ định tên máy chủ và tên các đối tượng theo cách sau:
// client
Product p = (Product)
namingContext.lookup("rmi://yourserver.com/toaster");

- RMI URL bắt đầu với rmi: / / và được theo sau bởi một máy chủ, một
số cổng tùy chọn, giảm khác, và tên của các đối tượng từ xa. Một ví dụ khác là:
rmi://localhost:99/central_warehouse
- Mặc định thì server là localhost và port là 1099.
NOTE
Bởi vì nó là rất khó khăn để giữ các tên duy nhất trong một đăng ký toàn cầu, bạn
không nên sử dụng kỹ thuật này là phương pháp chung để định vị đối tượng trên
máy chủ. Thay vào đó, cần có tương đối ít đối tượng máy chủ tên đăng ký với dịch
vụ khởi động. đối tượng có thể xác định vị trí các đối tượng khác cho bạn. Trong ví
dụ của chúng tôi, chúng tôi tạm thời hành vi vi phạm quy tắc này và đăng ký các
đối tượng tương đối nhỏ để cho bạn thấy cơ chế đăng ký, định vị đối tượng.
- Vì lý do an ninh, ứng dụng có thể mở ra ràng buộc, hoặc đăng ký tài liệu
tham khảo đối tượng rebind chỉ khi nó chạy trên máy chủ giống như đăng ký. Điều
này ngăn cản các client thù địch từ thay đổi thông tin đăng ký. Tuy nhiên, bất kỳ
client cũng có thể tra cứu các đối tượng. Các dịch vụ RMI đặt tên là tích hợp vào
trong đặt tên Java và mục thông tin (JNDI) dịch vụ. Trong JDK 1.3 và dưới đây,
bạn sử dụng một dịch vụ RMI đặt tên độc, như thế này:
Naming.bind("toaster", p1); // on the server
Product p = (Product) Naming.lookup("rmi://yourserver.com/toaster");

TIP
Nếu bạn so sánh các máy chủ của chúng tôi với các ví dụ máy chủ trong tài liệu
hướng dẫn Sun, bạn sẽ lưu ý rằng chúng tôi không cài đặt một trình quản lý bảo
mật trong máy chủ. Trái ngược với các báo cáo trong hướng dẫn này, một quản lý
an ninh là không cần thiết cho các máy chủ RMI. Bạn cần một quản lý an ninh, nếu
client gửi đến các đối tượng máy chủ thuộc về các lớp mà máy chủ không biết. Tuy
nhiên, trong dịch vụ của chúng tôi, client chỉ gửi các thông số String. Nói chung,
nó có vẻ khôn ngoan để hạn chế nạp lớp động trên các máy chủ.

13 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Example 5-1. ProductServer.java


1. import java.rmi.*;
2. import java.rmi.server.*;
3. import javax.naming.*;
4.
5. /**
6. This server program instantiates two remote objects,
7. registers them with the naming service, and waits for
8. clients to invoke methods on the remote objects.
9. */
10. public class ProductServer
11. {
12. public static void main(String args[])
13. {
14. try
15. {
16. System.out.println("Constructing server
implementations...");
17.
18. ProductImpl p1 = new ProductImpl("Blackwell
Toaster");
19. ProductImpl p2 = new ProductImpl("ZapXpress
Microwave Oven");
20.
21. System.out.println("Binding server implementations
to registry...");
22. Context namingContext = new InitialContext();
23. namingContext.bind("rmi:toaster", p1);
24. namingContext.bind("rmi:microwave", p2);
25. System.out.println("Waiting for invocations from
clients...");
26. }
27. catch (Exception e)
28. {
29. e.printStackTrace();
30. }
31. }
32. }

Example 5-2. ProductImpl.java


1. import java.rmi.*;
2. import java.rmi.server.*;
3.
4. /**
5. This is the implementation class for the remote product

14 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

6. objects.
7. */
8. public class ProductImpl
9. extends UnicastRemoteObject
10. implements Product
11. {
12. /**
13. Constructs a product implementation
14. @param n the product name
15. */
16. public ProductImpl(String n) throws RemoteException
17. {
18. name = n;
19. }
20.
21. public String getDescription() throws RemoteException
22. {
23. return "I am a " + name + ". Buy me!";
24. }
25.
26. private String name;
27. }

Example 5-3. Product.java


1. import java.rmi.*;
2.
3. /**
4. The interface for remote product objects.
5. */
6. public interface Product extends Remote
7. {
8. /**
9. Gets the description of this product.
10. @return the product description
11. */
12. String getDescription() throws RemoteException;
13. }

4/. Starting the Server:


- Chương trình máy chủ của chúng tôi là không hoàn toàn sẵn sàng để chạy
được. Bởi vì nó sử dụng các chương trình khởi động RMI Registry, dịch vụ đó
phải có sẵn. Để bắt đầu đăng ký RMI theo UNIX, thực hiện các tuyên bố
rmiregistry &

15 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Nếu là Windows thì gọi:


start rmiregistry

- Giờ thì bạn có thể khởi động Server. Nếu là Unix thì chạy lệnh:
java ProductServer &
- Nếu là Windows thì chạy lệnh:
start java ProductServer
- Nếu bạn chạy chương trình theo cách sau:
java ProductServer
thì chương trình sẽ không bao giờ thoát ra bình thường.Điều này có vẻ kỳ lạ sau
khi tất cả, chương trình chỉ tạo ra hai đối tượng và đăng ký chúng.Trên thực tế,các
chức năng chính không xuất cảnh ngay lập tức sau khi đăng ký,như bạn mong đợi.
Tuy nhiên,khi bạn tạo 1 đối tượng của một lớp mở rộng UnicastRemoteObject,
một chủ đề riêng biệt để duy trì chương trình còn sống vô thời hạn được bắt đầu.
Vì vậy, chương trình tồn tại xung quanh để cho phép khách hàng để kết nối với nó.

TIP
Các phiên bản Windows của JDK bao gồm một lệnh, javaw, mà bắt đầu các phiên
dịch bytecode là một quá trình Windows riêng biệt và giữ nó chạy. Một số nguồn
khuyên bạn nên sử dụng javaw, không phải start java, để chạy một phiên Java
trong nền trong Windows cho RMI. Làm như vậy không phải là một ý tưởng tốt, vì
hai lý do. Windows không có công cụ để giết một quá trình nền javaw không hiển
thị trong danh sách công việc. Nó chỉ ra rằng bạn cần phải giết và khởi động lại
dịch vụ đăng ký khởi động khi bạn thay đổi STUB của một lớp đăng ký. Để giết
một quá trình mà bạn bắt đầu với lệnh bắt đầu, tất cả các bạn phải làm là nhấp
chuột vào cửa sổ và nhấn Ctrl + C
Còn có một lý do quan trọng để sử dụng lệnh start. Khi bạn chạy một quá trình
máy chủ bằng cách sử dụng javaw, tin nhắn gửi tới đầu ra hoặc lỗi được loại bỏ.
Đặc biệt, họ không được hiển thị ở bất cứ đâu. Nếu bạn muốn xem kết quả hoặc
thông báo lỗi, sử dụng start. Sau đó, thông báo lỗi ít nhất là hiển thị trên giao
diện điều khiển. Và tin tưởng chúng TÔI, bạn sẽ muốn nhìn thấy những tin nhắn
này. Rất nhiều những thứ có thể đi sai khi bạn thử nghiệm với RMI. Các lỗi phổ
biến nhất có lẽ là bạn quên chạy rmic. Sau đó, máy chủ than phiền về việc thiếu
STUB . Nếu bạn sử dụng javaw, bạn sẽ không thấy thông báo lỗi, và bạn sẽ gãi
đầu của bạn tự hỏi tại sao các CLIENT không có thể tìm thấy các đối tượng máy
chủ.

16 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

5/. Listing Remote Objects:


- Trước khi viết chương trình client, hãy xác minh rằng chúng ta đã thành
công trong việc đăng ký các đối tượng từ xa. Ta gọi:
NamingEnumeration<NameClassPair> e = namingContext.list("rmi:");
để có được một điều tra của tất cả các đối tượng máy chủ với một rmi: URL. Việc
liệt kê sản lượng các đối tượng của loại NameClassPair, một lớp học bổ trợ có
chứa cả tên của đối tượng bị ràng buộc và tên của các lớp . Chúng tôi chỉ quan tâm
đến những cái tên:
while (e.hasMore()) System.out.println(e.next().getName());

- Ví dụ 5-4 chứa chương trình đầy đủ. Output sẽ là:


toaster
microwave

Example 5-4. ShowBindings.java


1. import java.rmi.*;
2. import java.rmi.server.*;
3. import javax.naming.*;
4.
5. /**
6. This programs shows all RMI bindings.
7. */
8. public class ShowBindings
9. {
10. public static void main(String[] args)
11. {
12. try
13. {
14. Context namingContext = new InitialContext();
15. NamingEnumeration<NameClassPair> e =
namingContext.list("rmi:");
16. while (e.hasMore())
17. System.out.println(e.next().getName());
18. }
19. catch (Exception e)
20. {
21. e.printStackTrace();
22. }
23. }
24. }

6/. The Client Side:

17 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Bây giờ, chúng ta có thể viết các chương trình CLIENT có yêu cầu từng
đối tượng sản phẩm mới được đăng ký để in mô tả của nó. chương trình CLIENT
có sử dụng RMI nên cài đặt một trình quản lý an ninh để kiểm soát các hoạt động
của các STUB động nạp. RMISecurityManager là như một người quản lý bảo
mật. Bạn cài đặt nó với chỉ dẫn sau:
System.setSecurityManager(new RMISecurityManager());

NOTE
Nếu tất cả các lớp (bao gồm cả stub ) có sẵn tại địa phương, sau đó bạn không thực
sự cần một người quản lý bảo mật. Nếu bạn biết tất cả các tập tin lớp của chương
trình của bạn tại thời gian triển khai, bạn có thể triển khai chúng tất cả các địa
phương. Tuy nhiên, nó thường xảy ra rằng chương trình tiến hóa máy chủ và các
lớp mới được thêm vào theo thời gian. Sau đó, bạn được hưởng lợi từ việc nạp lớp
năng động. Bất cứ lúc nào bạn mã tải từ một nguồn khác, bạn cần một người quản
lý bảo mật.

NOTE
Applet đã có một người quản lý bảo mật mà có thể kiểm soát tải của các lớp stub.
Khi sử dụng RMI từ applet, bạn không cài đặt một người quản lý bảo mật.

Example 5-5. ProductClient.java


[View full width]

1. import java.rmi.*;
2. import java.rmi.server.*;
3. import javax.naming.*;
4.
5. /**
6. This program demonstrates how to call a remote method
7. on two objects that are located through the naming
service.
8. */
9. public class ProductClient
10. {
11. public static void main(String[] args)
12. {
13. System.setProperty("java.security.policy",
"client.policy");
14. System.setSecurityManager(new RMISecurityManager());
15. String url = "rmi://localhost/";

18 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

16. // change to "rmi://yourserver.com/" when server


runs on remote machine
yourserver.com
17. try
18. {
19. Context namingContext = new InitialContext();
20. Product c1 = (Product) namingContext.lookup(url +
"toaster");
21. Product c2 = (Product) namingContext.lookup(url +
"microwave");
22.
23. System.out.println(c1.getDescription());
24. System.out.println(c2.getDescription());
25. }
26. catch (Exception e)
27. {
28. e.printStackTrace();
29. }
30. }
31. }

7/. Running the Client:


- Theo mặc định, RMISecurityManager những hạn chế tất cả các mã trong
chương trình từ việc thiết lập kết nối mạng. Tuy nhiên, chương trình cần để làm
cho các kết nối mạng. Để đạt được đăng ký RMI, và để liên hệ với các đối tượng
máy chủ.

- Một khi các chương trình client được triển khai, nó cũng cần sự cho phép
để tải các lớp stub của nó. Chúng tôi quyết vấn đề này sau khi chúng tôi thảo luận
triển khai.

- Để cho phép các client để kết nối đến cơ quan đăng ký RMI và đối tượng
máy chủ, bạn cung cấp một tập tin chính sách. Chúng tôi thảo luận về các tập tin
chính sách cụ thể hơn trong chương 9. Để bây giờ, chỉ cần sử dụng và sửa đổi các
mẫu mà chúng tôi cung cấp. Dưới đây là một tập tin chính sách cho phép một ứng
dụng để thực hiện bất kỳ kết nối mạng vào một cổng với số cổng ít nhất là 1024.
(Cổng RMI là 1099 theo mặc định, và các đối tượng máy chủ cũng sử dụng cổng ≥
1024.)
grant
{
permission java.net.SocketPermission
"*:1024-65535", "connect";

19 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

};

NOTE
Nhiều đối tượng máy chủ trên cùng một máy chủ có thể chia sẻ một cổng. Tuy
nhiên, nếu một cuộc gọi từ xa được thực hiện và cảng bận rộn, cảng khác được mở
ra tự động. Vì vậy, bạn nên mong đợi cổng nào được sử dụng ít hơn có các đối
tượng từ xa trên máy chủ.

- Trong chương trình client, chúng tôi hướng dẫn quản lý an ninh để đọc các
file chính sách bằng cách thiết lập thuộc tính java.security.policy với tên file. (.
Chúng tôi sử dụng file client.policy trong các chương trình ví dụ của chúng
tôi)
System.setProperty ("java.security.policy", "client.policy");
- Ngoài ra, bạn có thể chỉ định thiết lập hệ thống tài sản trên dòng lệnh:
java-Djava security.policy =. client.policy ProductClient

NOTE
Trong JDK 1.1, một tập tin chính sách đã không được yêu cầu cho khách hàng
RMI. Bạn không cần một tập tin chính sách cho các applet, hoặc, cung cấp máy
chủ và đăng ký RMI RMI đều nằm trên máy chủ phục vụ các mã applet

- Nếu đăng ký RMI và máy chủ vẫn còn đang chạy, bạn có thể tiến hành để
chạy các máy khách. Hoặc, nếu bạn muốn bắt đầu từ đầu, giết RMI registry và máy
chủ. Sau đó làm theo các bước sau:
1) Compile the source files for the interface, implementation, client, and server
classes.
javac Product*.java

2) If you use JDK 1.4 or below, run rmic on the implementation class.

rmic -v1.2 ProductImpl

3) Start the RMI registry.

rmiregistry &
or
start rmiregistry

20 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

4) Start the server.


java ProductServer &
or
start java ProductServer
5) Run the client.
java ProductClient
(Make sure the client.policy file is in the current directory.)
- Chương trình đơn giản là in ra:
I am a Blackwell Toaster. Buy me!
I am a ZapXpress Microwave Oven. Buy me!

- Output này không có vẻ tất cả những gì ấn tượng, nhưng xem xét những gì
diễn ra ở hậu trường khi các chương trình client thực hiện các cuộc gọi đến các
phương pháp getdescription. Chương trình client có một tham chiếu đến một
đối tượng stub rằng nó thu được từ phương pháp tra cứu. Nó gọi phương pháp
geTDescription, đó gửi một thông điệp mạng cho một đối tượng thu trên phía
máy chủ. Các đối tượng tiếp nhận viện dẫn phương thức getdescription trên đối
tượng ProductImpl nằm trên máy chủ. Đó là phương pháp tính toán một chuỗi.
Người nhận sẽ gửi chuỗi trên mạng. stub Các nhận được nó và trả về nó như là kết
quả (xem Hình 5-6).

21 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

8/. Preparing for Deployment:


- Triển khai một ứng dụng sử dụng RMI có thể được khó khăn bởi vì rất
nhiều điều có thể đi sai và các thông báo lỗi mà bạn nhận được khi một cái gì đó
mơ hồ . Chúng tôi đã tìm thấy rằng nó thực sự đền đáp cho giai đoạn triển khai tại
địa phương. Trong bước chuẩn bị, riêng các lớp tập tin vào ba thư mục con:
server
download
client
- Các máy chủ thư mục chứa tất cả các file cần thiết để chạy các máy chủ.
Bạn sau đó sẽ di chuyển các tập tin này vào máy chạy quá trình máy chủ. Trong ví
dụ của chúng tôi, máy chủ thư mục chứa các tập tin sau đây:
server/
ProductServer.class
ProductImpl.class
Product.class
CAUTION
Nếu bạn sử dụng JDK 1.4 hoặc dưới đây, thêm các lớp stub (như
ProductImpl_Stub.class) vào thư mục máy chủ. Chúng là cần thiết khi máy
chủ đăng ký các đối tượng thực hiện. Trái ngược với niềm tin phổ biến, máy chủ sẽ
không xác định vị trí chúng trong thư mục tải về, ngay cả khi bạn thiết lập các cơ
sở mã. Cần :
client/
ProductClient.class
Product.class
client.policy
- Bạn sẽ triển khai những tập tin trên máy khách. Cuối cùng, các thư mục
chứa các tập tin tải về lớp cần thiết cho đăng ký RMI, khách hàng, và máy chủ,
cũng như các lớp họ phụ thuộc vào. Trong ví dụ của chúng tôi, các thư mục tải về
sẽ như thế này
download/
Product.class
CAUTION
Hãy nhớ rằng ba máy ảo sử dụng thư mục download: các khách hàng, máy chủ, và
đăng ký RMI. Việc đăng ký RMI cần các tập tin lớp học cho các giao diện từ xa
của các đối tượng mà nó liên kết. client và máy chủ cần có tập tin lớp học cho các
thông số và giá trị trả lại.
- Bạn cần phải chạy một máy chủ web để phục vụ các tập tin lớp học trên
máy tính của bạn. Nếu bạn không có một máy chủ web cài đặt, tải về từ
http://jakarta.apache.org/tomcat và cài đặt nó. Tạo một thư mục tomcat / webapps

22 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

/ download, nơi tomcat là thư mục gốc cài đặt Tomcat của bạn. Tạo một thư mục
tomcat / webapps / download / WEB-INF, và để các tập tin tối thiểu sau đây
web.xml trong thư mục WEB-INF:
<?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>
</web-app

- Sau đó copy file class từ thư mục download vào thư mục
tomcat/webapps/download.
- Kế tiếp, chỉnh sửa file client.policy. Nó cần phải cấp cho client những
quyền hạn sau:
+ Để kết nối với cổng 1024 và ở trên để đạt được đăng ký RMI và triển khai
máy chủ; Để kết nối với cổng 80 (cổng HTTP tiêu chuẩn) để tải các tập tin lớp
stub. Bạn có thể bỏ qua sự cho phép này nếu bạn sử dụng Tomcat như là một
serverit web sử dụng cổng 8080 mặc định
- Thay đổi file sao cho giống như sau:
grant
{
permission java.net.SocketPermission
"*:1024-65535", "connect";
permission java.net.SocketPermission
"*:80", "connect";
};

- Cuối cùng, bạn đã sẵn sàng để thử nghiệm thiết lập của bạn.
1. Khởi động máy chủ web.
2. Điểm một trình duyệt web vào URL download
(http://localhost:8080/download/Product.class cho Tomcat) để xác minh rằng các
máy chủ web đang chạy.
3. Bắt đầu một lớp vỏ mới. Hãy chắc chắn rằng đường dẫn lớp là không
được đặt để bất cứ điều gì. Thay đổi vào một thư mục mà không chứa các tập tin
lớp. Sau đó bắt đầu đăng ký RMI.
4. Bắt đầu một lớp mới. Thay đổi vào thư mục máy chủ. Khởi động máy
chủ, cho một URL vào thư mục tải về là giá trị của tài sản
java.rmi.server.codebase:
java -Djava.rmi.server.codebase=http://localhost:8080/download/
ProductServer &

23 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

5. Thay đổi vào thư mục client. Hãy chắc chắn rằng các file client.policy là
trong thư mục đó. Bắt đầu cho client
java -Djava.security.policy=client.policy ProductClient

TIP
Nếu bạn không muốn cài đặt một máy chủ web tại địa phương, bạn có thể sử dụng
một URL tập tin để kiểm tra nạp lớp. Tuy nhiên, thiết lập được một chút phức tạp
hơn. Thêm dòng
permission java.io.FilePermission "downloadDirectory", "read ";
vào tập tin chính sách client của bạn. Ở đây, các thư mục tải về là tên đường dẫn
đầy đủ đến thư mục tải về, kèm theo trong dấu ngoặc kép, và kết thúc bằng một
dấu trừ (để chỉ tất cả các file trong thư mục và thư mục con của nó). Ví dụ:
permission java.io.FilePermission "/ home/test/download/-",
"read";
Trong tên tập tin Windows, bạn phải tăng gấp đôi mỗi dấu chéo ngược. Ví dụ,
permission java.io. FilePermission "c: \ \ home \ \ test \ \
download \ \ -", "read";
Bắt đầu đăng ký RMI sau đó các bắt đầu máy chủ với
java -Djava.rmi.server.codebase=file://home/test/download/
ProductServer &
hoặc
start java -
Djava.rmi.server.codebase=file:/c:\home\test\download/
ProductServer

9./ Deploying the Program(Triển khai chương trình):


- Bây giờ bạn đã kiểm tra việc triển khai các chương trình của bạn, bạn đã
sẵn sàng để phân phối nó vào thực tế khách hàng và máy chủ. Di chuyển các lớp
trong thư mục tải về vào máy chủ web.
- Hãy chắc chắn để sử dụng URL khi bắt đầu các máy chủ. Di chuyển các
lớp trong thư mục server vào máy chủ của bạn và bắt đầu đăng ký RMI và máy
chủ. thiết lập máy chủ của bạn bây giờ hoàn thành, nhưng bạn cần phải thực hiện
hai thay đổi trong máy khách. Đầu tiên, chỉnh sửa các tập tin chính sách và thay
thế * với tên máy chủ của bạn:
grant
{
permission java.net.SocketPermission
"yourserver.com:1024-65535", "connect";
permission java.net.SocketPermission

24 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

"yourserver.com:80", "connect";
};

- Cuối cùng thay thế localhost trong RMI URL của chương trình client:
String url = "rmi://yourserver.com/";
Product c1 = (Product) namingContext.lookup(url + "toaster");
. . .
- Sau đó, biên dịch lại client và thử nó. Nếu công trình tất cả mọi thứ, sau đó xin
chúc mừng là theo thứ tự. Nếu không, bạn có thể tìm được danh sách kiểm tra bên
hữu ích. Nó liệt kê một số vấn đề mà thường có thể phát sinh khi bạn đang cố gắng
để có được RMI để làm việc.

10/. Logging RMI Activity:

- Việc thực thi Sun RMI được cung cấp để tạo ra logging messages, sử dụng
tiêu chuẩn Java logging API
- Để thấy được hành động logging, tạo file logging.properties với nội
dung như sau:
handlers=java.util.logging.ConsoleHandler
.level=FINEST
java.util.logging.ConsoleHandler.level=FINEST
java.util.logging.ConsoleHandler.formatter=java.util.logging.Sim
pleFormatter
- Bạn có thể tinh chỉnh các thiết lập bằng cách thiết lập mức độ riêng lẽ cho
mỗi người đăng nhập hơn là thiết lập mức độ tổng thể.Bảng 5-2 danh sách logger
của RMI:

25 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Bắt đầu đăng ký RMI với lựa chon sau:


-J-Djava.util.logging.config.file=directory/logging.properties
- Khởi động client và server với:
-Djava.util.logging.config.file=directory/logging.properties
- Đó là cách tốt nhất để khởi động RMI, client, server trong môi trường khác
nhau.
- Sau đây là ví dụ của logging message cho ta thấy vấn đề của 1 lớp đang tải:
Việc đăng ký RMI không thể tìm thấy lớp Product . Nó cần để xây dựng 1 proxy
động
Aug 15, 2004 10:44:07 AM sun.rmi.server.LoaderHandler
loadProxyClass
FINE: RMI TCP Connection(1)-127.0.0.1: interfaces =
[java.rmi.Remote, Product],
codebase = "http://localhost:8080/download/"
Aug 15, 2004 10:44:07 AM sun.rmi.server.LoaderHandler
loadProxyClass
FINER: RMI TCP Connection(1)-127.0.0.1: (thread context class
loader: java.net
.URLClassLoader@6ca1c)
Aug 15, 2004 10:44:07 AM sun.rmi.server.LoaderHandler
loadProxyClass
FINE: RMI TCP Connection(1)-127.0.0.1: proxy class resolution
failed
java.lang.ClassNotFoundException: Product

V/. Parameter Passing in Remote Methods(Truyền tham số


trong phương thức từ xa) :
1/. Passing Nonremote Objects (truyền các đối tượng không từ xa):
- Khi một đối tượng từ xa được truyền từ máy chủ tới máy con, máy con
nhận một stub. Sử dụng stub này, máy con có thể thao tác đối tượng máy chủ bằng
cách gọi ra phương thức từ xa.Tuy nhiên,đối tượng vẫn lưu trên máy chủ. Nó cũng
có thể truyền và trả lại bất kỳ đối tượng khi gọi một phương thức từ xa không chỉ
vậy mà còn thực hiện giao diện từ xa.
Ví dụ : phương thức geTDescription của phần trước trả về một đối tượng kiểu
String
Chuỗi đó được tạo ra trên máy chủ và đã được chuyển đến máy con. Bởi vì kiểu
String không thực hiện giao diện từ xa nên máy chủ không thể trả về đối tượng
stub kiểu chuỗi. Thay vào đó, máy con lấy một bản sao của chuỗi.Khi đó, sau khi
gọi, máy con có đối tượng String để làm việc. Điều này có nghĩa rằng không cần

26 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

bất kỳ kết nối nào thêm nữa cho bất kỳ đối tượng trên máy chủ để phân tán chuỗi
đó.

- Bất cứ khi nào đối tượng không phải là đối tượng từ xa thì cần được
chuyển từ -máy ảo Java đến vùng khác,máy ảo Java thực hiện sao chép và gửi bản
sao chép đó thông qua kết nối mạng.Kỹ thuật này sử dụng phương pháp cục bộ để
truyền tham số.Khi ta truyền đối tượng vào một phương thức cục bộ,hoặc trả về
đối tượng từ kết quả của phương thức này,lưu ý là chỉ có đối tượng tham chiếu
được truyền.Tuy nhiên, các đối tượng tham chiếu là những địa chỉ nhớ của các đối
tượng trong máy ảo Java cục bộ.Thông tin này là vô nghĩa với một máy ảo Java
khác.

- Không khó để tưởng tượng rằng làm thế nào để sao chép một chuỗi và
chuỗi sao chép đó có thể được chuyển qua mạng.Cơ chế RMI có thể thực hiện sao
chép nhiều đối tượng phức tạp hơn nữa, miễn là chúng được tuần tự hóa(được sắp
xếp có thứ tự). RMI sử dụng cơ chế tuần tự hóa được mô tả trong “ Tập 1 Java,
Chương 12” ,nó thực hiện gửi các đối tượng qua một kết nối mạng. Điều này có
nghĩa rằng chỉ có các thông tin trong bất kỳ lớp mà thực hiện giao diện
Serializable có thể được sao chép.
Hình 5-7: Lấy sản phẩm đề xuất từ máy chủ

*Chú ý: Hình 5-7 có một biểu ngữ "Java Applet Window". Đây là kết quả của việc
chạy chương trình với một quản lý bảo mật. Biểu ngữ cảnh báo này được cung cấp
để bảo vệ chống vi mã "phishing" (vi mã : 1 chương trình ứng dụng nhỏ thực hiện
một nhiệm vụ cụ thể). Một vi mã chống đối có lẽ bật lên một cửa sổ, nhắc nhở điền
mật khẩu hoặc số thẻ tín dụng, và sau đó gửi thông tin lại đến host của nó. Để tắt
chức năng cảnh báo, thêm những dòng sau vào file client.policy:
permission java.awt.AWTPermission

27 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

"showWindowWithoutWarningBanner";
- Ghi chú : phish : (máy tính) sự lợi dụng sự không hiểu biết của người dùng
để đánh cắp thông tin cá nhân của họ,bằng cách mượn tên công ty(ngân hàng,công
ty thẻ tín dụng…) và dẫn người dùng đến trang Wed giả.

- Một đối tượng loại khách hàng (customer) được gửi đến máy chủ. Bởi vì
customer không phải là đối tượng từ xa nên một sao chép đối tượng được thực
hiện trên máy chủ. Chương trình máy chủ gửi lại một danh sách mảng các sản
phẩm.Danh sách mảng chứa các sản phẩm mà phù hợp với hồ sơ khách hàng, và
nó luôn luôn chứa một mục mà sẽ thỏa đáng bất cứ ai, cụ thể như một bản sao của
cuốn sách Core Java. Một lần nữa, ArrayList không phải là một lớp từ xa, do đó,
danh sách mảng được sao chép từ máy chủ trở lại cho máy con. Như mô tả trong
“Tập 1 Java, chương 12”,cơ chế tuần tự hóa thực hiện sao chép tất cả các đối tượng
được tham chiếu bên trong một đối tượng được sao chép. Trong trường hợp của ta,
nó thực hiện một sao chép tất cả các mảng danh sách truy cập tốt. Chúng ta thêm
một điều phức tạp phụ: Các truy cập là các đối tượng product từ xa. Như vậy,
người nhận sẽ lấy một bản sao của mảng danh sách,chứa đầy các đối tượng stub
với các sản phẩm trên máy chủ .(xem Hình 5-8).
Figure 5-8. Sao chép tham số cục bộ và trả về các đối tượng

28 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Tổng kết, các đối tượng từ xa được truyền qua mạng như là các stub. Các
đối tượng không từ xa được sao chép. Tất cả điều này là tự động và yêu cầu không
có người lập trình can thiệp.

- Bất cứ khi nào đoạn mã gọi một phương phức từ xa, stub thực hiện một gói
có chứa các bản sao chép của tất cả các giá trị tham số và gửi nó tới máy chủ, sử
dụng cơ chế tuần tự hóa đối tượng để sắp xếp có thứ tự các thông số.Máy chủ
không sắp xếp thứ tự cho chúng. Đương nhiên, tiến trình có thể khá là chậm khi
các đối tượng tham số rất lớn.

- Hãy nhìn vào các chương trình hoàn tất bên dưới. Trước tiên, chúng ta có
các giao diện cho các dịch vụ sản phẩm và nhà kho, như trong ví dụ 5-6 và 5-7.
Example 5-6. Product.java
1. import java.rmi.*;
2.
3. /**
4. The interface for remote product objects.
5. */
6. public interface Product extends Remote
7. {
8. /**
9. Gets the description of this product.
10. @return the product description
11. */
12. String getDescription() throws RemoteException;
13. }

Example 5-7. Warehouse.java


1. import java.rmi.*;
2. import java.util.*;
3.
4. /**
5. The remote interface for a warehouse with products.
6. */
7. public interface Warehouse extends Remote
8. {
9. /**
10. Gets products that are good matches for a customer.
11. @param c the customer to match
12. @return an array list of matching products
13. */
14. ArrayList<Product> find(Customer c) throws
RemoteException;
15. }

29 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Ví dụ 5-8 bên dưới cho thấy việc thực hiện của dịch vụ sản phẩm. Sản
phẩm lưu trữ một bản mô tả gồm độ tuổi, giới tính được nhắm đến (nam, nữ, hoặc
cả hai), và sở thích phù hợp. Lưu ý rằng lớp này thực hiện các phương thức
getdescription (lấy mô tả)trong giao diện Product, và nó cũng thực hiện một
số phương thức khác,chẳng hạn như phương thức match,nhưng những phương
thức này không phải là một phần của giao diện đó. Phương thức match là một ví
dụ về phương thức cục bộ, phương thức đó có thể được được gọi chỉ từ chương
trình cục bộ, không phải từ xa. Bởi vì phương thức match là cục bộ nên nó không
cần được chuẩn bị để throw (ném) một RemoteException (ngoại lệ từ xa).

- Ví dụ 5-9 bên dưới chứa đoạn mã cho lớp Customer. Lưu ý Customer
không phải là lớp từ xa hay các phương thức của nó có thể được thực thi từ xa. Tuy
nhiên, lớp này là tuần tự. Do đó, các đối tượng của lớp này có thể được chuyển từ
máy ảo đến vùng khác.

- Ví dụ 5-10 và 5-11 bên dưới cho thấy giao diện và việc thực hiện của dịch
vụ kho hàng. Cũng giống như lớp ProductImpl, lớp WarehouseImpl có phương
pháp cục bộ và từ xa. Phương thức add là cục bộ, nó được sử dụng bởi máy chủ
để thêm sản phẩm vào kho hàng. Phương thức find là từ xa, nó được sử dụng bởi
máy con để tìm các mục(sản phẩm) trong kho hàng.

- Để minh họa cho đối tượng Customer (khác hàng)thực sự được sao chép,
phương thức find của lớp WarehouseImpl xóa đối tượng khác hàng nhận được.
Khi phương thức từ xa trả về,lớp WarehouseClient hiển thị đối tượng khác hàng
mà nó gửi đến máy chủ. Như ta thấy, đối tượng đó không được thay đổi. Máy chủ
chỉ xóa sao chép của nó. Trong trường hợp này, toán tử reset phục vụ mục đích
không có hữu ích ngoại trừ việc chứng minh rằng đối tượng cục bộ được sao chép
khi chúng được truyền như là các tham số.

- Nó có thể cho nhiều stub con để thực hiện gọi đồng thời tới một đối tượng
máy chủ, ngay cả khi một số phương thức thay đổi trạng thái của máy chủ. Trong
ví dụ 5-11, chúng ta sử dụng một đôi tượng ReadWriteLock trong lớp
WarehouseImpl vì nó hiểu được rằng phương thức add cục bộ và phươn thức find
từ xa được gọi một cách đồng thời. Ví dụ 5-12 cho thấy chương trình máy chủ tạo
ra một đối tượng kho hàng và đăng ký nó với chương trình khởi động đăng ký dịch
vụ.

30 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Chú ý: Hãy nhớ rằng bạn phải bắt đầu chương trình đăng ký và chương trình
máy chủ, và giữ cho cả hai đang chạy trước khi bạn bắt đầu máy con.

- Ví dụ 5-13 cho thấy đoạn mã của máy con. Khi người dùng nhấp nút
Submit, một đối tượng khách hàng mới được tạo ra và được truyền tới phương
thức find từ xa.Lý lịch khách hàng được hiển thị trong vùng text(để chứng minh
rằng reset gọi trong máy chủ không ảnh hưởng đến nó).Cuối cùng, mô tả sản
phẩm của các sản phẩm được trả về trong danh sách mảng được thêm vào vùng
text. Lưu ý rằng mỗi lần gọi getdescription là một lần gọi phương thức từ xa.
Đây không phải là một thiết kế tốt trong thực tiễn,bạn thường truyền các đối tượng
nhỏ như mô tả sản phẩm bởi giá trị. Tuy nhiên, chúng ta muốn chứng minh rằng
một đối tượng từ xa sẽ tự động được thay thế bằng một stub trong suốt thời gian
sắp xếp thứ tự.

Tip: Nếu bạn khởi động máy chủ với


java -Djava.rmi.server.logCalls=true WarehouseServer &
sau đó máy chủ ký nhận tất cả phương thức từ xa gọi trên giao diện điều khiển
của nó. Hãy thử nó,ta sẽ có được một ấn tượng tốt đẹp của vận tải RMI.

Example 5-8. ProductImpl.java


1. import java.rmi.*;
2. import java.rmi.server.*;
3.
4. /**
5. This is the implementation class for the remote product
6. objects.
7. */
8. public class ProductImpl
9. extends UnicastRemoteObject
10. implements Product
11. {
12. /**
13. Constructs a product implementation
14. @param n the product name
15. */
16. public ProductImpl(String n) throws RemoteException
17. {
18. name = n;
19. }
20.
21. public String getDescription() throws RemoteException
22. {
23. return "I am a " + name + ". Buy me!";

31 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

24. }
25.
26. private String name;
27. }

Example 5-9. Customer.java


1. import java.io.*;
2.
3. /**
4. Description of a customer. Note that customer objects are
not
5. remote--the class does not implement a remote interface.
6. */
7. public class Customer implements Serializable
8. {
9. /**
10. Constructs a customer.
11. @param theAge the customer's age
12. @param theSex the customer's sex (MALE or FEMALE)
13. @param theHobbies the customer's hobbies
14. */
15. public Customer(int theAge, int theSex, String[]
theHobbies)
16. {
17. age = theAge;
18. sex = theSex;
19. hobbies = theHobbies;
20. }
21.
22. /**
23. Gets the customer's age.
24. @return the age
25. */
26. public int getAge() { return age; }
27.
28. /**
29. Gets the customer's sex
30. @return MALE or FEMALE
31. */
32. public int getSex() { return sex; }
33.
34. /**
35. Tests whether this customer has a particular hobby.
36. @param aHobby the hobby to test
37. @return true if this customer has the hobby
38. */
39. public boolean hasHobby(String aHobby)

32 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

40. {
41. if (aHobby == "") return true;
42. for (int i = 0; i < hobbies.length; i++)
43. if (hobbies[i].equals(aHobby)) return true;
44.
45. return false;
46. }
47.
48. /**
49. Resets this customer record to default values.
50. */
51. public void reset()
52. {
53. age = 0;
54. sex = 0;
55. hobbies = null;
56. }
57.
58. public String toString()
59. {
60. String result = "Age: " + age + ", Sex: ";
61. if (sex == Product.MALE) result += "Male";
62. else if (sex == Product.FEMALE) result += "Female";
63. else result += "Male or Female";
64. result += ", Hobbies:";
65. for (int i = 0; i < hobbies.length; i++)
66. result += " " + hobbies[i];
67. return result;
68. }
69.
70. private int age;
71. private int sex;
72. private String[] hobbies;
73. }

Example 5-10. Warehouse.java


1. import java.rmi.*;
2. import java.util.*;
3.
4. /**
5. The remote interface for a warehouse with products.
6. */
7. public interface Warehouse extends Remote
8. {
9. /**
10. Gets products that are good matches for a customer.
11. @param c the customer to match

33 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

12. @return an array list of matching products


13. */
14. ArrayList<Product> find(Customer c) throws
RemoteException;
15. }

Example 5-11. WarehouseImpl.java


1. import java.io.*;
2. import java.rmi.*;
3. import java.util.*;
4. import java.rmi.server.*;
5. import java.util.*;
6. import java.util.concurrent.locks.*;
7.
8. /**
9. This class is the implementation for the remote
10. Warehouse interface.
11. */
12. public class WarehouseImpl
13. extends UnicastRemoteObject
14. implements Warehouse
15. {
16. /**
17. Constructs a warehouse implementation.
18. */
19. public WarehouseImpl()
20. throws RemoteException
21. {
22. products = new ArrayList<ProductImpl>();
23. add(new ProductImpl("Core Java Book", 0, 200,
Product.BOTH, "Computers"));
24. }
25.
26. /**
27. Add a product to the warehouse. Note that this is a
local method.
28. @param p the product to add
29. */
30. public void add(ProductImpl p)
31. {
32. Lock wlock = rwlock.writeLock();
33. wlock.lock();
34. try
35. {
36. products.add(p);
37. }
38. finally

34 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

39. {
40. wlock.unlock();
41. }
42. }
43.
44. public ArrayList<Product> find(Customer c)
45. throws RemoteException
46. {
47. Lock rlock = rwlock.readLock();
48. rlock.lock();
49. try
50. {
51. ArrayList<Product> result = new
ArrayList<Product>();
52. // add all matching products
53. for (ProductImpl p : products)
54. {
55. if (p.match(c)) result.add(p);
56. }
57. // add the product that is a good match for
everyone, a copy of Core Java
58. if (!result.contains(products.get(0)))
59. result.add(products.get(0));
60.
61. // we reset c just to show that c is a copy of the
client object
62. c.reset();
63. return result;
64. }
65. finally
66. {
67. rlock.unlock();
68. }
69. }
70.
71. private ArrayList<ProductImpl> products;
72. private ReadWriteLock rwlock = new
ReentrantReadWriteLock();
73. }

Example 5-12. WarehouseServer.java


1. import java.rmi.*;
2. import java.rmi.server.*;
3. import javax.naming.*;
4.
5. /**
6. This server program instantiates a remote warehouse

35 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

7. object, registers it with the naming service, and waits


8. for clients to invoke methods.
9. */
10. public class WarehouseServer
11. {
12. public static void main(String[] args)
13. {
14. try
15. {
16. System.out.println("Constructing server
implementations...");
17. WarehouseImpl w = new WarehouseImpl();
18. w.add(new ProductImpl("Blackwell Toaster",
Product.BOTH, 18, 200, "Household"));
19. w.add(new ProductImpl("ZapXpress Microwave Oven",
Product.BOTH, 18, 200,
"Household"));
20. w.add(new ProductImpl("DirtDigger Steam Shovel",
Product.MALE, 20, 60,
"Gardening"));
21. w.add(new ProductImpl("U238 Weed Killer",
Product.BOTH, 20, 200, "Gardening"));
22. w.add(new ProductImpl("Persistent Java Fragrance",
Product.FEMALE, 15, 45,
"Beauty"));
23. w.add(new ProductImpl("Rabid Rodent Computer
Mouse", Product.BOTH, 6, 40,
"Computers"));
24. w.add(new ProductImpl("My first Espresso Maker",
Product.FEMALE, 6, 10,
"Household"));
25. w.add(new ProductImpl("JavaJungle Eau de Cologne",
Product.MALE, 15, 45,
"Beauty"));
26. w.add(new ProductImpl("FireWire Espresso Maker",
Product.BOTH, 20, 50,
"Computers"));
27. w.add(new ProductImpl("Learn Bad Java Habits in 21
Days Book", Product.BOTH,
20, 200,
28. "Computers"));
29.
30. System.out.println("Binding server implementations
to registry...");
31. Context namingContext = new InitialContext();
32. namingContext.bind("rmi:central_warehouse", w);
33.

36 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

34. System.out.println("Waiting for invocations from


clients...");
35. }
36. catch (Exception e)
37. {
38. e.printStackTrace();
39. }
40. }
41. }

Example 5-13. WarehouseClient.java


1. import java.awt.*;
2. import java.awt.event.*;
3. import java.io.*;
4. import java.rmi.*;
5. import java.rmi.server.*;
6. import java.util.*;
7. import javax.naming.*;
8. import javax.swing.*;
9.
10. /**
11. The client for the warehouse program.
12. */
13. public class WarehouseClient
14. {
15. public static void main(String[] args)
16. {
17. try
18. {
19. System.setProperty("java.security.policy",
"client.policy");
20. System.setSecurityManager(new
RMISecurityManager());
21.
22. Properties props = new Properties();
23. String fileName = "WarehouseClient.properties";
24. FileInputStream in = new
FileInputStream(fileName);
25. props.load(in);
26. String url = props.getProperty("warehouse.url");
27. if (url == null)
28. url = "rmi://localhost/central_warehouse";
29.
30. Context namingContext = new InitialContext();
31. Warehouse centralWarehouse = (Warehouse)
namingContext.lookup(url);

37 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

32. JFrame frame = new


WarehouseClientFrame(centralWarehouse);
33.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
34. frame.setVisible(true);
35. }
36. catch (Exception e)
37. {
38. e.printStackTrace();
39. }
40. }
41. }
42.
43. /**
44. A frame to select the customer's age, sex, and hobbies,
and to
45. show the matching products resulting from a remote call
to the
46. warehouse.
47. */
48. class WarehouseClientFrame extends JFrame
49. {
50. public WarehouseClientFrame(Warehouse warehouse)
51. {
52. this.warehouse = warehouse;
53. setTitle("WarehouseClient");
54. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
55.
56. JPanel panel = new JPanel();
57. panel.setLayout(new GridLayout(0, 2));
58.
59. panel.add(new JLabel("Age:"));
60. age = new JTextField(4);
61. age.setText("20");
62. panel.add(age);
63.
64. female = new JRadioButton("Female", true);
65. male = new JRadioButton("Male", true);
66. ButtonGroup group = new ButtonGroup();
67. panel.add(female); group.add(female);
68. panel.add(male); group.add(male);
69.
70. panel.add(new JLabel("Hobbies: "));
71. hobbies = new ArrayList<JCheckBox>();
72. for (String h : new String[] { "Gardening", "Beauty",
"Computers", "Household",
"Sports" })

38 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

73. {
74. JCheckBox checkBox = new JCheckBox(h);
75. hobbies.add(checkBox);
76. panel.add(checkBox);
77. }
78.
79. result = new JTextArea(4, 40);
80. result.setEditable(false);
81.
82. JPanel buttonPanel = new JPanel();
83. JButton submitButton = new JButton("Submit");
84. buttonPanel.add(submitButton);
85. submitButton.addActionListener(new
86. ActionListener()
87. {
88. public void actionPerformed(ActionEvent event)
89. {
90. callWarehouse();
91. }
92. });
93.
94. add(panel, BorderLayout.NORTH);
95. add(result, BorderLayout.CENTER);
96. add(buttonPanel, BorderLayout.SOUTH);
97. }
98.
99. /**
100. Call the remote warehouse to find matching products.
101. */
102. private void callWarehouse()
103. {
104. try
105. {
106. ArrayList<String> selected = new
ArrayList<String>();
107. for (JCheckBox checkBox : hobbies)
108. if (checkBox.isSelected())
selected.add(checkBox.getText());
109. Customer c = new
Customer(Integer.parseInt(age.getText()),
110. (male.isSelected() ? Product.MALE : 0)
111. + (female.isSelected() ? Product.FEMALE : 0),
112. selected.toArray(new String[selected.size()]));
113. ArrayList<Product> recommendations =
warehouse.find(c);
114. result.setText(c + "\n");
115. for (Product p : recommendations)

39 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

116. {
117. String t = p.getDescription() + "\n";
118. result.append(t);
119. }
120. }
121. catch (Exception e)
122. {
123. e.printStackTrace();
124. result.setText("Exception: " + e);
125. }
126. }
127.
128. private static final int DEFAULT_WIDTH = 300;
129. private static final int DEFAULT_HEIGHT = 300;
130.
131. private Warehouse warehouse;
132. private JTextField age;
133. private JRadioButton male;
134. private JRadioButton female;
135. private ArrayList<JCheckBox> hobbies;
136. private JTextArea result;
137. }

2/. Passing Remote Objects (Truyền đối tượng từ xa):


- Truyền các đối tượng từ xa từ máy chủ đến máy con thì đơn giản. Máy con
nhận được một đối tượng stub, sau đó lưu nó vào một biến đối tượng với cùng loại
như giao diện từ xa.Máy con có thể truy cập liền các đối tượng hiện tại trên máy
chủ thông qua biến.Máy con có thể sao chép biến này vào máy cục bộ riêng của nó
,những bản sao chép đó được tham chiếu đơn đến cùng stub. Điều quan trọng cần
lưu ý rằng chỉ có các giao diện từ xa mới có thể được truy cập thông qua stub. Một
giao thức từ xa là bất kì giao thức thực hiện mở rộng Remote. Tất cả các phương
thức cục bộ là không thể truy cập thông qua stub.(Một phương thức cục bộ là bất
kỳ phương thức mà không được xác định trong một giao diện từ xa).Các phương
thức cục bộ có thể chỉ chạy trên các máy ảo có chứa các đối tượng hiện tại.

- Tiếp theo, các stub chỉ được tạo ra từ các lớp mà thực hiện một giao diện từ
xa,và chỉ các phương thức được quy định các giao diện là được cung cấp trong các
lớp stub. Nếu một lớp con không thực hiện một giao diện từ xa nhưng một lớp cha
thực hiện điều đó, và một đối tượng của lớp con (subclass) được truyền cho một
phương thức từ xa, chỉ có các phương thức lớp cha (superclass) có thể truy cập. Để
hiểu điều này tốt hơn, xem xét ví dụ sau đây. Chúng ta nhận được một lớp
BookImpl từ ProductImpl.

40 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

class BookImpl extends ProductImpl


{
public BookImpl(String title, String theISBN, int sex, int
age1, int age2, String hobby)
{
super(title + " Book", sex, age1, age2, hobby);
ISBN = theISBN;
}
public String getStockCode() { return ISBN; }
private String ISBN;
}

- Bây giờ, giả sử chúng ta truyền một đối tượng sách đến một phương thức
từ xa,cũng như truyền một tham số hoặc là một giá trị trả về. Người nhận có được
một đối tượng stub, nhưng stub đó không phải là một stub sách. Thay vào đó, nó là
một stub cho lớp cha ProductImpl bởi vì chỉ có lớp mà thực hiện một giao diện từ
xa (xem Hình 5-9). Như vậy, trong trường hợp này, phương thức getStockCode
không có sẵn từ xa.
Hình 5-9. Chỉ có phương thức ProductImpl là từ xa

41 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

-Một lớp từ xa có thể thực hiện đa giao diện.Ví dụ, lớp BookImpl có thể
thực hiện giao diện thứ hai trong việc thêm vào Product. Ở đây, chúng ta định
nghĩa một giao diện từ xa StockUnit và có lớp BookImpl thực hiện nó.

interface StockUnit extends Remote


{
public String getStockCode() throws RemoteException;
}

class BookImpl extends ProductImpl implements StockUnit


{
public BookImpl(String title, String theISBN, int sex, int
age1, int age2, String hobby)
throws RemoteException
{
super(title + " Book", sex, age1, age2, hobby);
ISBN = theISBN;
}

public String getStockCode() throws RemoteException

{
return ISBN;
}

private String ISBN;


}

Hình 5-10 cho thấy sơ đồ thừa kế.


và BookImpl có thêm các phương thức từ xa

42 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Bây giờ, khi một đối tượng sách được truyền đến một phương thức từ xa,
người nhận có được một stub mà đã truy cập tới các phương thức từ xa trong cả
lớp Product và StockUnit. Trong thực tế, ta có thể sử dụng toán tử instanceof
để tìm kết quả ra mà có một đối tượng từ xa cụ thể thực hiện một giao diện hay
không. Dưới đây là một tình huống điển hình mà trong đó ta sẽ sử dụng tính năng
này. Giả sử bạn nhận được một đối tượng từ xa thông qua một biến kiểu Product.

ArrayList<Product> result = centralWarehouse.find(c);


for (Product p : result)
{
. . .
}

- Đối tượng từ xa có lẽ là sách hoặc không phải. Chúng ta muốn sử dụng


instanceof để tìm kết quả ra dù nó có hay không, nhưng chúng ta không thể
kiểm tra

if (p instanceof BookImpl) // wrong


{
BookImpl b = (BookImpl) p;
. . .
}

- Đối tượng p ám chỉ một đối tượng stub, và BookImpl là lớp của đối tượng
máy chủ. Thay vào đó, tìm đến giao thức thứ hai:

if (p instanceof StockUnit)

43 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

{
StockUnit s = (StockUnit) p;
String c = s.getStockCode();
. . .
}

- Đoạn mã này kiểm tra xem có đối tượng stub mà p ám chỉ thực hiện giao
thức StockUnit hay không.Nếu vậy, nó gọi phương thức từ xa getStockCode của
giao diện đó.

Tóm tắt:
 Nếu một đối tượng thuộc về một lớp mà thực hiện một giao diện từ xa sẽ
được truyền tới một phương thức từ xa, phương thức từ xa nhận được một
đối tượng stub.
 Bạn có thể bỏ đối tượng stub đó để bất kỳ giao diện từ xa mà lớp thực hiện
triển khai thực hiện.
 Bạn có thể gọi tất cả các phương thức từ xa được xác định tại các giao diện
đó, nhưng bạn không thể gọi bất kỳ phương thức cục bộ thông qua stub.

3/. Remote Objects and the equals and hashCode Methods(Các đối
tượng từ xa và các phương thức so sánh bằng và mã băm):

- Trong trường hợp một tập băm hoặc bản đồ băm, phương thức hashCode
phải được xác định.Tuy nhiên, có một vấn đề khi cố gắng để so sánh các đối tượng
từ xa.Tìm kết quả ra nếu hai đối tượng từ xa có cùng một nội dung, gọi đến
equals và equals cần liên lạc với máy chủ đang chứa các đối tượng và so sánh
các nội dung đó.Nhưng việc gọi đó có thể thất bại. Tuy nhiên, phương thức
equals trong lớp Object không được mô tả để ném một RemoteException, trong
khi tất cả các phương thức trong một giao diện từ xa phải ném ngoại lệ. Bởi vì một
phương thức lớp con không thể ném nhiều ngoại lệ hơn so với phương pháp lớp
cha mà nó thay thế, ta không thể xác định một phương thức equals trong một giao
diện từ xa.Tương tự cho hashCode.
- Đáng lẽ, các phương thức equals và hashCode trên các đối tượng stub
chỉ cần nhìn vào vị trí của các đối tượng máy chủ. Phương thức equals cho rằng
hai stub bằng nhau nếu chúng chuyển đến đối tượng cùng máy chủ. Hai stub đề cập

44 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

tới các đối tượng máy chủ khác nhau là không bao giờ bằng nhau, ngay cả khi
những đối tượng đó có nội dung giống hệt nhau. Tương tự như vậy, các mã băm
chỉ được tính toán từ định danh đối tượng.

Tóm tắt: ta có thể sử dụng các đối tượng stub trong các bộ và các bảng băm,
nhưng ta phải nhớ rằng kiểm tra tính bằng nhau và băm không tính đến bản kê khai
nội dung của các đối tượng từ xa.

4/. Cloning Remote Objects (Nhân bản các đối tượng từ xa):
- Các stub không có phương thức clone, vì thế ta không thể nhân bản một
đối tượng từ xa bằng cách gọi clone trên stub. Lý do là kỹ thuật có trở lại một
chút. Đó là nếu clone được thực hiện gọi từ xa kêu máy chủ nhân bản các đối
tượng thực hiện, thì phương thức clone sẽ cần được ném một
RemoteException. Tuy nhiên, phương thức clone trong lớp cha Object bảo đảm
sẽ không bao giờ ném bất kỳ ngoại lệ khác với CloneNotSupportedException.
Đó là sự hạn chế tương tự mà ta gặp phải trong phần trước: equals và hashCode
không tìm kiếm tất cả giá trị đối tượng từ xa mà chỉ so sánh các tham chiếu stub.
Tuy nhiên, nó thực hiện clone không hướng, để thực hiện một nhân bản khác của
một stub nếu bạn muốn được có một tham chiếu khác đến đối tượng remote, bạn
chỉ có thể sao chép các biến stub. Vì vậy, clone đơn giản là không được xác định
cho các stub.

V/. Server Object Activation: (Sự hoạt hóa đối tượng máy
chủ ):
- Trong các chương trình mẫu trước, ta sử dụng một chương trình máy chủ
để khởi tạo và ghi nhận các đối tượng để mà các máy con có thể thực hiện gọi từ
xa trên chúng. Tuy nhiên, trong một số trường hợp, thật là lãng phí để khởi tạo rất
nhiều đối tượng máy chủ và chờ đợi các kết nối giữa chúng, mà có hoặc không có
các đối tượng máy con sử dụng chúng. Cơ chế activation cho phép bạn trì hoãn
việc xây dựng đối tượng để cho một đối tượng máy chủ chỉ được xây dựng khi có
ít nhất một máy con gọi một phương thức từ xa.

- Để tận dụng activation ,mã máy con sẽ hoàn toàn không thay đổi. Máy con
chỉ cần yêu cầu một tham chiếu từ xa và thực hiện gọi nó.
- Tuy nhiên, chương trình máy chủ được thay thế bằng một chương trình
activation mà các cấu trúc activation descriptors (mô tả sự hoạt hóa) của các đối
tượng mà sẽ được xây dựng tại một thời gian sau đó, và liên kết với người nhận từ

45 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

các lần gọi phương thức từ xa với dịch vụ tên. Khi một lần gọi được thực hiện lần
đầu tiên, thông tin trong mô tả sự hoạt hóa được sử dụng để xây dựng đối tượng.
- Một đối tượng máy chủ được sử dụng theo cách này nên mở rộng lớp
Activatable và tất nhiên, thực hiện một hoặc nhiều giao diện từ xa. Ví dụ:

class ProductImpl
extends Activatable
implements Product
{
. . .
}
- Bởi vì việc xây dựng đối tượng được hoãn cho đến một thời gian sau
đó,nên nó phải xảy ra trong một hình thức tiêu chuẩn hóa. Vì vậy, ta phải cung cấp
một hàm tạo có hai tham số:
+Một ID hoạt hóa (trong đó bạn chỉ cần truyền vào cho hàm tạo lớp cha)
+Một đối tượng duy nhất đang chứa tất cả thông tin xây dựng,được bọc
trong lớp MarshalledObject.

- Nếu bạn cần nhiều tham số xây dựng, bạn phải gói chúng trong một đối
tượng duy nhất. Bạn luôn có thể sử dụng một mảng Object[] hoặc một lớp
ArrayList. Như ta sẽ sớm thấy, ta đặt một bản sao được tuần tự (hoặc sắp xếp
theo thứ tự) của thông tin xây dựng bên trong mô tả sự hoạt hóa. Hàm tạo đối
tượng máy chủ của ta nên sử dụng phương thức get của lớp MarshalledObject
để không tuần tự hóa thông tin xây dựng.
- Trong trường hợp của lớp ProductImpl, thủ tục này khá đơn giản,chỉ một
đoạn thông tin là cần thiết để xây dựng, cụ thể là, tên sản phẩm. Thông tin đó có
thể được bao bọc trong lớp MarshalledObject và không được bao bọc trong hàm
tạo:
public ProductImpl(ActivationID id, MarshalledObject data)
{
super(id, 0);
name = (String) data.get();;
System.out.println("Constructed " + name);
}
- Bằng cách truyền 0 như tham số thứ hai của hàm tạo lớp cha ,chúng ta chỉ
ra rằng thư viện RMI nên chỉ định số cổng phù hợp đến cổng sự lắng
nghe(listener).

- Hàm tạo này in một tin nhắn để ta có thể thấy rằng các đối tượng sản
phẩm được hoạt hóa theo yêu cầu.

46 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Chú ý : các đối tượng máy chủ của bạn không thực sự mở rộng lớp
Activatable. Nếu chúng không làm điều đó thì đặt gọi phương thức tĩnh
Activatable.exportObject(this, id, 0) trong hàm tạo của lớp máy chủ.

- Bây giờ chúng ta chuyển sang chương trình sự hoạt hóa. Trước tiên, ta cần
phải xác định một nhóm hoạt hóa. Một nhóm hoạt hóa mô tả các thông số chung
cho việc khởi động máy ảo có chứa các đối tượng máy chủ. Thông số quan trọng
nhất là security policy (cách xử sự bảo mật).
- Cũng như với các đối tượng máy chủ khác của chúng ta, chúng ta không
kiểm tra bảo mật. (Có lẽ, các đối tượng đến từ một nguồn đáng tin cậy.) Tuy nhiên,
máy ảo (mà trong đó các đối tượng được hoạt hóa chạy) có một sự quản lý bảo mật
được cài đặt. Để kích hoạt tất cả các quyền truy cập, cung cấp một tập tin
server.policy với nội dung sau:
grant
{
permission java.security.AllPermission;
};
- Xây dựng một mô tả nhóm hoạt hóa như sau:
Properties props = new Properties();
props.put("java.security.policy", "/server/server.policy");
ActivationGroupDesc group = new ActivationGroupDesc(props,
null);
- Tham số thứ hai mô tả các tùy chọn lệnh đặc biệt, chúng ta không cần bất
kỳ cho ví dụ này, vì vậy chúng ta truyền một tham chiếu null.
- Tiếp theo, tạo ra một ID nhóm với việc gọi
ActivationGroupID id =
ActivationGroup.getSystem().registerGroup(group);
- Bây giờ ta đã sẵn sàng để xây dựng các mô tả hoạt hóa. Đối với mỗi đối
tượng mà phải được xây dựng trên nhu cầu, bạn cần những điều sau đây:
 ID nhóm hoạt hóa cho máy ảo mà trong đó đối tượng cần được xây dựng;
 Tên của lớp (như "ProductImpl" hoặc "com.mycompany.MyClassImpl");
 Chuỗi URL mà từ đó để tải các tập tin lớp. Điều này cần có các địa chỉ URL
cơ bản, không bao gồm các đường dẫn gói;
 Thông tin xây dựng được sắp xếp theo thứ tự.

Ví dụ:
MarshalledObject param = new MarshalledObject("Blackwell
Toaster");
ActivationDesc desc = new ActivationDesc(id, "ProductImpl",
http://myserver.com/download /, param);

47 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Truyền mô tả đến phương thức Activatable.register tĩnh. Nó trả về


một đối tượng của một số lớp mà thực hiện các giao diện từ xa của lớp thực hiện.
Bạn có thể ràng buộc các đối tượng với dịch vụ tên:
Product p = (Product) Activatable.register(desc);
namingContext.bind("toaster", p);

- Không giống như các chương trình máy chủ trong những ví dụ trước,
chương trình hoạt hóa thoát sau khi ghi nhận và bắt buộc người nhận hoạt hóa. Các
đối tượng máy chủ được xây dựng chỉ khi gọi phương thức từ xa đầu tiên xảy ra.

- Ví dụ 5-14 và 5-15 cho thấy đoạn mã cho việc thực hiện sản phẩm có thể
được hoạt hóa và chương trình hoạt hóa. Giao diện sản phẩm và chương trình máy
con là không được thay đổi.

- Để khởi động chương trình này, làm theo các bước sau:
1) Biên dịch tất cả các tập tin nguồn.
2) Nếu bạn sử dụng JDK 1.4 hoặc phiên bản thấp hơn , chạy rmic để tạo ra
một stub (mạch nhánh) cho lớp ProductImpl:
rmic -v1.2 ProductImpl
3) Bắt đầu đăng ký RMI.
4) Bắt đầu hoạt hóa RMI.
rmid -J-Djava.security.policy=rmid.policy &
hoặc
start rmid -J-Djava.security.policy=rmid.policy

+ Chương trình rmid nghe yêu cầu hoạt hóa và hoạt hóa các đối tượng
trong một máy ảo riêng biệt. Để khởi động một máy ảo, chương trình rmid cần
quyền truy cập nhất định. Điều đó được quy định trong một tập tin xử sự (xem Ví
dụ 5-16). Sử dụng tùy chọn –J để truyền một tùy chọn đến máy ảo đang chạy hoạt
hóa.
5) Chạy chương trình hoạt hóa.Trong cài đặt này, chúng ta giả định rằng ta bắt
đầu chương trình trong bảng kiểm kê chứa các tập tin lớp và các tập tin xử
sự máy chủ.
java ProductActivator
+ Chương trình thoát sau khi người nhận hoạt hóa đã được đăng ký
xong với dịch vụ tên.
6) Chạy chương trình máy con
java -Djava.security.policy=client.policy ProductClient
+ Máy con sẽ in các mô tả sản phẩm quen thuộc. Khi ta chạy máy con
lần đầu tiên, ta cũng sẽ thấy các thông điệp hàm tạo trong cửa sổ vỏ máy chủ.

48 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Example 5-14. ProductImpl.java


1. import java.rmi.*;
2. import java.rmi.server.*;
3.
4. /**
5. This is the implementation class for the remote product
6. objects.
7. */
8. public class ProductImpl
9. extends UnicastRemoteObject
10. implements Product
11. {
12. /**
13. Constructs a product implementation
14. @param n the product name
15. */
16. public ProductImpl(String n) throws RemoteException
17. {
18. name = n;
19. }
20.
21. public String getDescription() throws RemoteException
22. {
23. return "I am a " + name + ". Buy me!";
24. }
25.
26. private String name;
27. }

Example 5-15. ProductActivator.java


1. import java.io.*;
2. import java.net.*;
3. import java.rmi.*;
4. import java.rmi.activation.*;
5. import java.util.*;
6. import javax.naming.*;
7.
8. /**
9. This server program activates two remote objects and
10. registers them with the naming service.
11. */
12. public class ProductActivator
13. {
14. public static void main(String args[])
15. {
16. try

49 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

17. {
18. System.out.println("Constructing activation
descriptors...");
19.
20. Properties props = new Properties();
21. // use the server.policy file in the current
directory
22. props.put("java.security.policy", new
File("server.policy").getCanonicalPath());
23. ActivationGroupDesc group = new
ActivationGroupDesc(props, null);
24. ActivationGroupID id =
ActivationGroup.getSystem().registerGroup(group);
25. MarshalledObject p1param = new
MarshalledObject("Blackwell Toaster");

26. MarshalledObject p2param = new


MarshalledObject("ZapXpress Microwave

Oven");
27.
28. String classDir = ".";
29. // turn the class directory into a file URL
30. // for this demo we assume that the classes are in
the current dir
31. // we use toURI so that spaces and other special
characters in file names are
// escaped
32. String classURL = new
File(classDir).getCanonicalFile().toURI().toString();
33.
34. ActivationDesc desc1 = new ActivationDesc(id,
"ProductImpl", classURL,

p1param);
35. ActivationDesc desc2 = new ActivationDesc(id,
"ProductImpl", classURL,

p2param);
36.
37. Product p1 = (Product) Activatable.register(desc1);
38. Product p2 = (Product) Activatable.register(desc2);
39.
40. System.out.println("Binding activable
implementations to registry...");
41. Context namingContext = new InitialContext();
42. namingContext.bind("rmi:toaster", p1);

50 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

43. namingContext.bind("rmi:microwave", p2);


44. System.out.println("Exiting...");
45. }
46. catch (Exception e)
47. {
48. e.printStackTrace();
49. }
50. }
51. }

Example 5-16. rmid.policy


1. grant
2. {
3. permission com.sun.rmi.rmid.ExecPermission
4. "${java.home}${/}bin${/}java";
5. permission com.sun.rmi.rmid.ExecOptionPermission
6. "-Djava.security.policy=*";
7. };

Example 5-17. server.policy


1. grant
2. {
3. permission java.security.AllPermission;
4. };

*API(Application Programming Interface)-giao diện chương trình ứng dụng :


java.rmi.activation.Activatable 1.2
 protected Activatable(ActivationID id, int port)
xây dựng các đối tượng có thể hoạt hóa và thiết lập một sự lắng nghe trên
cổng đã định. Sử dụng 0 cho cổng để có một cổng được chỉ định tự động.
 static Remote exportObject(Remote obj, ActivationID id, int
port)
thực hiện một đối tượng từ xa có thể hoạt hóa. Trả về người nhận hoạt hóa
mà cần được làm có hiệu lực cho người gọi từ xa. Sử dụng 0 cho cổng để có
một cổng được chỉ định tự động.
 static Remote register(ActivationDescriptor desc)
đăng ký mô tả cho một đối tượng có thể hoạt hóa và chuẩn bị nó cho việc
nhận thao tác gọi từ xa.Trả về người nhận hoạt hóa mà cần được làm cho có
hiệu lực từ người gọi từ xa.

java.rmi.MarshalledObject 1.2
 MarshalledObject(Object obj)

51 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

xây dựng một đối tượng đang chứa các dữ liệu tuần tự của một đối tượng đã
cho .
 Object get()
Không tuần tự dữ liệu của đối tượng được lưu trữ và trả về đối tượng.

java.rmi.activation.ActivationGroupDesc 1.2
 ActivationGroupDesc(Properties props,

ActivationGroupDesc.CommandEnvironment env)
xây dựng một mô tả nhóm hoạt hóa mà chỉ rõ những đặc tính máy ảo cho một
máy ảo mà máy chủ hoạt hóa các đối tượng. Thông số env chứa đường dẫn đến
máy ảo có thể thực thi và các tùy chọn lệnh- dòng, hoặc nó là null nếu không
có thiết lập đặc biệt được yêu cầu.

java.rmi.activation.ActivationGroup 1.2
 static ActivationSystem getSystem()
trả về một tham chiếu đến hệ thống kích hoạt.

java.rmi.activation.ActivationSystem 1.2
 ActivationGroupID registerGroup(ActivationGroupDesc group)
đăng ký một nhóm hoạt hóa và trả về ID của nhóm.

java.rmi.activation.ActivationDesc 1.2
 ActivationDesc(ActivationGroupID id, String className,
String classFileURL,MarshalledObject data)
Hàm tạo một mô tả hoạt hóa.

VI/. Java IDL and CORBA:


- Ở phần trước các bạn đã làm quen với khái niệm lập trình phân tán bằng kỹ
thuật RMI của Java. Phần này sẽ cung cấp cho bạn 1 khái niệm lập trình phân tán
mở rộng hơn với các đối tượng được thiết kế trên nền CORBA áp dụng cho mọi
ngôn ngữ lập trình.

- Không giống như RMI, CORBA cho phép bạn thực hiện phép gọi giữa các
đối tượng Java với những đối tượng được viết bằng ngôn ngữ khác. Để gọi được
lẫn nhau thì cần phải thông qua chương trình môi giới trung gian của CORBA gọi
là Object Request Broker-ORB(Trình môi giới các yêu cầu của đối tượng) tồn tại
cả trên sever lẫn client. Các tính năng của CORBA 2 định nghĩa 1 loạt các dịch vụ
mà ORB có thể sử dụng cho những tác vụ quản lý khác nhau.

52 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- JDK 1.2 đã giới thiệu đầy đủ 1 ORB CORBA 2-compliant, đem lại những
ứng dụng Java và applet khả năng kết nối tới những đối tượng CORBA từ xa.

- Sau đây là những bước cho việc thực thi những đối tượng CORBA:
1. Viết interface cho đối tượng và chỉ định rõ đối tượng đó thi hành như
thế nào, sử dụng IDL(interface definition language) cho việc định
nghĩa interface CORBA. IDL là 1 ngôn ngữ đặt biệt dùng để định rõ
các interface trong/bằng 1 dạng ngôn ngữ trung gian(language-
neutral).
2. Sử dụng trình biên dịch IDL cho ngôn ngữ đích(target language) tạo
ra stub cần thiết và lớp trợ giúp(helper classes).
3. Thêm code thực thi cho những đối tượng server, ngôn ngữ viết thì tùy
bạn. (Khung sườn được tạo ra bởi trình biên dịch IDL chỉ là glue
code(thành phần kết hợp thông qua các kết nối đặc biệt nối 2 interface
khác loại). Bạn vẫn cần cung cấp code thực thi cho những phương
thức server(server methods).Và biên dịch code thực thi đó.
4. Viết 1 chương trình cho server nhằm tạo và ghi nhận các đối tượng
của server. Phương thức thuận tiện nhất cho việc ghi nhận là sử dụng
dịch vụ naming của CORBA(CORBA naming service), dịch vụ này
tương tự như rmiregistry
5. Viết 1 chương trình cho client để xác định vị trí của các server object
và gọi các dịch vụ dựa và chúng.
6. Khởi động naming service và chương trình server trên server cũng
như chương trình client trên client.

- Các bước trên khá giống với những bước mà bạn dùng để xây dựng các ứng dụng
phân tán với RMI, nhưng có 2 sự khác niệt quan trọng là:
1. Bạn có thể sử dụng bất kỳ ngôn ngữ nào với 1 liên kết CORBA để
thực thi trên client và server.
2. Bạn dùng IDL để định rõ các interface.
- Phần tiếp theo mình sẽ trình bày về việc làm sao dùng IDL để định nghĩa các giao
diện CORBA(CORBA interface). Tuy nhiên, CORBA là 1 chủ đề phức tạp nên sẽ
giới thiệu vài ví dụ đơn giản để bạn biết làm sao để bắt đầu

1/. The Interface Definition Language(Ngôn ngữ đặc tả giao diện):


- Để giới thiệu cú pháp IDL, chúng ta sẽ chạy thử các ví dụ minh họa mà đã
dùng cho RMI. Trong RMI, bạn bắt đầu 1 interface bằng ngôn ngữ lập trình Java.
Còn trong CORBA, thì bắt đầu 1 interface bằng cú pháp IDL như sau:
interface Product
{

53 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

string getDescription();
};

- Có 1 vài khác biệt khó nhận thấy giữa IDL và Java. Trong IDL thì việc kết
thúc định nghĩa interface bằng bằng dấu chấm phẩy. Để ý là string được viết
thường. Thực ra lớp string này liên quan tới khái niệm của CORBA về 1 chuỗi,
mà nó khác với 1 chuỗi trong Java. Trong ngôn ngữ lập trình Java, chuỗi bao gồm
16-bit kí tự Unicode. Còn trong CORBA thì chuỗi chỉ chứa 8-bit kí tự. Nếu bạn
gửi chuỗi 16-bit qua CORBA và chuỗi đó có những ký tự với byte cao khác
không(nonzero high byte), thì 1 ngoại lệ sẽ được đưa vào.

- Trình biên dịch “IDL sang Java” (trình biên dịch Java IDL) sẽ chuyển các
định nghĩ của IDL sang định nghĩa các interface của Java. Ví dụ, giả sử bạn đưa
định nghĩa IDL Product vào 1 file Product.idl và chạy câu lệnh idlj
Product.idl thì kết quả là 1 file ProductOperations.java với nội dung như
sau:

interface ProductOperations
{
String getDescription();
}

- Cùng với 1 file Product.java định nghĩa 1 interface như sau:

public interface Product extends


ProductOperations,
org.omg.CORBA.Object,
org.omg.CORBA.portable.IDLEntity
{
}

- Các quy tắc điều khiển việc chuyển đổi từ IDL sang ngôn ngữ lập trình
Java được gọi chung là các ràng buộc trong ngôn ngữ lập trình Java. Những ràng
buộc trong ngôn ngữ được tiêu chuẫn hóa bởi OMG. Tất cả các nhà cung cấp
CORBA đều được yêu cầu sử dụng những quy tắc chung cho việc ánh
xạ(mapping) cấu trúc IDL sang 1 ngôn ngữ lập trình cụ thể.

2/. Một số khái niệm quan trọng mà bất cứ người sử dụng IDL nào
cũng cần phải biết như:

54 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Trong IDL bạn dùng từ khóa sequence để định nghĩa mảng giá trị, bạn
phải định nghĩa 1 kiểu dữ liệu trước khi bạn khai báo tham số sequence .VD như
ta định nghĩa 1 chuỗi các sản phẩm như sau:
typedef sequence<Product> ProductSeq;

- Ta dùng nó trong khai báo phương thức sau:


interface Warehouse
{
ProductSeq find(in Customer c);
. . .
};

- Trong ngôn ngữ Java thì sequence tương ứng với array , VD như
phương thức find được ánh xạ qua ngôn ngữ Java là:
Product[] find(Customer c)

- Để đưa exception vào trong phương thức, đầu tiên ta phải định nghĩa kiểu
exception đó, sau đó dùng khai báo raises .VD như:
interface Warehouse
{
exception BadCustomer { string reason; };
ProductSeq find(in Customer c) raises BadCustomer;
. . .
};

- Bộ biên dịch IDL sẽ chuyển exception thành class:


final public class BadCustomer
extends org.omg.CORBA.UserException
{
public BadCustomer() {}
public BadCustomer(String __reason) { reason =
__reason; }
public String reason;
}
+Còn raises sẽ chuyển thành throws trong phương thức của
Java:
ProductSeq find(Customer c) throws BadCustomer

- Khai báo hằng trong interface:


interface Warehouse
{
const int SOLD_OUT = 404;
. . .
};

55 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Thuộc tính trong interface ta dùng từ khóa attribute ,VD như ta có


thuộc tính isbn trong interface Book như sau:
interface Book
{
attribute string isbn;
. . .
};

- Còn trong Java thì nó tương ứng với:


String isbn() // accessor
void isbn(String __isbn) // mutator

+ Nếu thuộc tính được khai báo là readonly thì không có phương thức
mutator được tạo ra

- CORBA có hỗ trợ cho việc thừa kế interface, VD như:


interface Book : Product { /* . . . */ };
+ Bạn sử dụng dấu : để chỉ việc thừa kế, 1 interface có thể thừa kế
từ nhiều interface

- Trong IDL, bạn có thể nhóm định nghĩa của các interface, các kiểu, các
hằng, các exception(ngoại lệ) vào trong 1 module:
module corejava
{
interface Product
{
. . .
};

interface Warehouse
{
. . .
};
};

- module được chuyển thành package trong Java

- Một khi bạn đã có file IDL, bạn chạy trình biên dịch IDL để có các lớp stub và
các lớp helper cho ngôn ngữ lập trình đích của bạn như Java hay C++.VD :
+ Để chuyển từ file IDL sang Java, bạn dùng lệnh idlj như sau:
idlj Product.idl
->Nó sẽ tạo ra 5 file nguồn đó là:

56 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

 Product.java , định nghĩa interface


 ProductOperations.java, interface chứa các thao tác
hiện thời
 ProductHolder.java , lớp holder cho các tham số out
 ProductHelper.java, lớp helper
 _ProductStub.java, lớp stub để giao tiếp với ORB

+ Để chuyển từ file IDL sang C++, ta dùng lệnh omniidl để tạo ra


C++ stub:
omniidl -bcxx Product.idl
->Nó sẽ tạo ra 2 file C++ như sau:
 ProductSK.cc , 1 file header định nghĩa các lớp Product,
Product_Helper và POA_Product
 ProductSK.cc,1 file C++ chứa code nguồn cho các lớp
trên
=>Trình biên dịch IDL sang C++ của những nhà cung cấp khác nhau sẽ
tạo ra các file khác nhau

3/. A CORBA Example:


- Trong ví dụ sau, ta sẽ trình bày cách làm sao gọi 1 đối tượng C++ trên
sever từ 1 client Java, sử dụng hỗ trợ của CORBA trong JDK. Về phía server ta sử
dụng omniORB, 1 mã nguồn mở ORB có thể tải từ http://omniorb.sourceforge.net.

- Ví dụ về đối tượng C++ trên server đơn giản là giá trị của 1 biến trên
server. Interface như sau:
interface Env
{
string getenv(in string name);
};

- Đoạn chương trình Java sau đây thu được giá trị của biến PATH trong quá
trình đối tượng trên server chạy:
Env env = . . .;
String value = env.getenv("PATH")

- Code C++ cho interface này thực sự đơn giản, ta chỉ việc gọi phương
thức getenv trong thư viện chuẩn của C:
class EnvImpl

57 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

: public POA_Env, public PortableServer::RefCountServantBase


{
public:
virtual char* getenv(const char *name)
{
char* value = std::getenv(name);
return CORBA::string_dup(value);
}
};

- Bạn không cần phải hiểu code C++ trong phần này vì nó chỉ là 1 mẩu
trong code mà bạn muốn đóng gói trong 1 đối tượng CORBA nên bạn có thể gọi
nó từ chương trình Java.

-Trên Server, bạn viết 1 chương trình C++ theo yêu cầu sau:
1. Khởi động ORB.
2. Tạo 1 đối tượng cho lớp EnvImpl và lưu nó với ORB.
3. Sử dụng name server để ràng buộc đối tượng với 1 cái tên nào đó.
4. Chờ lời gọi từ client

- Bạn có thể tìm chương trình đó trong ví dụ 5-19 ở phần cuối.

- Tiếp theo là phần code cho client. Bạn đã biết được làm sao để gọi 1
phương thức từ đối tượng trên server một khi bạn có tham chiếu(reference) tới đối
tượng từ xa.Tuy nhiên, để có được tham chiếu đó, bạn phải đi qua 1 bộ mumbo-
jumbo khác so với cách của RMI
- Đầu tiên bạn khởi tạo ORB. ORB đơn giản là 1 thư viện code trình bày
cách thức làm sao để “nói chuyện” được với các ORB khác và làm sao để sắp xếp
thứ tự hoặc không thừ tự cho các tham số.
ORB orb = ORB.init(args, null);

- Tiếp theo là bạn xác định naming service nhằm giúp bạn xác định được
vị trí những đối tượng khác. Tuy nhiên trong CORBA, naming service chỉ là 1 đối
tượng CORBA khác. Để gọi được naming service , bạn cần xác định vị trí của nó.
Trong CORBA 1 thì đây là 1 vần đề lớn bởi vì không có tiêu chuẩn nào để có thể
tham chiếu đến nó. Tuy nhiên, CORBA 2 ORB sẽ cho bạn vị trí của những dịch vụ
tiêu chuẩn bằng tên:
String[] services = orb.list_initial_services();

58 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Naming service có tên tiêu chuẩn là NameService . Hầu hết các ORB
đều có các dịch vụ cộng thêm lúc đầu, chẳng hạn như dịch vụ RootPOA cho phép
truy cập tới gốc của Portable Object Adaptor

- Để có được 1 đối tượng tham chiếu tới dịch vụ, bạn sử dụng phương thức
resolve_initial_references . Nó return 1 đối tượng CORBA chung, chẳng
hạn như lớp org.omg.corba.Object .Nếu bạn chỉ sử dụng Object thì trình biên
dịch sẽ hiểu là java.lang.Object ,sử dụng:
org.omg.CORBA.Object object = orb.resolve_initial_references("NameService");

- Tiếp theo, chuyển tham chiếu này thành tham chiếu NamingContext nên
bạn có thể gọi phương thức của interface NamingContext .Trong RMI, bạn đơn
giản chỉ nhắc lại tham chiếu sang 1 kiểu khác.Tuy nhiên trong CORBA thì bạn
không thể làm vậy được:
NamingContext namingContext = (NamingContext) object; // ERROR

- Thay vào đó, bạn phải dùng phương thức narrow của lớp helper của
interface đích
NamingContext namingContext = NamingContextHelper.narrow(object);
->sử dụng phương thức narrow để chuyển tham chiếu đối tượng CORBA
sang subtype

- Hiện giờ bạn đã có naming context,bạn có thể dùng nó để xác định vị trí
của đối tượng. Naming context kết hợp tên gọi với các đối tượng server

- Trong ví dụ của chúng ta thì chương trình server đặt đối tượng EnvImpl
theo tên gọi được mô tả bởi chuỗi sau:
(id="corejava", kind="Context"), (id="Env", kind="Object")

- Chúng ta truy hồi lại tham số từ xa từ nó bằng cách khai báo 1 mảng các
name compoment và truyền nó tới phương thức resolve của interface
NamingContext
NameComponent[] path =
{
new NameComponent("corejava", "Context"),
new NameComponent("Env", "Object")
};
org.omg.CORBA.Object envObj = namingContext.resolve(path);
- Một lần nữa, chúng ta phải thu hẹp các kết quả của tham chiếu đối tượng:
Env env = EnvHelper.narrow(envObj);

59 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Bây giờ chúng ta đã sẵn sàng gọi phương thức từ xa:


String value = env.getenv("PATH");

- Bạn sẽ tìm được code hoàn chỉnh ở Ví dụ 5-18

- Đây là các bước thực hiện trong chương trình client điển hình:
1. Khởi động ORB
2. Định vị trí của naming service bằng cách truy hồi lại tham chiếu ban
đầu tới "NameService" và thu hẹp nó từ tham chiếu
NamingContext
3. Định vị trí đối tượng của phương thức mà bạn muốn gọi bằng cách
thu thập tên của nó và gọi phương thức resolve của NamingContext
4. Thu hẹp đối tượng trả về tới khi đúng và gọi phương thức bạn muốn.

- Để kiểm nghiệm chương trình này, các bạn làm như sau:
1. Biên dịch file IDL, sử dụng cả trình biên dịch C++ và Java.
omniidl -bcxx Env.idl
idlj Env.idl
2. Biên dịch chương trình C++ trên server. Những câu lệnh được biên
dịch phụ thuộc vào ORB, chẳng hạn với OmniORB trên Unix thì bạn
dùng:
g++
s-o EnvServer
-D__x86__ -D__linux__ -D__OSVERSION__=2
-I/usr/local/include/omniORB4
EnvServer.cpp EnvSK.cc
-lomniORB4 -lomnithread -lpthread
3. Biên dịch chương trình Java trên client.
4. Khởi động dịch vụ Naming Service trên server, có thể sử dụng chương
trình orbd đi kèm với bộ JDK hay dịch vụ Naming Service trên ORB
của bạn, chẳng hạn như omniNames nếu bạn dùng omniORB. Dịch vụ
Naming Service này sẽ chạy cho tới khi nào bạn dừng nó thì thôi
+ Để bắt đầu orbd , bạn chạy orbd -ORBInitialPort
2809 &
+ Ngoài ra để bắt đầu omniNames , bạn chạy lệnh:
omniNames -ORBsupportBootstrapAgent 1 &
5. Khởi động server:
./EnvServer -ORBInitRef
NameService=corbaname::localhost:2809 &

6. Chạy client:
java EnvClient -ORBInitialPort 2809

60 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Nếu như server ở trên 1 máy từ xa hay port ban đầu của server ORB
không giống với giá trị mặc định của Java IDL là 900 thì hãy đặt giá trị cho thuộc
tính của ORBInitialHost và ORBInitialPort .Ví dụ như, OmniORB sử dụng
port 2809, còn khi sử dụng orbd thì port cũng là 2809 bởi vì ta cần phải có quyền
root để khởi động 1 dịch vụ trên cổng 1024 trong Unix/Linux

- Có 2 phương thức để cài đặt các thuộc tính đó. Bạn có thể cài đặt thuộc
tính cho hệ thống:
org.omg.CORBA.ORBInitialHost
org.omg.CORBA.ORBInitialPort

- Chẳng hạn, bằng việc khởi động bộ biên dịch Java với lựa chọn –D , bạn
có thể chỉ định giá trị trên dòng lệnh:
java EnvClient -ORBInitialHost warthog -ORBInitialPort
2809

- Tham số dòng lệnh sẽ được truyền tới ORB bằng cách gọi :
ORB orb = ORB.init(args, null);

- Nếu client của bạn không tìm thấy dịch vụ Naming Service ,hãy cố gắng
đặt giá trị port ban đầu cho cả server lẫn client cùng 1 giá trị

*Mẹo:
+ Nếu bạn gặp rắc rối trong việc kết nối tới Naming Service , hãy in ra 1
danh sách các dịch vụ ban đầu mà ORB của bạn định được vị trí
public class ListServices
{
public static void main(String args[]) throws Exception
{
ORB orb = ORB.init(args, null);
String[] services = orb.list_initial_services();
for (int i = 0; i < services.length; i++)
System.out.println(services[i]);
}
}
+ Với 1 vài ORB, NameService sẽ không nằm trong danh sách đó. Trong
trường hợp này, hãy chuyển sang kế hoạch B, định vị các đối tượng server bằng
cách dùng Interoperable Object Reference(IOR) của chính nó

- Trong phần này, bạn đã thấy được làm cách nào để kết nối tới 1 server mà
được thực hiện bằng C++. Bạn có thể gói gọn các dịch vụ kế thừa vào trong những

61 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

đối tượng CORBA và có thể truy xuất tới chúng từ chương trình Java của bạn, mà
không cần phải triển khai thêm các hệ thống phần mềm trên client.

4/. Ví dụ về server được thực thi bằng Java còn client bằng C++:
Ví dụ 5-18: EnvClient.java
1. import org.omg.CosNaming.*;
2. import org.omg.CORBA.*;
3.
4. public class EnvClient
5. {
6. public static void main(String args[])
7. {
8. try
9. {
10. ORB orb = ORB.init(args, null);
11. org.omg.CORBA.Object namingContextObj =
orb.resolve_initial_references
("NameService");
12. NamingContext namingContext =
NamingContextHelper.narrow(namingContextObj);
13.
14. NameComponent[] path =
15. {
16. new NameComponent("corejava", "Context"),
17. new NameComponent("Env", "Object")
18. };
19. org.omg.CORBA.Object envObj =
namingContext.resolve(path);
20. Env env = EnvHelper.narrow(envObj);
21. System.out.println(env.getenv("PATH"));
22. }
23. catch (Exception e)
24. {
25. e.printStackTrace();
26. }
27. }
28. }

Ví dụ 5-19: EnvServer.cpp
1. #include <iostream>
2. #include <cstdlib>
3.
4. #include "Env.hh"
5.
6. using namespace std;
7.

62 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

8. class EnvImpl :
9. public POA_Env,
10. public PortableServer::RefCountServantBase
11. {
12. public:
13. virtual char* getenv(const char *name);
14. };
15.
16. char* EnvImpl::getenv(const char *name)
17. {
18. char* value = std::getenv(name);
19. return CORBA::string_dup(value);
20. }
21.
22. static void bindObjectToName(CORBA::ORB_ptr orb, const char
name[], CORBA::Object_ptr
objref)
23. {
24. CosNaming::NamingContext_var rootContext;
25.
26. try
27. {
28. // Obtain a reference to the root context of the name
service:
29. CORBA::Object_var obj;
30. obj = orb->resolve_initial_references("NameService");
31.
32. // Narrow the reference returned.
33. rootContext = CosNaming::NamingContext::_narrow(obj);
34. if(CORBA::is_nil(rootContext))
35. {
36. cerr << "Failed to narrow the root naming
context." << endl;
37. return;
38. }
39. }
40. catch (CORBA::ORB::InvalidName& ex)
41. {
42. // This should not happen!
43. cerr << "Service required is invalid [does not
exist]." << endl;
44. return;
45. }
46.
47. try
48. {
49. CosNaming::Name contextName;

63 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

50. contextName.length(1);
51. contextName[0].id = (const char*) "corejava";
52. contextName[0].kind = (const char*) "Context";
53.
54. CosNaming::NamingContext_var corejavaContext;
55. try
56. {
57. // Bind the context to root.
58. corejavaContext = rootContext-
>bind_new_context(contextName);
59. }
60. catch (CosNaming::NamingContext::AlreadyBound& ex)
61. {
62. // If the context already exists, this exception
will be raised. In this
case, just
63. // resolve the name and assign the context to the
object returned:
64. CORBA::Object_var obj;
65. obj = rootContext->resolve(contextName);
66. corejavaContext =
CosNaming::NamingContext::_narrow(obj);
67. if( CORBA::is_nil(corejavaContext) )
68. {
69. cerr << "Failed to narrow naming context." <<
endl;
70. return;
71. }
72. }
73.
74. // Bind objref with given name to the context:
75. CosNaming::Name objectName;
76. objectName.length(1);
77. objectName[0].id = name;
78. objectName[0].kind = (const char*) "Object";
79.
80. try
81. {
82. corejavaContext->bind(objectName, objref);
83. }
84. catch (CosNaming::NamingContext::AlreadyBound& ex)
85. {
86. corejavaContext->rebind(objectName, objref);
87. }
88. }
89. catch (CORBA::COMM_FAILURE& ex)
90. {

64 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

91. cerr << "Caught system exception COMM_FAILURE--unable


to contact the naming
service."
92. << endl;
93. }
94. catch (CORBA::SystemException&)
95. {
96. cerr << "Caught a CORBA::SystemException while using
the naming service." << endl;
97. }
98. }
99.
100. int main(int argc, char *argv[])
101. {
102. cout << "Creating and initializing the ORB..." << endl;
103.
104. CORBA::ORB_var orb = CORBA::ORB_init(argc, argv,
"omniORB4");
105.
106. CORBA::Object_var obj = orb-
>resolve_initial_references("RootPOA");
107. PortableServer::POA_var poa =
PortableServer::POA::_narrow(obj);
108. poa->the_POAManager()->activate();
109.
110. EnvImpl* envImpl = new EnvImpl();
111. poa->activate_object(envImpl);
112.
113. // Obtain a reference to the object, and register it in
the naming service.
114. obj = envImpl->_this();
115.
116. cout << orb->object_to_string(obj) << endl;
117. cout << "Binding server implementations to registry..."
<< endl;
118. bindObjectToName(orb, "Env", obj);
119. envImpl->_remove_ref();
120.
121. cout << "Waiting for invocations from clients..." <<
endl;
122. orb->run();
123.
124. return 0;
125. }

65 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

5/. Locating Objects Through IORs(Định vị đối tượng thông qua


IORs):
- Nếu bạn không thể cấu hình cho server ORB của bạn và dịch vụ Naming
Service thì bạn vẫn có thể định vị được các đối tượng CORBA bằng cách dùng
Interoperable Object Reference . 1 IOR là 1 chuỗi dài bắt đầu bằng IOR: và tiếp
theo là rất nhiều số hexadecimal.Ví dụ như:
IOR
:012020201000000049444c3a4163636f756e743a312e300001000000000000
004e000000010100200f0000003231362e31352e3131322e3137390020350420
202e00000001504d43000000001000000049444c3a4163636f756e743a312e30
000e0000004a61636b20422e20517569636b00

- 1 IOR mô tả 1 đối tượng duy nhất. Thông thường, rất nhiều lớp server in
ra hết chuỗi IOR của tất cả các đối tượng mà chúng lưu giữ, giúp khởi động các
client để định vị chúng. Bạn có thể dán chuỗi IOR server vào trong chương trình
client. Cụ thể là dùng code sau:

String ref = "IOR:012020201000000049444c3a4163636f...";


// paste IOR from server
org.omg.CORBA.Object object = orb.string_to_object(ref);
- Sau đó, thu hẹp các đối tượng trả về tới khi phù hợp, chẳng hạn:
Env env = EnvHelper.narrow(object);
Hoặc dùng:
NamingContext context =
NamingContextHelper.narrow(object);

org.omg.CORBA.ORB 1.2
 static ORB init(String[] commandLineArgs, Properties
orbConfigurationprops)
tạo 1 ORB mới và khởi động nó.
 String[] list_initial_services()
Trả về 1 danh sách các dịch vụ tồn tại lúc đầu chẳng hạn như
"NameService".

 org.omg.CORBA.Object resolve_initial_references(String
initialServiceName)
trả về 1 đối tượng mà thực thi 1 trong các dịch vụ khởi đầu.
 org.omg.CORBA.Object string_to_object(String ior)
định vị đối tượng với 1 IOR cho trước

66 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

org.omg.CosNaming.NamingContext 1.2

 org.omg.CORBA.Object resolve(NameComponent[] name)

trả về đối tượng với tên cho trước.


org.omg.CosNaming.NameComponent 1.2

 NameComponent(String componentId, String componentType)


Khởi tạo 1 name component mới

6/ Implementing CORBA Servers(Thực thi Server CORBA):


- Nếu bạn đang phát triển 1 cơ sở hạ tầng CORBA, bạn sẽ nhận thấy Java là
1 ngôn ngữ thực thi tốt đối với các đối tượng CORBA server. Phần này mô tả cách
thức thực thi 1 server CORBA bằng ngôn ngữ lập trình Java

- Chương trình ví dụ trong phần này thì đơn giản hơn phần trước. Giả sử ta
cung cấp 1 dịch vụ cho phép tìm kiếm 1 thuộc tính của hệ thống trên 1 máy ảo
Java. Sau đây là mô tả IDL:
interface SysProp
{
string getProperty(in string name);
};

- Ví dụ, chương trình kiểm nghiệm client của ta gọi tới server bằng lệnh:
CORBA::String_var key = "java.vendor";
CORBA::String_var value = sysProp->getProperty(key);
->Kết quả là 1 chuỗi mô tả nhà cung cấp máy ảo Java đang thực hiện
chương trình server. Code trình bày trong Ví dụ: 5-21

- Để thực thi server, bạn chạy lệnh idlj với lựa chọn -fall (mặc định thì
lệnh idlj chỉ tạo ra client-side stubs): idlj -fall SysProp.idl

- Sau đó bạn mở rộng lớp SysPropPOA mà bộ biên dịch idlj tạo ra từ file
IDL. Code như sau:
class SysPropImpl extends SysPropPOA
{
public String getProperty(String key)
{
return System.getProperty(key);
}

67 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Tiếp theo, viết 1 chương trình server thực hiện các nhiệm vụ sau:
1. Khởi động ORB.
2. Định vị và kích hoạt Portable Object Adaptor (POA).
3. Tạo lớp server thực thi
4. Sử dụng POA để chuyển tham chiếu tạm thời sang tham chiếu đối
tượng CORBA.
5. In các IOR của nó.
6. Ràng buộc lớp server thực thi với Naming service.
7. Chờ lời gọi từ client.
- Bạn sẽ hiểu rõ hơn ở Ví dụ:5-20 với code đầy đủ, sau đây là những điểm
lưu ý:
+Khởi động ORB:
ORB orb = ORB.init(args, null);
+Kích hoạt POA:
POA rootpoa = (POA)
orb.resolve_initial_references("RootPOA");
rootpoa.the_POAManager().activate();
+ Khởi tạo đối tượng server và chuyển nó sang đối tượng CORBA:
SysPropImpl impl = new SysPropImpl();
org.omg.CORBA.Object ref =
rootpoa.servant_to_reference(impl);
+ Từ phương thức object_to_string ta thu được IOR và in nó ra:
System.out.println(orb.object_to_string(impl));
+ Bạn tham chiếu tới Naming Service :
org.omg.CORBA.Object namingContextObj =
orb.resolve_initial_references("NameService");
NamingContext namingContext =
NamingContextHelper.narrow(namingContextObj);
+ Sau đó bạn tạo tên mong muốn cho đối tượng, ở đây ta gọi đối tượng là
SysProp
NameComponent[] path =
{
new NameComponent("SysProp", "Object")
};
+ Bạn dùng phương thức rebind để ràng buộc đối tượng với 1 tên để gọi:
namingContext.rebind(path, impl);
+ Cuối cùng, bạn chờ lời gọi từ client:
orb.run();

- Để kiểm tra chương trình chạy, ta làm như sau:


1. Biên dịch file IDL, sử dụng cả 2 trình biên dịch C++ và Java:

68 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

omniidl -bcxx SysProp.idl


idlj -fall SysProp.idl
2. Biên dịch chương trình server:
javac SysPropServer.java
3. Biên dịch chương trình client C++. Trên Linux dùng lệnh:
g++ -o SysPropClient -D__x86__ -D__linux__ -
D__OSVERSION__=2 -I /usr/local/include/omniORB4/
SysPropClient.cpp SysPropSK.cc -lomniORB4 -lomnithread –
lpthread
4. Khởi động dịch vụ Naming Server orbd trên server. Chương trình
này là 1 phần của bộ JDK:
orbd -ORBInitialPort 2809 &
5. Khởi động server, server chạy cho tới khi bạn dừng thì thôi
java SysPropServer -ORBInitialPort 2809 &
6. Chạy client, nó sẽ in ra nhà cung cấp JVM của server:
./SysPropClient -ORBInitRef
NameService=corbaname::localhost

- Bây giờ bạn đã biết được cách dùng CORBA để kết nối client và server mà
được viết bằng những ngôn ngữ lập trình khác nhau

Ví dụ:5-20 SysPropServer.java
1. import org.omg.CosNaming.*;
2. import org.omg.CORBA.*;
3. import org.omg.PortableServer.*;
4.
5. class SysPropImpl extends SysPropPOA
6. {
7. public String getProperty(String key)
8. {
9. return System.getProperty(key);
10. }
11. }
12.
13. public class SysPropServer
14. {
15. public static void main(String args[])
16. {
17. try
18. {
19. System.out.println("Creating and initializing the
ORB...");
20.
21. ORB orb = ORB.init(args, null);
22.

69 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

23. System.out.println("Registering server


implementation with the ORB...");
24.
25. POA rootpoa = (POA)
orb.resolve_initial_references("RootPOA");
26. rootpoa.the_POAManager().activate();
27.
28. SysPropImpl impl = new SysPropImpl();
29. org.omg.CORBA.Object ref =
rootpoa.servant_to_reference(impl);
30.
31. System.out.println(orb.object_to_string(ref));
32.
33. org.omg.CORBA.Object namingContextObj =
orb.resolve_initial_references
("NameService");
34. NamingContext namingContext =
NamingContextHelper.narrow(namingContextObj);
35. NameComponent[] path =
36. {
37. new NameComponent("SysProp", "Object")
38. };
39.
40. System.out.println("Binding server implemenation to
name service...");
41. namingContext.rebind(path, ref);
42.
43. System.out.println("Waiting for invocations from
clients...");
44. orb.run();
45. }
46. catch (Exception e)
47. {
48. e.printStackTrace(System.out);
49. }
50. }
51. }

Ví dụ:5-21 SysPropClient.cpp
1. #include <iostream>
2.
3. #include "SysProp.hh"
4.
5. using namespace std;

70 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

6.
7. CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb,
const char serviceName[])
8. {
9. CosNaming::NamingContext_var rootContext;
10.
11. try
12. {
13. // Obtain a reference to the root context of the name
service:
14. CORBA::Object_var initServ;
15. initServ = orb-
>resolve_initial_references("NameService");
16.
17. // Narrow the object returned by
resolve_initial_references() to a CosNaming:
:NamingContext
18. // object
19. rootContext =
CosNaming::NamingContext::_narrow(initServ);
20. if (CORBA::is_nil(rootContext))
21. {
22. cerr << "Failed to narrow naming context." << endl;
23. return CORBA::Object::_nil();
24. }
25. }
26. catch (CORBA::ORB::InvalidName&)
27. {
28. cerr << "Name service does not exist." << endl;
29. return CORBA::Object::_nil();
30. }
31.
32. // Create a name object, containing the name
corejava/SysProp:
33. CosNaming::Name name;
34. name.length(1);
35.
36. name[0].id = serviceName;
37. name[0].kind = "Object";
38.
39. CORBA::Object_ptr obj;
40. try
41. {
42. // Resolve the name to an object reference, and assign
the returned reference to a
43. // CORBA::Object:
44. obj = rootContext->resolve(name);

71 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

45. }
46. catch (CosNaming::NamingContext::NotFound&)
47. {
48. // This exception is thrown if any of the components
of the path [contexts or
the object]
49. // aren't found:
50. cerr << "Context not found." << endl;
51. return CORBA::Object::_nil();
52. }
53. return obj;
54. }
55.
56. int main (int argc, char *argv[])
57. {
58. CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv,
"omniORB4");
59.
60. CORBA::Object_var obj = getObjectReference(orb,
"SysProp");
61. SysProp_var sysProp = SysProp::_narrow(obj);
62.
63. if (CORBA::is_nil(sysProp))
64. {
65. cerr << "Cannot invoke on a nil object reference." <<
endl;
66. return 1;
67. }
68.
69. CORBA::String_var key = "java.vendor";
70. CORBA::String_var value = sysProp->getProperty(key);
71.
72. cerr << key << "=" << value << endl;
73.
74. return 0;
75. }

org.omg.CORBA.ORB 1.2

 void connect(org.omg.CORBA.Object obj)

kết nối đối tượng thực thi cho trước với ORB, kích hoạt ORB để hướng lời
gọi tới các phương thức của đối tượng
 String object_to_string(org.omg.CORBA.Object obj)

72 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Trả về chuỗi IOR của đối tượng cho trước.

org.omg.CosNaming.NamingContext 1.2

 void bind(NameComponent[] name, org.omg.CORBA.Object obj)


 void rebind(NameComponent[] name, org.omg.CORBA.Object obj)

ràng buộc 1 đối tượng với 1 tên gọi.Phương thức bind dùng 1 ngoại lệ
AlreadyBound nếu như đối tượng trước đây đã được ràng buộc. Phương thức
rebind sẽ thay thế đối tượng đã ràng buộc trước đây

VII/. Remote Method Calls with SOAP(Gọi phương thức từ


xa qua giao thức SOAP):
- Trong những năm gần đây, các dịch vụ Web đã nổi lên như 1 công nghệ
phổ biến cho cơ chế gọi phương thức từ xa. Về mặt kỹ thuật, 1 dịch vụ Web gồm
có 2 phần:
 Một server có thể được truy cập bằng giao thức truyền Simple Object
Access Protocol (SOAP)
 Một mô tả về dịch vụ bằng định dạng Web Service Description
Language (WSDL)

- SOAP là 1 giao thức XML, gần giống với IIOP của CORBA, cung cấp 1
giao thức cho cơ chế gọi phương thức từ xa. Cũng giống như bạn có thể lập trình
server và client trên nền CORBA mà không cần hiểu gì về IIOP, nên bạn thực sự
không cần hiểu chi tiết về SOAP để gọi 1 dịch vụ Web.

- WSDL tương tự như IDL. Nó mô tả interface của dịch vụ Web. Trong


phần này, ta chỉ thảo luận cách thực thi của 1 client kết nối tới 1 dịch vụ Web hiện
hành.

- Việc thực thi dịch vụ Web vượt quá phạm vi của cuốn sách. Để có nhiều
thông tin hơn hãy xem Chapter 8 của J2EE tutorial tại
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html

- Để hình dung 1 dịch vụ Web dễ hiểu, ta xét ví dụ cụ thể sau: Dịch vụ Web
của Amazon, mô tả ở địa chỉ http://www.amazon.com/gp/aws/landing.html. Dịch
vụ Web của Amazon cho phép 1 người lập trình tương tác với hệ thống của

73 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

Amazon với nhiều mục tiêu đa dạng. Chẳng hạn, bạn có thể có được danh sách của
tất cả cuốn sách chỉ cần đưa ra tên tác giả hay tựa đề cuốn sách. Amazon tạo ra
dịch vụ này để các công ty có nhu cầu bán các mặt hàng của họ cho người tiêu
dùng, sử dụng hệ thống của Amazon giống như là fulfillment backend. Để chạy ví
dụ này, bạn cần đăng nhập vào Amazon và nhận thẻ miễn phí dành cho người phát
triển(developer) để có thể kết nối tới dịch vụ.

- Bạn có thể phỏng theo các kỹ thuật đã mô tả trong phần này cho bất kỳ
dịch vụ Web nào. Trang http://www.xmethods.com cung cấp rất nhiều dịch vụ
Web miễn phí cho bạn thử.

- Vì sao những dịch vụ Web lại có nhiều sức hút đến vậy?, bởi vì chúng là
ngôn ngữ trung gian. Chẳng hạn, ta có thể truy cập vào Amazon Web Services bằng
cách dùng chương trình Java, nhưng những người phát triển khác có thể truy cập
bằng C++ hay Visual Basic cũng được. The WSDL descriptor describes the
services in a language-independent manner(WSDL mô tả các dịch vụ bằng 1 ngôn
ngữ độc lập). Chẳng hạn, WSDL của Amazon Web Services (ở địa chỉ
http://soap.amazon.com/schemas3/AmazonWebServices.wsdl) mô tả 1 operation
AuthorSearchRequest như sau:
<operation name="AuthorSearchRequest">
<input message="typens:AuthorSearchRequest"/>
<output message="typens:AuthorSearchResponse"/>
</operation>
. . .
<message name="AuthorSearchRequest">
<part name="AuthorSearchRequest"
type="typens:AuthorRequest"/>
</message>
<message name="AuthorSearchResponse">
<part name="return" type="typens:ProductInfo"/>
</message>

- Ngoài ra, nó định nghĩa các kiểu dữ liệu. Sau đây là định nghĩa
AuthorRequest
<xsd:complexType name="AuthorRequest">
<xsd:all>
<xsd:element name="author" type="xsd:string"/>
<xsd:element name="page" type="xsd:string"/>
<xsd:element name="mode" type="xsd:string"/>
<xsd:element name="tag" type="xsd:string"/>
<xsd:element name="type" type="xsd:string"/>
<xsd:element name="devtag" type="xsd:string"/>
<xsd:element name="sort" type="xsd:string" minOccurs="0"/>

74 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

<xsd:element name="locale" type="xsd:string"


minOccurs="0"/>
<xsd:element name="keywords" type="xsd:string"
minOccurs="0"/>
<xsd:element name="price" type="xsd:string"
minOccurs="0"/>
</xsd:all>
</xsd:complexType>

- Khi được chuyển sang Java, AuthorRequest sẽ trở thành 1 lớp:


public class AuthorRequest
{
public AuthorRequest(String author, String page, String mode,
String tag, String type,
String devtag, String sort, String locale, String keyword,
String price) { . . . }
public String getAuthor() { . . . }
public void setAuthor(String newValue) { . . . }
public String getPage() { . . . }
public void setPage(String) { . . . }
. . .
}

- Để gọi dịch vụ tìm kiếm, khởi tạo 1 đối tượng AuthorRequest và gọi
authorSearchRequest của đối tượng “port”:
AmazonSearchPort port = (AmazonSearchPort) (new
AmazonSearchService_Impl()
.getAmazonSearchPort());
AuthorRequest request = new AuthorRequest(name, "1", "books",
"", "lite", "", token, "",
"", "");
ProductInfo response = port.authorSearchRequest(request);

- Đối tượng “port” sẽ biến đối tượng Java thành 1 message của SOAP,
truyền nó tới server Amazon, và biến message trả về thành 1 đối tượng ProductInfo .
Sau đó lớp port sẽ được tự động tạo ra.

- Chú ý là: File WSDL không chỉ định rõ dịch vụ phải làm gì. Nó chỉ xác định
tham số và trả về kiểu dữ liệu.

- Có thể download Java Web Services Developer Pack (JWSDP) từ


http://java.sun.com/webservices/webservicespack.html và cài đặt nó.

75 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Để tạo ra các lớp Java theo yêu cầu, đưa file config.xml như sau vào thư
mục của chương trình client:
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl

location="http://soap.amazon.com/schemas3/AmazonWebServices.wsdl
"
packageName="com.amazon" />
</configuration>

- Sau đó chạy câu lệnh sau:


jwsdp/jaxrpc/bin/wscompile.sh -import config.xml
jwsdp/jaxrpc/bin/wscompile.sh -gen -keep config.xml

- Ở đây jwsdp là thư mục mà bạn đã cài đặt JWSDP vào, ví dụ:
/usr/local/jwsdp-1.4 hay c:\jwsdp-1.4.
+ Câu lệnh đầu tiên sẽ import file WSDL từ vị trí cho trước. Câu lệnh thứ 2
sẽ tạo ra các lớp mà client cần. Lựa chọn -keep sẽ giữ lại những file nguồn. Kết
quả từ các câu lệnh trên là 1 số lượng lớn các lớp được tạo ra trong thư mục đã chỉ
định(ở đây là com/amazon ) như com/amazon/AuthorRequest.java và
com/amazon/Details.java . Chúng ta sẽ dùng các lớp đó cho ví dụ ứng dụng
sau.

- Ví dụ ứng dụng trong Ví dụ:5-22 , người dùng chỉ định tên của tác giả và
nhấp vào nút Search.Kết quả thể hiện trong hình 5-11 . Điều này cho thấy dịch vụ
Web hoạt động tốt

76 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

- Để biên dịch ứng dụng này, ta bổ sung thêm file thư viện
jwsdp/jaxrpc/lib/jaxrpc-impl.jar cho lớp của bạn.
- Để chạy ứng dụng này, file lớp của bạn phải chứa:
. (the current directory)
jwsdp/jaxrpc/lib/jaxrpc-api.jar
jwsdp/jaxrpc/lib/jaxrpc-impl.jar
jwsdp/jaxrpc/lib/jaxrpc-spi.jar
jwsdp/jwsdp-shared/lib/activation.jar
jwsdp/jwsdp-shared/lib/mail.jar
jwsdp/saaj/lib/saaj-api.jar
jwsdp/saaj/lib/saaj-impl.jar

- Ở ví dụ này sẽ cho thấy việc gọi 1 dịch vụ Web về cơ bạn giống như là
việc tạo ra lời gọi phương thức từ xa khác. Người lập trình gọi 1 phương thức cục
bộ trên 1 đối tượng proxy và proxy sẽ kết nối tới 1 server. Từ khi dịch vụ Web xuất
hiện ở mọi nơi, nó thực sự đem lại sự hứng thú cho những người lập trình ứng
dụng. Và nó trở nên dễ sử dụng khi các công cụ và thư viện được tập hợp đầy đủ
trong version mới của JDK
Ví dụ: 5-22. SOAPTest.java
1. import com.amazon.*;
2. import java.awt.*;
3. import java.awt.event.*;
4. import java.rmi.*;

77 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

5. import java.util.*;
6. import javax.swing.*;
7.
8. /**
9. The client for the warehouse program.
10. */
11. public class SOAPTest
12. {
13. public static void main(String[] args)
14. {
15. JFrame frame = new SOAPTestFrame();
16. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
17. frame.setVisible(true);
18. }
19. }
20.
21. /**
22. A frame to select the book author and to display the
server response.
23. */
24. class SOAPTestFrame extends JFrame
25. {
26. public SOAPTestFrame()
27. {
28. setTitle("SOAPTest");
29. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
30.
31. JPanel panel = new JPanel();
32.
33. panel.add(new JLabel("Author:"));
34. author = new JTextField(20);
35. panel.add(author);
36.
37. JButton searchButton = new JButton("Search");
38. panel.add(searchButton);
39. searchButton.addActionListener(new
40. ActionListener()
41. {
42. public void actionPerformed(ActionEvent event)
43. {
44. result.setText("Please wait...");
45. new Thread(new
46. Runnable()
47. {
48. public void run()
49. {
50. String name = author.getText();

78 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

51. String books =


searchByAuthor(name);
52. result.setText(books);
53. }
54. }).start();
55. }
56. });
57.
58. result = new JTextArea();
59. result.setLineWrap(true);
60. result.setEditable(false);
61.
62. add(panel, BorderLayout.NORTH);
63. add(new JScrollPane(result), BorderLayout.CENTER);
64. }
65.
66. /**
67. Calls the Amazon web service to find titles that
match the author.
68. @param name the author name
69. @return a description of the matching titles
70. */
71. private String searchByAuthor(String name)
72. {
73. try
74. {
75. AmazonSearchPort port = (AmazonSearchPort)
76. (new
AmazonSearchService_Impl().getAmazonSearchPort());
77.
78. AuthorRequest request
79. = new AuthorRequest(name, "1", "books", "",
"lite", "", token, "", "", "");
80. ProductInfo response =
port.authorSearchRequest(request);
81.
82. Details[] details = response.getDetails();
83. StringBuilder r = new StringBuilder();
84. for (Details d : details)
85. {
86. r.append("authors=");
87. String[] authors = d.getAuthors();
88. if (authors == null) r.append("[]");
89. else r.append(Arrays.asList(d.getAuthors()));
90. r.append(",title=");
91. r.append(d.getProductName());
92. r.append(",publisher=");

79 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java
Trường Đại Học Công Nghệ Thông Tin – Đại Học Quốc Gia Hồ Chí Minh 2011

93. r.append(d.getManufacturer());
94. r.append(",pubdate=");
95. r.append(d.getReleaseDate());
96. r.append("\n");
97. }
98. return r.toString();
99. }
100. catch (RemoteException e)
101. {
102. return "Exception: " + e;
103. }
104. }
105.
106. private static final int DEFAULT_WIDTH = 450;
107. private static final int DEFAULT_HEIGHT = 300;
108.
109. private static final String token = "your token goes
here";
110.
111. private JTextField author;
112. private JTextArea result;
113. }

80 Lập Trình Hệ Thống Với Java – Tìm hiểu lập trình phân tán trong Java

You might also like