3. CHƯƠNG 3: LẬP TRÌNH PHÂN TÁN VỚI CORBA
1.11. MỘT SỐ KỸ THUẬT LẬP TRÌNH PHÂN TÁN VỚI CORBA
3.1.11. Tham chiếu ngược trong CORBA (CALLBACK)
Tham chiếu ngược là lời gọi từ trình chủ ngược về các đối tượng đặt trên máy khách. Đối tượng CORBA cũng cho phép ta thực hiện kỹ thuật này. Sau đây ta sẽ viết lại các đối tượng RMI AtServer và AtClient trong bài minh họa của phần RMI theo cách của CORBA.
AtServer là đối tượng CORBA cài đặt trên máy chủ. AtClient là đối tượng CORBA chạy trên máy khách. Chương trình khách gọi AtServer trên máy chủ và gửi tham chiếu của AtClient đến trình chủ. AtServer sử dụng tham chiếu của AtClient để gọi ngược về trình khách.
Ví dụ:
Bước 1: Đặc tả đối tượng AtClient và AtServer bằng ngôn ngữ IDL:
//ClientServer.idl
interface AtClient{
void callClientMethod(in string message); };
interface AtServer{
void registerClient(in AtClient c);
void callServerMethod(in string message); }
idlj -fall ClientServer.idl
Bước 2: Cài đặt 2 giao tiếp AtClient và AtServer thành 2 đối tượng CORBA là AtClientServant và AtServerServant bằng Java.
//AtClientServant.java
class AtClientServant extends AtClientPOA {
public void callClientMethod (String message){
System.out.println(message);
} }
//AtServerServant.java
class AtServerServant extends AtServerPOA{ AtClient client;
public void registerClient(AtClient c) { client=c; } public void callServerMethod(String message){
System.out.println(message); for(int i=1;i<10;i++){
String msg=“Server response “ + Math.random()*100; client.callClientMethod(msg);
} } }
Bước 3: Xây dựng trình chủ để cài đặt và đăng ký đối tượng AtServer trên máy chủ. //Setup.java import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CORBA.*; import org.omg.PortableServer.*; import org.omg.PortableServer.POA; import java.util.Properties; class Setup {
public static void main(String args[]) { try{
ORB orb = ORB.init(args, null);
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA")); rootpoa.the_POAManager().activate();
AtServerServant serv = new AtServerServant(); serv.setORB(orb);
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(serv); AtServer href = AtServerHelper.narrow(ref);
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef); String name = "AtServer";
NameComponent path[] = ncRef.to_name( name ); ncRef.rebind(path, href);
System.out.println("Server ready and waiting ..."); // wait for invocations from clients
orb.run(); } catch (Exception e) { System.err.println("ERROR: " + e); e.printStackTrace(System.out); } System.out.println("HelloServer Exiting ..."); } }
Bước 4: Cài đặt trình khách, gọi đối tượng AtServer trên máy chủ.
//Client.java
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CORBA.*;
import org.omg.PortableServer.POA; class Client {
static AtServer serv;
public static void main(String args[]) { try{
ORB orb = ORB.init(args, null); POA rootpoa = POAHelper.narrow
(orb.resolve_initial_references("RootPOA")); rootpoa.the_POAManager().activate();
AtClientServant clie = new AtClientServant(); clie.setORB(orb);
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(clie); AtClient Cref = AtClientHelper.narrow(ref);
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContextExt Root= NamingContextExtHelper.narrow(objRef); String name = "AtClient";
NameComponent path[] = Root.to_name( name ); Root.rebind(path, Cref);
String name2 = "AtServer";
serv = AtServerHelper.narrow(Root.resolve_str(name2)); serv.registerClient(Cref); serv.callServerMethod("Client request..."); } catch (Exception e) { System.out.println("ERROR : " + e) ; e.printStackTrace(System.out); } } }
Bước 5: Biên dịch và chạy chương trình. - Chạy chương trình tnamserv
- Chạy trình chủ:
3.1.12. Gọi các phương thức động trong CORBA (DYNAMIC METHOD CALL)
3.1.12.1 Sử dụng kỹ thuật phản chiếu (reflect) của Java
Trong Java, ta có thể dùng kỹ thuật phản chiếu (reflect) để truy tìm các phương thức có trong các đối tượng CORBA. Cơ chế này giúp trình khách có thể gọi được phương thức của một đối tượng CORBA bất kỳ mà không cần dùng đến lớp giao tiếp interface tương ứng của đối tượng do trình chủ cung cấp.
Chương trình minh họa sau đây mô tả lại ví dụ minh họa kỹ thuật reflect trong RMI bằng kỹ thuật reflect trong CORBA.
Bước 1: Đặc tả đối tượng Friend bằng ngôn ngữ IDL
//Friend.idl
interface Friend{
string greeting(in string name); };
Chuyển đổi đặc tả IDL thành các tập tin trung gian dùng ở cả hai phía máy khách và máy chủ:
idlj –fall Friend.idl
Bước 2: Cài đặt cho đối tượng CORBA Friend trong Java.
//FriendServant.java
class FriendServant extends FriendPOA{ public String greeting (String name){ return "Hi "+name +" from CORBA !"; }
}
Bước 3: Thiết kế chương trình đăng ký đối tượng Friend với dịch vụ COS Naming
//Setup.java
import org.omg.CORBA.*; import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*; import java.util.Properties;
class Setup{
public static void main(String args[]) throws Exception{ System.out.println("CORBA Friend Object");
ORB orb=ORB.init(args,null);
Friend servant= new FriendServant(); orb.connect(servant);
org.omg.CORBA.Object nameService=
orb.resolve_initial_references("NameService");
NamingContext nsContext = NamingContextHelper.narrow( nameService); NameComponent nc=new NameComponent("FriendObject","");
NameComponent path[]={nc}; nsContext.rebind(path,servant);
java.lang.Object obj = new java.lang.Object(); synchronized (obj){
obj.wait(); }
} }
Bước 4:Thiết kế chương trình trên máy khách
//Client.java
import org.omg.CORBA.*; import org.omg.CosNaming.*; import java.lang.reflect.*; class Client{
static Friend frie;
public static void main(String args[])throws Exception{ ORB orb = ORB.init(args, null);
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContextExt Root= NamingContextExtHelper.narrow(objRef); String name = "Friend";
// Chuyển tham chiếu của đối tượng CORBA thành đối tượng Java
FriendHelper.narrow(Root.resolve_str(name));
// Truy tìm xuất xứ lớp của đối tượng
Class c = o.getClass();
// Định kiểu tham số của phương thức cần gọi
Class[] parameterTypes = new Class[] {String.class};
// Truy tìm phương thức cần gọi
Method theMethod;
theMethod = c.getMethod("greeting", parameterTypes);
// Định đối số truyền vào phương thức cần gọi
java.lang.Object[] arguments = new java.lang.Object[] {"Jerry"};
// Gọi phương thức và nhận kết quả trả về
String result = (String) theMethod.invoke(o, arguments); System.out.println(result);
} }
- Chạy chương trình tnamserv:
- Chạy trình chủ:
3.1.12.2 Sử dụng kỹ thuật gọi động (DII – Dynamic Invocation Interface) của CORBA
CORBA cung cấp cho ta cách gọi động các phương thức của một đối tượng CORBA bất kì. Trong CORBA, khả năng này được gọi là “giao tiếp gọi động” (Dynamic Invocation Interface).
Với kỹ thuật này, trình khách có thể yêu cầu trình môi giới ORB duyệt qua các phương thức của đối tượng CORBA, sau đó truyền tham số và triệu gọi phương thức mà không cần đến lớp tham chiếu tường minh của đối tượng.
Sau đây là ví dụ minh họa. Chương trình Client ở trên trong kỹ thuật reflect được viết lại theo kỹ thuật gọi động như sau:
//ClientCorba.java
import org.omg.CORBA.*; import org.omg.CosNaming.*; class ClientCorba {
public static void main(String args[]) throws Exception{ ORB orb=ORB.init(args,null);
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContextExt Root= NamingContextExtHelper.narrow(objRef); String name = "Friend";
// Lấy về tham chiếu tổng quát của đối tượng CORBA
org.omg.CORBA.Object o= (org.omg.CORBA.Object)
FriendHelper.narrow(Root.resolve_str(name));
/* Yêu cầu CORBA truy tìm và đưa về tham chiếu của phương thức greeting() có trong đối tượng CORBA. Đối tượng Request sẽ chứa các thông tin về phương thức của đối tượng nếu tìm thấy. */
org.omg.CORBA.Request request=o._request("greeting");
// Tạo đối số để truyền vào hàm greeting()
request.add_in_arg().insert_string("Jerry");
// Định kiểu trả về của phương thức greeting()
request.set_return_type
// Gọi phương thức greeting()
request.invoke();
// Lấy kết quả về sau khi gọi hàm
String result= request.result().value().extract_string(); System.out.println(result);
} }
Thực thi chương trình:
3.1.13. RMI trên IIOP (RMI over IIOP)
RMI và CORBA đều hướng đến mô hình lập trình phân tán đối tượng tương tự nhau. Tuy nhiên, RMI trong Java có những hạn chế so với CORBA. Kỹ thuật RMI chỉ áp dụng cho những đối tượng thuần Java. Trong khi đó, CORBA dung hòa được đối tượng của rất nhiều ngôn ngữ lập trình. Do đó, kỹ thuật CORBA không phải đơn giản như RMI trong Java. Các đối tượng RMI sử dụng giao thức JRMP (Java Remote Method Protocol) để “nói chuyện với nhau”. Trong khi đó, các đối tượng CORBA sử dụng giao thức IIOP được sự chấp nhận và chuẩn hóa của hầu hết các nhà phát triển lớn.
Sun Microsystem muốn có một tiếng nói chung giữa đối tượng CORBA và RMI. Vì lý do này, Java cung cấp cho ta cách cài đặt để các đối tượng RMI của Java có thể sử dụng giao thức IIOP để giao tiếp với các đối tượng CORBA. Khả năng này gọi là RMI – Over – IIOP. Với kỹ thuật cài đặt RMI – Over – IIOP, một đối tượng RMI sẽ vừa có khả năng giao tiếp được với trình khách RMI vừa có thể giao tiếp được với các đối tượng CORBA.
Hình vẽ sau đây minh họa khả năng giao tiếp bằng giao thức RMI-Over-IIOP so với các giao thức JRMP và IIOP giữa các đối tượng:
Hình 3.4-14: Giao tiếp RMI-Over-IIOP giữa đối tượng trình chủ và trình khách 1. Trình khách viết bằng Java, có thể giao tiếp được với đối tượng chủ theo
giao thức JRMP hoặc RMI-Over-IIOP.
2. Trình khách viết bằng Java, có thể giao tiếp được với đối tượng CORBA theo giao thức IIOP
3. Trình khách viết bằng C++, có thể dùng giao thức IIOP để gọi đối tượng CORBA.
4. Trình khách viết bằng C++, chỉ có thể giao tiếp được với đối tượng RMI thông qua giao thức RMI-Over-IIOP mà thôi.
5. Đối tượng RMI và CORBA giao tiếp với nhau bằng giao thức RMI-Over- IIOP.
Xây dựng đối tượng RMI giao tiếp bằng IIOP: Bước 1: Đặc tả giao tiếp interface cho đối tượng Hello
//HelloInterface.java
import java.rmi.Remote;
interface HelloInterface extends java.rmi.Remote {
public void sayHello( String from ) throws java.rmi.RemoteException; }
Bước 2: Cài đặt đối tượng Hello
//HelloImpl.java
import javax.rmi.PortableRemoteObject;
class HelloImpl extends PortableRemoteObject implements HelloInterface { public HelloImpl() throws java.rmi.RemoteException {
super(); }
public void sayHello( String from ) throws java.rmi.RemoteException { System.out.println( "Hello from " + from + "!!" );
System.out.flush(); }
}
Đối tượng Hello cung cấp một phương thức sayHello() để đưa ra một chuỗi. Đây là đối tượng được viết hoàn toàn bằng Java. Ta dịch chương trình như sau:
javac HelloImpl.java
Ta thu được lớp HelloImpl.class. Muốn đối tượng này có khả năng giao tiếp bằng giao thức IIOP thay cho giao thức JRMP ta có thể dùng trình dịch rmic.exe như sau:
rmic.exe –iiop HelloImpl
Kết quả thu được là tệp trung gian _Stub và _Tie. Đây là những lớp chịu trách nhiệm chuyển đổi lời gọi theo giao thức JRMP thành IIOP.
Bước 3: Xây dựng trình cài đặt cho đối tượng:
//HelloServer.java
import javax.naming.InitialContext; import javax.naming.Context; class HelloServer {
public static void main(String[] args) { try {
HelloImpl helloRef = new HelloImpl();
// Tạo tham chiếu đến Naming Service
Context initialNamingContext = new InitialContext(); initialNamingContext.rebind("HelloService", helloRef ); System.out.println("Hello Server: Ready...");
System.out.println("Trouble: " + e); e.printStackTrace(); } } } Giải thích:
Thư viện javax.naming cung cấp cho ta đối tượng Context, để lấy về context gốc ta dùng lệnh:
Context initialNamingContext = new InitialContext();
Và với context gốc lấy được, ta dùng phương thức bind() của đối tượng Context để ràng buộc đối tượng HelloImplt với dịch vụ tên COS Naming như sau:
initialNamingContext.rebind("HelloService", helloRef );
Để chỉ định context gốc lấy về là context của dịch vụ COS Naming, khi chạy trình Setup, ta dùng lệnh như sau:
Với tham số dòng lệnh –Djava.naming.initial, Java sẽ sử dụng lớp khởi tạo
com.sun.cosnaming.CNCtxFactory làm dịch vụ đăng ký tên. Bằng cách này, các nhà
cung cấp khác nhau có thể chỉ định lớp khởi tạo khác nhau. Tham số
-Djava.naming.provider.url dùng để chỉ định vị trí dịch vụ quản lý tên đang chạy. Ở
đây, trước khi chạy trình Setup, ta khởi động dịch vụ tnameserv với cổng mặc định là 1050.
//HelloClient.java import java.rmi.RemoteException; import java.net.MalformedURLException; import java.rmi.NotBoundException; import javax.rmi.*; import java.util.Vector; import javax.naming.NamingException; import javax.naming.InitialContext; import javax.naming.Context; class HelloClient {
public static void main( String args[] ) { Context ic; Object objref; HelloInterface hi; try { ic = new InitialContext();
// Lấy đối tượng tham chiếu từ Name Service // Dùng hàm JNDI .
objref = ic.lookup("HelloService");
System.out.println("Client: Obtained a ref. to Hello server."); hi = (HelloInterface) PortableRemoteObject.narrow(
objref, HelloInterface.class); hi.sayHello( " MARS " );
} catch( Exception e ) {
System.err.println( "Exception " + e + "Caught" ); e.printStackTrace( );
return; }
} }
Tương tự trình chủ, để chạy trình khách viết theo phong cách RMI truy tìm được dịch vụ COS Naming, ta phải gọi máy ảo Java với tham số dòng lệnh như sau: