3. CHƯƠNG 3: LẬP TRÌNH PHÂN TÁN VỚI CORBA
1.10. TÌM HIỂU JNDI VÀ DỊCH VỤ COSNAMING TRONG CORBA
3.1.8. JNDI và dịch vụ NAMING trong RMI
Khi muốn sử dụng một đối tượng nào đó, ta phải biết tên của đối tượng và nơi mà đối tượng đó được cất giữ. Mỗi đối tượng đều có một cái tên để phân biệt nhau. Mỗi dịch vụ quản lý tên đều quy định cách thức để lưu trữ và gắn tên cho các đối tượng. Dịch vụ quản lý tên cũng cung cấp cách thức tìm kiếm và đặt tên cho đối tượng một cách nhất định.
Java đặc tả một tập hợp các hàm API dùng để phục vụ cho việc quản lý, lưu tên và tìm kiếm các đối tượng. Các hàm API loại này được gọi là JNDI (Java Naming Directory Interface).
JNDI chỉ đưa ra đặc tả ở dạng interface. Các dịch vụ nào muốn sử dụng JNDI thì phải cài đặt giao tiếp do Java qui định. Dịch vụ Naming chỉ hỗ trợ cho các đối tượng RMI trong Java. Naming có cách truy xuất đối tượng theo địa chỉ dạng URL dựa vào tên đối tượng khi đăng ký.
Ví dụ: tên myhello được ràng buộc với đối tượng Hello như sau:
Naming.bind(“rmi://localhost/myhello”, Hello);
Cho nên để lấy tham chiếu của một đối tượng RMI thì ta gọi hàm như sau:
Object o=Naming.lookup(“rmi://localhost/myhello”);
Quá trình dựa vào tên của đối tượng để tìm lại được tham chiếu của đối tượng đã được dịch vụ Naming lưu trước đó thì được gọi là phân giải đối tượng (object resolving).
3.1.9. JNDI và dịch vụ COS NAMING của CORBA
Cũng dựa vào đặc tả JNDI của Java nhưng CORBA xây dựng một cơ chế lưu trữ và tham chiếu của đối tượng khác với dịch vụ Naming của RMI. CORBA phân ra khái niệm Naming Context và Naming Component.
Namning Context (đóng vai trò như một thư mục) là một đối tượng có khả năng dẫn tham chiếu đến một Naming Component hay một Naming Context khác.
Naming Component (tương tự như một file): là một thành phần chứa tên, có khả năng ràng buộc tên với một đối tượng bất kỳ nào đó (và đối tượng này phải là đối tượng CORBA).
Naming Component và Naming Context quan hệ với nhau theo cấu trúc hình cây tương tự như cấu trúc cây thư mục của hệ thống file. Trình tnameserv.exe chính là dịch vụ lưu trữ theo cách này trong CORBA
Hình 3.3-13: Cấu trúc cây của dịch vụ COS NAMING trong CORBA
Ta thấy Market là một Naming Context. Nó cho phép tìm 3 thành phần khác là Stock, Shelf và Panel. Trong đó Stock và Shelf là hai Naming Context. Stock chứa 2 thành phần là Toys và Fruit. Shelf chứa một thành phần mang tên là
Book. Đối tượng Object A được ràng buộc với Toys. Đối tượng Object B được ràng buộc với Fruit. Đối tượng Object C được ràng buộc bởi cả Book và Panel. - Nếu đứng từ Market tham chiếu đến Object A thì phải chỉ ra đường dẫn là:
Market\Stock\Toys.
- Nếu đứng từ Market tham chiếu đến Object C thì đường dẫn là: Market\Panel hoặc Market\Shelf\Book.
- Nếu đứng từ Stock tham chiếu đến Object B thì đường dẫn là: Stock\Fruit. Xây dựng chương trình cài đặt và lưu trữ tên cho đối tượng CORBA
Dưới đây là một ví dụ minh họa cho thấy cách sử dụng dịch vụ COS Naming trong CORBA. Chương trình sử dụng mô hình ở hình trên làm minh họa. Ta lần lượt tạo ra các đối tượng ObjectA, ObjectB, ObjectC đồng thời xây dựng cách đặt tên và lưu trữ những đối tượng này theo cấu trúc hình cây đã vẽ.
Bước 1: Đặc tả một đối tượng CORBA để gắn với các thành phần tên trong cây
//Generic.idl
interface Generic{ string getName(); };
Đối tượng Generic đơn giản chỉ có nhiệm vụ là trả về tên của chính nó. Thực hiện lệnh biên dịch bằng idlj:
idlj –fall Generic.idl
Tiếp đến, ta cài đặt cho Generic trở thành đối tượng CORBA:
//GenericServant.java
class GenericServant extends GenericPOA{ String name;
public GenericServant (String name){ this.name =name;
}
public String getName (){ return name;
} }
Bước 2: Xây dựng trình đăng ký tên của các đối tượng Generic với dịch vụ COS Naming // NamingRegister.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 NamingRegister {
public static void main(String args[]) throws Exception { System.out.println("COS Naming Demo Application"); System.out.println(" Create Corba Object");
// Tạo các đối tượng CORBA Generic
GenericServant objectA=new GenericServant("This is Object A"); GenericServant objectB=new GenericServant("This is Object B"); GenericServant objectC=new GenericServant("This is Object C");
// Khởi động trình môi giới ORB
ORB orb=ORB.init(args,null);
// Tạo các đối tượng
objectA.setORB(orb); objectB.setORB(orb); objectC.setORB(orb);
// Lấy tham chiếu đến rootpoa và kích hoạtPOAManager
POA rootpoa = POAHelper.narrow
(orb.resolve_initial_references("RootPOA")); rootpoa.the_POAManager().activate();
// Tạo tham chiếu đến dịch vụ đăng ký tnameserv
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(objectA); Generic GAref = GenericHelper.narrow(ref);
ref = rootpoa.servant_to_reference(objectB); Generic GBref = GenericHelper.narrow(ref); ref = rootpoa.servant_to_reference(objectC); Generic GCref = GenericHelper.narrow(ref);
// Lấy về đối tượng context gốc của dịch vụ COS Naming
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContextExt Root = NamingContextExtHelper.narrow(objRef);
// Tạo đối tượng NameComponent mang tên Market
System.out.println(" Create Market context under root"); NameComponent market = new NameComponent
("Market", "Market folder");
// Tạo đường dẫn đến Market
NameComponent []path = {market};
// Yêu cầu context gốc tạo context mới mang tên Market
NamingContext marketctx=Root.bind_new_context(path); System.out.println(" Create Stock context under Market");
// Tạo đối tượng NameComponent mang tên Stock
NameComponent stock = new NameComponent
( "Stock", "Stock folder");
// Tạo đường dẫn đến Stock (đường dẫn này được tính từ context gốc)
NameComponent[] path1 = {market,stock};
// Yêu cầu context gốc tạo context mới mang tên Stock
NamingContext stockctx = Root.bind_new_context(path1);
System.out.println(" Create and bound object with Stock context");
// Tạo đối tượng NameComponent mang tên Toys
NameComponent toys = new NameComponent("Toys","object id");
// Tạo đối tượng NameComponent mang tên Fruit
NameComponent fruit = new NameComponent("Fruit", "object id");
// Tạo đường dẫn đến Toys (đường dẫn này được tính từ context gốc)
NameComponent[] path11 = {market,stock,toys};
// Tạo đường dẫn đến Fruit (đường dẫn này được tính từ context gốc)
NameComponent[] path12 = {market,stock,fruit};
/* Yêu cầu context gốc ràng buộc đối tượng ObjectA với thành phần
mang tên Toys theo đường dẫn chỉ định */
Root.rebind(path11,GAref);
/* Yêu cầu context gốc ràng buộc đối tượng ObjectB với thành phần
mang tên Fruit theo đường dẫn chỉ định */
Root.rebind(path12,GBref);
// Tạo đối tượng NameComponent mang tên Sheft
System.out.println(" Create Shelf context under Market");
NameComponent shelf = new NameComponent("Shelf", "Shelf folder");
//Tạo đường dẫn đến tên Sheft (đường dẫn này được tính từ Market)
NameComponent[] path2 = {shelf};
// Yêu cầu context Market tạo ra context mới mang tên Sheft
NamingContext shelfRoot = marketctx.bind_new_context(path2);
// Tạo đối tượng NameComponent mang tên Book
System.out.println(" Create and bound object with Shelf context"); NameComponent book = new NameComponent("Book", "object id");
//Tạo đường dẫn đến tên Book (đường dẫn này được tính từ Sheft)
NameComponent[] path21 = {book};
/* Yêu cầu context Sheft ràng buộc đối tượng ObjectC với thành phần
mang tên Book theo đường dẫn chỉ định */
shelfRoot.rebind(path21,GCref);
System.out.println(" Create and bound object with Market context");
// Tạo đối tượng NameComponent mang tên Panel
NameComponent panel = new NameComponent("Panel", "object id");
//Tạo đường dẫn đến tên Panel (đường dẫn này được tính từ context
gốc)
NameComponent[] path3 = {panel};
/* Yêu cầu context Sheft ràng buộc đối tượng ObjectC với thành phần
mang tên Panel theo đường dẫn chỉ định */
marketctx.rebind(path3,GCref);
System.out.println("rebind successfull"); System.out.println("Waiting for client ...");
// Tạo đối tượng Java
java.lang.Object obj = new java.lang.Object();
// Vòng lặp vô tận chờ máy khách synchronized (obj){ obj.wait(); } } } Giải thích:
Tương tự như cấu trúc cây thư mục của hệ điều hành có thư mục gốc, dịch vụ COS Naming cung cấp cho ta một context gốc mang tên “NameService”. Ta lấy
- Khởi tạo trình môi giới trung gian ORB:
orb = ORB.init (args, null);
- Gọi phương thức orb.resolve_initial_reference() của trình môi giới ORB với tham số là “NameService” để trả về đối tượng context. Phương thức
NamingContextExtHelper.narow() có nhiệm vụ chuyển đối tượng context về
đúng kiểu lớp NamingContext để ta sử dụng:
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContextExt Root = NamingContextExtHelper.narrow(objRef);
Có context gốc, ta có thể lần lượt tạo ra các tham chiếu đến các context hoặc thành phần ràng buộc tên NamingComponent khác.
Trong ví dụ trên, chúng ta muốn tạo ra một context mới mang tên Market để lưu trữ các thành phần tên. Trước tiên, ta dùng đối tượng NamingComponent để tạo tên cho context.
NameComponent market = new NameComponent (“Market”, “Market folder”);
Phương thức khởi tạo NameComponent gồm 2 đối số. Đối số thứ nhất là tên ta muốn đặt cho đối tượng, đối số thứ hai là định danh để nhận dạng đối tượng. Một đối tượng context có hai khả năng. Khả năng tạo ra các context khác và khả năng ràng buộc một NameComponent gắn với một đối tượng nào đó vào context. Trước hết, chúng ta cần cung cấp đường dẫn tương ứng. Với thành phần Market, ta chỉ định nó là xuất phát đầu tiên từ context gốc như sau:
NameComponent path[] = {market};
Sau đó là tạo mới đối tượng context Market dựa trên đường dẫn path. Phương thức bind_new_context() của đối tượng context gốc sẽ giúp ta làm việc này:
NamingContext marketctx = ctx.bind_new_context(path);
Quá trình tạo các context khác như Stock và Sheft diễn ra tương tự. Biên dịch và thực thi chương trình:
- Chạy trình chủ đăng kí các đối tượng:
Xây dựng trình duyệt các thành phần tên của COS Naming:
Các đối tượng đã lưu trong dịch vụ COS Naming ở trên có kiểu là Generic, nên trước hết ta sử dụng idlj để dịch tệp Generic.idl ra các lớp hỗ trợ cần thiết cho máy khách:
idlj –fclient Generic.idl
Ta thu được lớp Generic và GenericHelper để sử dung trong quá trình duyệt các đối tượng ObjectA, ObjectB, ObjectC đã đăng ký.
Chương trình duyệt các thành phần tên COS Naming xây dựng như sau:
//NamingBrowser.java
import org.omg.CORBA.*; import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*; class NamingBrowse{
BindingListHolder bl = new BindingListHolder();
BindingIteratorHolder blIt= new BindingIteratorHolder(); nc.list(1000, bl, blIt);
Binding bindings[] = bl.value; if (bindings.length == 0) return;
// Duyệt danh sách tên lấy được
for (int i=0; i < bindings.length; i++) {
// Lấy về tham chiếu của đối tượng dựa vào đường dẫn
org.omg.CORBA.Object obj = nc.resolve(bindings[i].binding_name);
//Kiểm tra xem có phải là context hay không
if (bindings[i].binding_type == BindingType.ncontext) {
int lastIx = bindings[i].binding_name.length-1;
System.out.println( "Context: "
+ bindings[i].binding_name[lastIx].id);
//Đệ quy tìm sâu xuống các context bên dưới nếu có
getAllTree((NamingContext)obj); }
else {
// Lấy về tên của đối tượng trong đường dẫn
int lastIx = bindings[i].binding_name.length-1; System.out.println("Object: "
+ bindings[i].binding_name[lastIx].id);
// Lấy về đối tượng với tên ràng buộc tương ứng
Generic generic=GenericHelper.narrow(obj); System.out.println(generic.getName());
}
} }
public static void main(String args[]) throws Exception{ System.out.println("CORBA Browse Application");
// Khởi tạo trình môi giới ORB
ORB orb=ORB.init(args,null);
// Lấy về context gốc của dịch vụ COS Naming
NamingContext nc = NamingContextHelper.narrow
(orb.resolve_initial_references("NameService"));
// Duyệt toàn bộ nội dung cây, bắt đầu từ context gốc
getAllTree(nc);
System.out.println("End browse"); }
}
Giải thích:
Phương thức getAllTree() chịu trách nhiệm duyệt nội dung của COS Naming bắt đầu từ context gốc. Trước hết, ta cần tạo sẵn một danh sách nắm giữ các đối tượng do tham số NamingContext nc trả về. Đối tượng BindingListHolder dùng để nhận về một số lượng tên nhất định (1000 tên) từ tham số nc. Đối tượng
BindingIteratorHolder được dùng để chứa các ràng buộc bổ sung nếu có.
Sau lời gọi nc.list(); ta lấy về được một danh sách các đường dẫn chứa trong mảng có kiểu Binding:
Binding bindings[] = bl.value;
Sau đó, ta duyệt toàn bộ mảng bindings[] để truy tìm đối tượng. Với mỗi đường dẫn, ta lấy về đối tượng ràng buộc với nó:
org.omg.CORBA.Object obj = nc.resolve(bindings[i].binding_name);
và kiểm tra xem đối tượng gặp phải và context hay NamingComponent. Nếu là context thì xuất ra tên của nó và tiếp tục duyệt sâu xuống dưới, nếu là component thì xuất ra tên của đối tượng Generic.
3.1.10. Lưu địa chỉ tham chiếu của đối tượng CORBA ở dạng chuỗi – Kỹ thuật STRING FIELD (IOR):
Nếu thấy dịch vụ COS Naming của CORBA quá phức tạp thì vẫn còn cách truy xuất khác dễ dàng mà không cần đến COS Naming.
CORBA cho phép chuyển đổi tham chiếu của một đối tượng trên máy chủ thành một chuỗi (String). Ta lưu chuỗi này thành tập tin và chuyển đến trình máy khách. Trình máy khách đọc nội dung của chuỗi sau đó khôi phục chuỗi trở lại thành tham chiếu của đối tượng. Kỹ thuật này trong CORBA gọi là String field (còn có tên khác là IOR-Interoperable Object References)
Ví dụ:
Sử dụng lại đối tượng Generic ở trên, ta cài đặt hai tập tin Setup.java và Client.java:
//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.io.*; class Setup{
System.out.println("Corba Stringified Demo"); ORB orb=ORB.init(args,null);
GenericServant servant= new GenericServant("My Generic Object"); servant.setORB(orb);
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA")); rootpoa.the_POAManager().activate();
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(servant); Generic Gserv = GenericHelper.narrow(ref);
// yêu cầu ORB tạo chuỗi tên đối tượng
String genericRef=orb.object_to_string(Gserv);
// Tạo tập tin lưu chuỗi
FileOutputStream f = new FileOutputStream("t.tmp");
ObjectOutputStream outstream = new ObjectOutputStream(f); outstream.writeObject(genericRef);
System.out.println("Already save reference to file"); System.out.println("Wait for client request ..."); java.lang.Object obj = new java.lang.Object(); synchronized (obj){
obj.wait(); } }
}
Sau khi tạo ra đối tượng CORBA Generic, chương trình gọi phương thức object_to_string() để yêu cầu ORB chuyển địa chỉ tham chiếu của đối tượng về dạng chuỗi. Sau đó ta mở tập tin “t.tmp” để lưu nội dung chuỗi xuống file. Bởi vì kiểu String đã cài đặt sẵn giao diện Serializable nên nội dung của file sẽ được giữ nguyên khi truyền đi nơi khác khôi phục lại. Ta chuyển tập tin “t.tmp” đến máy khách và trên máy khách sẽ tham chiếu và gọi đối tượng Generic như sau:
//Client.java
import org.omg.CORBA.*; import org.omg.CosNaming.*; import java.io.*;
class Client {
public static void main(String args[]) throws Exception{
// Khởi tạo trình môi giới ORB
ORB orb=ORB.init(args,null);
// Mở file t.tmp và đọc dữ liệu
FileInputStream f = new FileInputStream("t.tmp");
ObjectInputStream instream = new ObjectInputStream(f);
// Đọc đối tượng chuỗi chứa trong tập tin
String genericRef =(String) instream.readObject();
// Nhờ ORB chuyển đổi chuỗi trở về tham chiếu của đối tượng CORBA
org.omg.CORBA.Object o = orb.string_to_object(genericRef);
// Chuyển kiểu đối tượng CORBA trở về tham chiếu cụ thể
Generic generic = GenericHelper.narrow(o);
// Gọi phương thức của đối tượng CORBA
System.out.println( generic.getName() ); }
}
Nhận xét:
Cách tham chiếu bằng kỹ thuật này trong CORBA tiện lợi và đơn giản hơn tham chiếu bằng dịch vụ COS Naming. Tuy nhiên, tập tin t.tmp chỉ có giá trị khi chương trình Setup chưa chấm dứt. Nghĩa và vòng lặp chờ vô tận
synchronized(obj) { obj.wait(); }
vẫn chưa thoát ra. Nếu ta chấm dứt chương trình Setup và chạy lại thì nội dung của tập tin t.tmp sẽ thay đổi và ta sẽ phải gửi lại tệp này đến máy khách để cập nhật. Đối với dịch vụ COS Naming cũng vậy, một khi đã ràng buộc tên cho đối tượng, ta phải duy trì sự hoạt động liên tục của cả hai phía. Nếu trình đăng ký tnameserv.exe chấm dứt thì tham chiếu của máy khách từ xa đến đối tượng phục vụ cũng sẽ mất hiệu lực.
Thực thi chương trình:
- Chạy trình khách: