บทที่ 4
Jini Network เบื้องต้น
4.1 การค้นหา Lookup Service
สำหรับการที่ Client จะเรียกใช้ Remote Service จะต้องทำการค้นหา Service นั้นผ่านทาง
Lookup Service ก่อน กระบวนการแรกที่ต้องทำก่อนจะค้นหา Service ก็คือการค้นหา
Lookup Service และสำหรับ Server การที่จะลงทะเบียนตัวเองกับ Lookup Service
สิ่งแรกที่ต้องทำก็คือการค้นหา Lookup Service เช่นกัน เพราะฉะนั้นกระบวนการค้นหา
Lookup Service นี้ จึงเป็นกระบวนการที่ใช้ร่วมกันทั้ง Client และ Server
การค้นหา Lookup Service นั้นสามารถทำได้ทั้งวิธี Unicast ผ่าน TCP Protocol ( กรณีทราบอยู่แล้วว่า
Lookup Service อยู่ที่ใดในเครือข่าย ) หรือ Multicast ผ่าน UDP Protocol ( กรณีไม่ทราบว่ามี Lookup
Service ทำงานอยู่ที่ไดบ้าง ) ซึ่งแท้จริงแล้ว Lookup Service ก็เป็น Service
ชนิดหนึ่งของ Jini แต่ว่าจะมีความพิเศษกว่า Service อื่น ๆ คือทำหน้าที่เก็บรักษา Service อื่น ๆ
แล้วส่งต่อ Remote Service ไปยัง Client ที่ต้องการ
4.1.1 การค้นหาแบบ Unicast
การค้นหาแบบ Unicast Discovery จะใช้กรณีที่รู้ว่า Lookup Service ทำงานอยู่ที่ไหน ส่วนใหญ่ใช้สำหรับอยู่นอกเครือข่าย ( WAN หรือ Internet ) โดยต้องระบุ URL หรือ IP Address ให้ชัดเจน
LookupLocator
การทำ unicast discovery ค้นหา Lookup Service ผ่าน LookupLocator Class
package net.jini.core.discovery;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import net.jini.core.lookup.ServiceRegistrar;
public Class LookupLocator implements Serializable {
public LookupLocator( java.lang.String url ) throws java.net.MalformedURLException;
public LookupLocator( java.lang.String host, int port )
public String getHost();
public int getPort();
public ServiceRegistrar getRegistrar() throws IOException, ClassNotFoundException;
public ServiceRegistrar getRegistrar( int timeout ) throws IOException, ClassNotFoundException;
}
สำหรับ constructor แรก มีการส่งค่าตัวแปร url เราจะต้องกำหนดรูปเป็น jini://host หรือ jini://host:port
กรณีไม่มีการกำหนด port จะถือค่าตาม default คือ 4160 ส่วน host สามารถใช้ชื่อว่า localhost หรือชื่อที่ DNS รู้จัก หรือเป็น IP Address
ถ้า url ไม่ถูกต้อง หรือไม่มีอยู่จริง มันจะผ่านค่าความผิดพลาด ( throws ) มาทาง java.net.MalformedURLException
สำหรับ constructor ตัวที่สอง มีการส่งค่าตัวแปรแยกระหว่าง host กับ port อย่างชัดเจน
สำหรับการทำ unicast discovery นั้น จะทำผ่าน Method ชื่อ getRegistrar() หรือ getRegistrar( int timeout )
กรณีไม่มีการกำหนด timeout จะถือค่าตาม default คือ 60 วินาที ถ้าค้นหา Lookup Service พบ Method ชื่อ getRegistrar()
จะส่ง ServiceRegistrar Object กลับมาให้ แต่ถ้าไม่พบ จะส่งค่าความผิดพลาด ( throw ) มาทาง IOException และ ClassNotFoundException
ซึ่งการนำ ServiceRegistrar Object ไปใช้ ขึ้นอยู่กับว่าเป็น Server หรือ Client โดยจะกล่าวถึงรายละเอียดในภายหลัง แผนภาพ UML Sequence
Diagram ของการค้นหา Lookup Service ของ LookupLocator คือ
รูปที่ 4-1 UML Sequence Diagram ของ Unicast Discovery
ซึ่งตัวอย่างโปรแกรมที่ทำการ
ค้นหา Lookup Service ด้วยวิธียูนิคาสท์แล้วทำการโหลด ServiceRegistrar
ออบเจ็กต์มา มีดังต่อไปนี้
public class UnicastRegister {
static public void main(String argv[]) {
new UnicastRegister();
}
public UnicastRegister() {
LookupLocator lookup = null;
ServiceRegistrar registrar = null;
try { lookup = new LookupLocator("jini://localhost"); }
catch(java.net.MalformedURLException e) {
System.err.println("Lookup failed: " + e.toString());
System.exit(1);
}
try { registrar = lookup.getRegistrar(); }
catch (java.io.IOException e) {
System.err.println("Registrar search failed: " + e.toString());
System.exit(1);
} catch (java.lang.ClassNotFoundException e) {
System.err.println("Registrar search failed: " + e.toString());
System.exit(1);
}
System.out.println("Registrar found");
// the code takes separate routes from here for client or service
}
} // UnicastRegister
4.1.2 การค้นหาแบบ Multicast
ถ้าเราไม่สามารถระบุตำแหน่งของ Lookup Service
ได้เราจำเป็นที่จะต้องทำการบรอดคาสท์ (Broadcast) เพื่อค้นหา Lookup Service
การค้นหานั้นอาจจะกระทำผ่านอินเตอร์เน็ต แต่ปรกตินั้นการค้นหาจะถูกจำกัดอยู่ภายใน
LAN หรือในเครือข่ายขององค์กร ซึ่งในเครือข่ายขนาดเล็กเช่น
เครือข่ายภายในบ้านอาจจะมี Lookup Service ทำงานอยู่เพียง 1 ตัวเท่านั้น
แต่ถ้าเป็นเครือข่ายขนาดใหญ่แล้วมักจะมีมากกว่า 1 ตัว ซึ่งถ้าหากมี Lookup Service
จำนวนมาก เราสามารถแบ่ง Lookup Service ออกเป็นกลุ่มต่าง ๆ สำหรับประเภทต่าง ๆ ของ
Service ที่มาลงทะเบียนกับ Lookup Service นั้นๆ ตัวอย่างเช่นอาจจะมี Lookup
Service เฉพาะสำหรับแผนกเอกสารซึ่งทำหน้าที่ดูแล Printer Service
ก็ให้ชื่อกลุ่มว่า Printer Room หรือมี Lookup Service สำหรับ
ห้องประชุมหลาย ๆ ห้อง เราอาจะตั้งชื่อกลุ่มของ Lookup Service สำหรับห้องประชุมว่า
Conference Room โดย Service
จะรู้ว่าตนอยู่ในกลุ่มไหนโดยการกำหนดรายชื่อของกลุ่มให้กับ Service โดยกำหนดเป็น
อาร์เรย์ของสตริงก์ ดังนี้
String[ ] groups = {Printer Room , Conference Room};
LookupDiscovery
การค้นหา
Lookup Service ด้วยวิธีทำมัลติคาสท์ นั้นเราจะใช้คลาส LookupDiscovery
มาช่วยในการค้นหา Lookup Service ซึ่ง LookupDiscovery นั้นเป็น คลาสที่มากับ
แพคเกจ net.jini.discovery ซึ่งคลาสนี้มีเพียงคอนสตรัคเตอร์ เดียวคือ
LookupDiscovery(java.lang.String[] groups) ;
พารามิเตอร์ที่ผ่านให้กับ
LookupDiscovery สามารถเป็นได้ 3 กรณีคือ
-
null หรือ LookupDiscovert.ALL_GROUPS หมายความว่าการค้นหา Lookup Service
นั้นจะค้นหาทุก ๆ Lookup Service ที่ค้นพบโดยไม่สนใจว่า Lookup Service
นั้นจะอยู่ในกลุ่มไหน
-
หากเป็น อาร์เรย์เปล่า ของสตริงก์ หรือ LookupDiscovery.NO_GROUPS หมายความว่า
ให้สร้าง ออบเจ็กต์ของ LookupDiscovery ขึ้นมาเฉยๆ แต่ไม่ต้องมีการค้นหา Lookup
Service ซึงในกรณีนี้จะใช้ Method ชื่อ setGroups() ในการสั่งให้ทำการค้นหา
-
อาร์เรย์ของชื่อกลุ่ม หมายความว่าให้ค้นหาเฉพาะ Lookup Service
อยู่ในกลุ่มที่กำหนดไว้เท่านั้น
Discovery Listener
การทำมัลติคาสท์นั้นเป็นการทำงานบนเครือข่าย ซึ่งคาดว่าจะมี Lookup Service
ตอบกลับมา แต่การที่ ต้องรอให้ LookupDiscovery ค้นหาจนกระทั่งมีการตอบกลับมาจาก
Lookup Service นั้นเป็นการเสียเวลามาก ซึ่งวิธีการจัดการกับความไม่แน่นอนนี้ก็คือ
การสร้างออบเจ็กต์ตัวดักฟังมาเพื่อลงทะเบียนกับ LookupDiscovery
ซึ่งออบเจ็กต์ตัวดักฟังนั้นจะถูกเรียกให้ทำงานในเวลาที่มีการตอบรับกลับมาจาก
Lookup Service โดยการลงทะเบียนนั้น จะเรียกใช้ Method ของ LookupDiscovery คือ
public void
addDiscoveryListener(DiscoveryListener l);
โดยที่
ออบเจ็กต์ตัวดักฟังนั้นจะต้องสร้างตาม Interface ของ DiscoveryListener ดังนี้
package net.jini.discovery;
public abstract interface DiscoveryListener {
public void discovered(DiscoveryEvent e);
public void discarded(DiscoveryEvent e);
}
ซึ่ง Method ชื่อ discovered() จะถูกเรียกให้ทำงานจาก LookupDiscovery
เมื่อมีการตอบรับมาจาก Lookup Service และ discarded()
จะถูกเรียกให้ทำงานเมื่อโปรแกรมต้องการจะเลิกการติดต่อกับ Lookup Service
โดยการเรียกใช้ Method ชื่อ discard() ผ่านทาง ออบเจ็กต์ ServiceRegistrar
ซึ่งพารามิเตอร์สำหรับเม-ธอด discovered() คือ DiscoveryEvent ออบเจ็กต์ซึ่งมี
Interface ดังนี้
package net.jini.discovery;
public Class DiscoveryEvent {
public net.jini.core.lookup.ServiceRegistrar[]
getRegistrars();
}
เมธอด
getRegistrars() จะคืนค่ามาเป็น อาร์เรย์ของ ออบเจ็กต์ ServiceRegistrar ซึ่ง
ออบเจ็กต์แต่ละตัวนั้นจะเหมือนกัน (มาจากคลาสเดียวกัน)
กับออบเจ็กต์ที่คืนมาจากการค้นหาแบบ ยูนิคาสท์ ซึ่งการส่งค่าให้กับ
DiscoveryListerner อาจจะมี ServiceRegistrar ส่งมามากกว่า 1
ตัวก็ได้จากการส่งมาครั้งเดียว ซึ่ง UML ของกระบวนการค้นหา Lookup Service
แบบมัลติคาสท์ คือ
รูปที่ 4-2 การค้นหา Service แบบมัลติกาสท
ตัวอย่างโปรแกรมที่ทำการค้นหา
Lookup Service ด้วยวิธี มัลติคาสท์ แล้วทำการ โหลดออบเจ็กต์ของ ServiceRegistrar
มาจาก Lookup Service มีดังนี้
import
net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
/* MulticastRegister.java */
public class MulticastRegister implements DiscoveryListener {
static public void main(String argv[]) {
new MulticastRegister();
// stay around long enough to receive replies
try { Thread.currentThread().sleep(10000L); }
catch(java.lang.InterruptedException e) { // do nothing }
}
public MulticastRegister(){
System.setSecurityManager(new java.rmi.RMISecurityManager());
LookupDiscovery discover = null;
try {
discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
} catch(Exception e) {
System.err.println(e.toString());
e.printStackTrace(); System.exit(1);
}
discover.addDiscoveryListener(this);
}
public void discovered(DiscoveryEvent evt) {
ServiceRegistrar[] registrars = evt.getRegistrars();
for (int n = 0; n < registrars.length; n++) {
ServiceRegistrar registrar = registrars[n];
// the code takes separate routes from here for client or service
System.out.println("found a service locator");
}
}
public void discarded(DiscoveryEvent evt) {
}
} // MulticastRegister
4.1.3 ServiceRegistrar
เป็นแอ๊บสแทรกต์คลาส (Abstract Class) ที่ถูกสร้างขึ้นในแต่ละ Lookup Service
หน้าที่ของ ServiceRegistrar คือทำหน้าที่เป็นพรอกซี่ให้กับ Lookup Service
ซึ่งตัวพรอกซี่นี้จะทำงานอยู่ที่แอพพลิเคชันทั้งที่ฝั่ง Client และฝั่ง Server
ServiceRegistrar เป็นออบเจ็กต์ตัวแรกที่มีการเคลื่อนย้ายระหว่าง จาวาโพรเซสใน
เครือข่ายของ Jini ServiceRegistrar จะถูกเคลื่อนย้ายจาก Lookup Service ไปยัง
แอพพลิเคชันที่ทำการค้นหา Lookup Service ผ่านทางการเชื่อมต่อแบบ ซ๊อกเก็ต จากนั้น
ServiceRegistrar จะทำงานอยู่บนแอพพลิเคชันที่ต้องการติดต่อกับ Lookup Service
โดยที่แอพพลิเคชันนั้นจะเรียกใช้ Method ของ ServiceRegistrar เพื่อติดต่อไปยัง
Lookup Service ซึ่งในตัว ServiceRegistrar จะไม่ได้เก็บข้อมูลใด ๆ ไว้ในฝั่ง
แอพพลิเคชันเลยแต่เมื่อ application ต้องการข้อมูลจาก Lookup Service
ServiceRegistrar ก็จะไปดึงข้อมูลจาก Lookup Service มาให้ โดยที่ Lookup Service
จะมี Method ที่สำคัญอยู่สองส่วนคือ
1)
ใช้สำหรับ Service ในการ ลงทะเบียน ตัวเองเข้ากับ Lookup Service
public
ServiceRegistration register(ServiceItem item,
long leaseDuration) throws java.rmi.RemoteException
2)
ใช้สำหรับ Client ในการค้นหา Service อื่น ๆ จาก Lookup Service
public java.lang.Object
lookup(ServiceTemplate tmpl)
throws java.rmi.RemoteException;
public ServiceMatches
lookup(ServiceTemplate tmpl, int maxMatches)
throws java.rmi.RemoteException;
ซึ่งกระบวนการที่ Service ใช้ ServiceRegistrar ในการลงทะเบียน Service เข้ากับ
Lookup Service และกระบวนการที่ Client ใช้ในการค้นหา Service อื่น ๆ ใน Lookup
Service จะกล่าวถึงในบทต่อๆไป
4.2 การสร้าง Server ของ Service
4.2.1 ServiceRegistrar
Server
สำหรับ Service สามารถทำการค้นหา Lookup Service ด้วยวิธี ยูนิคาสท์ ด้วย
LookupLocator และวิธีมัลติคาสท์ ด้วย LookupDiscovery
ซึ่งผลลัพท์ของทั้งสองวิธีก็คือออบเจ็กต์ของ ServiceRegistrar นั่นเอง
ดังที่ได้กล่าวมาแล้ว ServiceRegistrar ทำหน้าที่เป็นพรอกซี่ให้กับ Lookup Service
ซึ่งกระบวนการการ ลงทะเบียนของ Service ไปยัง Lookup Service นั้นก็ทำผ่านเมธอด
register() ของ ServiceRegistrar นั่นเอง
public Class ServiceRegistrar {
public ServiceRegistration
register(ServiceItem item,
long leaseDuration)
throws java.rmi.RemoteException;
}
พารามิเตอร์ตัวที่สองคือระยะเวลา (มิลลิวินาที) ที่ Lookup Service จะยังคงให้
Service นั้นลงทะเบียนซึ่ง Service จะต้องแจ้งให้ Lookup Service
ทราบก่อนที่เวลาจะหมดว่าต้องการลงทะเบียนต่อ หากไม่ได้แจ้ง Lookup Service
ก็จะทำการยกเลิกการลงทะเบียนของ Service
ซึ่งรายละเอียดเกี่ยวกับกระบวนการนี้จะอธิบายอีกทีในหัวข้อเรื่อง ลีส (Lease)
ส่วนพารามิเตอร์ตัวแรกเป็นออบเจ็กต์ซึ่งมีชนิด คือ ServiceItem
package net.jini.core.lookup;
public Class ServiceItem {
public ServiceID serviceID;
public java.lang.Object service;
public Entry[] attributeSets;
public ServiceItem(ServiceID serviceID,
java.lang.Object service,
Entry[] attrSets);
}
4.2.2 ServiceItem
Server จะทำการสร้างออบเจ็กต์ของ ServiceItem
และส่งเป็นพารามิเตอร์ให้กับเมธอด register() เพื่อใช้ในการลงทะเบียน Service
โดยค่าพารามิเตอร์ที่ส่งผ่านให้กับคอนสตรัคเตอร์ของ ServiceItem ตัวแรกคือ
ServiceID ส่งให้เป็น null เมื่อ Service นั้นลงทะเบียนกับ Lookup Service
เป็นครั้งแรก หลังจากลงทะเบียนแล้ว Lookup Service จะทำการสร้าง ServiceID ให้กับ
Service เพื่อใช้เป็น ตัวอ้างอิงเฉพาะ (Unique Identifier) ให้กับ Service
เพื่อใช้ต่อไป
รูปที่ 4-3 กระบวนการลงทะเบียน Service
พารามิเตอร์ตัวที่สองคือ Service Object ที่จะใช้ ลงทะเบียน กับ Lookup Service
โดยออบเจ็กต์นี้จะถูกซีเรียลไลซ์ (Serialize) และส่งไปให้กับ Lookup Service
เมื่อมี Client ต้องการที่จะเรียกใช้ Service นี้ Lookup Service
ก็จะส่งออบเจ็กต์นี้ให้กับ Client
พารามิเตอร์ตัวที่สามคือกลุ่มของข้อมูลเพิ่มเติมเกี่ยวกับ Service (Service
Attribute) อยู่ในลักษณะของอาร์เรย์ของ Entry
ออบเจ็กต์ซึ่งถ้าหากไม่มีข้อมูลเพิ่มเติมก็สามารถส่งค่า null ให้กับ
คอนสตรัคเตอร์ได้ วิธีการในการเตรียม Service Attribute สามารถดูได้ในท้ายบทนี้
4.2.3 ServiceRegistration
หลังจากที่ได้เรียกใช้เมธอด register() เพื่อลงทะเบียน Service กับ Lookup Service
แล้วเมธอด register() จะคืนค่ามาเป็นออบเจ็กต์ที่มี Type คือ ServiceRegistration
ซึ่งออบเจ็กต์นี้ทำหน้าที่เป็น Proxy ที่ทำหน้าที่ควบคุมสถานะต่าง ๆ
ของออบเจ็กต์ที่อยู่ที่ Lookup Service
ออบเจ็กต์นี้จะมีข้อมูลต่าง ๆ ของ Service ที่ Lookup Service ได้สร้างให้ด้วย
ข้อมูลที่สำคัญอันหนึ่งก็คือ ServiceID ซึ่งใช้ในการระบุถึง
ออบเจ็กต์ออบเจ็กต์ที่อยู่ที่ Lookup Service ด้วย ServiceID
นี้สามารถเรียกดูได้จากเมธอด getServiceID() ของออบเจ็กต์ ServiceRegistration
และค่า ServiceID ยังสามารถนำมาใช้ในการลงทะเบียน Service นี้กับ Lookup Service
ตัวอื่น ๆ ได้อีก เพื่อให้ Service นี้มี ID เดียวกันในทุก ๆ Lookup Service
ServiceRegistration
ยังมี Method อื่น ๆ อีกที่ใช้ในการปรับเปลี่ยน Attribute ต่าง
ของออบเจ็กต์ที่เก็บอยู่ที่ Lookup Service คือ
void addAttributes(Entry[] attrSets);
void modifyAttributes(Entry[] attrSetTemplates, Entry[]
attrSets);
void setAttributes(Entry[] attrSets);
และยังมี Method สุดท้ายที่สำคัญอยู่อีก 1 เมธอดคือ getLease() ซึ่งจะคืนค่ามาเป็น
Lease ออบเจ็กต์ของการลงทะเบียน ซึ่งจะกล่าวถึงเรื่อง Lease
ออบเจ็กต์โดยรายละเอียดอีกทีหนึ่งงานหลัก ๆ ของ Server ก็มีเพียงเท่านี้
หลังจากที่ Server ได้ส่ง Service Object ไปเก็บไว้ที่ Lookup Service
แล้วงานที่เหลือของ Server ก็คือการทำให้ Service ยังคงลงทะเบียนกับ Lookup
Service อยู่เสมอ เนื่องจากถ้าหากว่า Service Object ที่ไปฝากไว้ที่ Lookup
Service นั้นสามารถทำงานทุกอย่างได้เหมือนกับ Service ที่ Server ตัว Server ที่มี
Service ทำงานอยู่ก็ไม่จำเป็นต้องคงอยู่อีกต่อไปเนื่องจาก การทำงานทั้งหมด Service
Object ที่อยู่ที่ Lookup Service สามารถทำงานแทนได้
แต่ในความจริงไม่ได้เป็นเช่นนั้น Service Object ที่ Server ได้ส่งให้กับ Lookup
Service นั้นมักจะทำหน้าที่เป็นแค่พรอกซี่ที่จำเป็นต้องติดต่อกลับมายัง Service
เพื่อเรียกให้ Service ทำงาน เพราะฉะนั้น Service จึงต้องยังคงทำงานอยู่เสมอ
ซึ่งการลงทะเบียนตัวเองกับ Lookup Service อยู่เสมอนั้นก็เป็นการยืนยันว่า Service
ยังคงทำงานอยู่นั่นเอง
ซึ่งกระบวนการลงทะเบียนอยู่เสมอนั้นเราจะใช้กระบวนการของลีสซึ่งจะกล่าวถึงรายละเอียดอีกทีในบทที่กล่าวถึงเรื่องลีส
ตัวอย่างโปรแกรมที่ทำหน้าที่เป็น
Server ของ Service แบบง่ายๆ ซึ่ง Service Object ก็คือตัว Server เอง และใช้วิธี
ยูนิคาสท์ ในการค้นหา Lookup Service มีดังนี้
import net.jini.core.discovery.LookupLocator;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistration;
import java.io.Serializable;
/** SimpleService.java */
public class SimpleService implements Serializable {
static public void main(String argv[]) {
new SimpleService();
}
public SimpleService() {
LookupLocator lookup = null;
ServiceRegistrar registrar = null;
try { lookup = new LookupLocator("jini://localhost"); }
catch(java.net.MalformedURLException e) {
System.err.println("Lookup failed: " + e.toString());
System.exit(1);
}
try { registrar = lookup.getRegistrar(); }
catch (java.io.IOException e) {
System.err.println("Registrar search failed: " +
e.toString());
System.exit(1);
}
catch (java.lang.ClassNotFoundException e) {
System.err.println("Registrar search failed: " + e.toString()); System.exit(1);
}
// register ourselves as service, with no serviceID
// or set of attributes
ServiceItem item = new ServiceItem(null, this, null);
ServiceRegistration reg = null;
try { // ask to register for 10,000,000 milliseconds
reg = registrar.register(item, 10000000L);
} catch(java.rmi.RemoteException e) {
System.err.println("Register exception: " + e.toString());
}
System.out.println("Service registered");
// we can exit here if the exported service object can do
// everything, or we can sleep if it needs to communicate
// to us or we need to renew a lease later
//
// Typically, we will need to renew a lease later
}
} // SimpleService
สิ่งที่น่าสนใจของ โปรแกรมนี้คือ การ implements Serializable ของคลาสนี้ซึ่งการ
Implement Serializable หมายความว่าออบเจ็กต์ของคลาสนี้สามารถทำการ ซีเรียลไลซ์ได้
ที่ต้องทำให้ ซีเรียลไลซ์ ได้เพราะว่าคลาสนี้ทำตัวเป็น Service Object ด้วย และ
Service Object จะต้องถูก ซีเรียลไลซ์ เพื่อส่งตัวออบเจ็กต์ไปยัง Lookup Service
เพราะฉะนั้นจึงต้องทำให้คลาสนี้ที่จะเป็น Service Object นั้น ซีเรียลไลซ์
ได้ด้วยการ Implement Serializable
และคลาสไฟล์
ของ Service Object จะตัองถูกส่งไปทางเครือข่ายด้วย โดยอาจจะส่งผ่านไปทาง HTTP
Server หรือวิธีใดก็ตามไม่จำกัดอยู่เพียงแค่ HTTP
4.2.4 Service Attribute และ Interface ของ Entry
Service
Attribute ใช้ในการระบุถึงรายละเอียดของ Service เช่นหากว่า Service นั้นเป็น
Service ของ Printer Attribute ก็มักจะเป็นตำแหน่งที่อยู่ของ Printer
หรือเป็นความสามารถของ Printer ต่าง ๆ เช่น สามารถพรินต์สีได้หรือไม่
สามารถพิมพ์ที่ความละเอียดสูงสุดเท่าใด หรือแม้แต่เป็นสถานะต่าง ๆ ของ Printer
เช่น กระดาษหมด หรือหมึกพิมพ์หมดเป็นต้น Attribute
แต่ละตัวที่ใช้ในการระบุถึงรายละเอียดของ Jini Service นั้นไม่ได้อยู่ในรูปของชื่อ
Attribute และค่าพารามิเตอร์โดยตรง แต่ว่ามีลักษณะเป็นออบเจ็กต์ของคลาสที่สร้างตาม
Interface ของ net.jini.core.entry.Entry ตัวอย่างของการสร้าง Entry
คลาสมีดังนี้Entry ออบเจ็กต์นั้นจะต้องเป็นออบเจ็กต์ที่สามารถซีเรียลไลซ์ได้
ซึ่งข้อแตกต่างของ Attribute ต่าง ๆ นั้นขึ้นอยู่กับวิธีการ สร้างจาก Interface
ของ Entry แต่เนื่องจากตัว Interface ของ Entry นั้นไม่มี Method มันจึงทำหน้าที่เป็น
ตัวแสดงชนิด (Type Identifier) เท่านั้นโดยข้อกำหนดในการสร้างคลาสของ Entry
ออบเจ็กต์คือ
-
จะต้อง implement interface net.jini.core.entry.Entry และ java.io.Serializable
-
ทุก ๆ ฟีลด์ ของคลาสจะต้องเป็น Public
-
ไม่สามารถมี Instance Variable ของ Primitive Type ได้
-
ไม่สามารถอ้างอิงถึงออบเจ็กต์ที่มี ชนิดเป็น static, transient, final
หรือตัวที่ไม่ได้เป็น public เนื่องจากว่าออบเจ็กต์ที่มีชนิดเหล่านี้ไม่สามารถทำ
ซีเรียลไลซ์ ได้และจะถูก สร้างขึ้นใหม่เมื่อทำ ดีซีเรียลไลซ์
-
จะต้องมี ดีฟอลต์คอนสตรัคเตอร์ (ไม่มี อาร์กิวเมนต์)
ตัวอย่างของการสร้างคลาสของออบเจ็กต์ Entry คือ
import net.jini.entry.*;
import net.jini.lookup.entry.*;
public class Copyright extends AbstractEntry implements
ServiceControlled{
//The field of Entry
//each must be a public, serializable
object
public String owner;
public String date;
public String lawfirm;
public Copyright(){
this(null,null,null);
}
public Copyright(String owner,String
date,String lawfirm){
this.owner = owner;
this.date = date;
this.lawfirm = lawfirm
}
}
จากตัวอย่างเป็นการสร้าง Attribute ใหม่ชื่อว่า Copyright ซึ่งประกอบด้วย 3
ฟีลด์ซึ่งแสดงชื่อ,วันที่และองค์กรที่ดูแลของ Copyright
ซึ่งการสร้างคลาสนี้ทำตามข้อกำหนดด้านบนคือ ทุกฟีลด์เป็น public มี
ดีฟอลต์คอนสตรัคเตอร์และ Implement net.jini.core.entry.Entry และ
java.io.Serializable ด้วยการอินเฮอริแทนซ์มาจาก net.jini.entry.AbstractEntry
ซึ่งได้ implement ทั้งสอง Interface ไว้แล้ว และการ implement
net.jini.lookup.entry.ServiceControlled นั้นทำเพื่อเป็นตัวบอกว่า Attribute ของ
Service นี้นั้นไม่สามารถแก้ไขได้จาก Client ซึ่ง Client
สามารถอ่านได้อย่างเดียวเท่านั้น
และเพื่อความสะดวกในแพคเกจ net.jini.lookup.entry ยังได้มี Attribute
คลาสที่สร้างมาพร้อมแล้วโดยสามารถนำมาใช้ได้เลยคือ
-
net.jini.lookup.entry.Address : ใช้บอกที่อยู่ของ Service
-
net.jini.lookup.entry.Comment : ใช้บอกข้อมูลอื่น ๆ ของ Service
-
net.jini.lookup.entry.Location : ใช้บอกตำแหน่งที่เฉพาะของ Service
-
net.jini.lookup.entry.Name : ใช้บอกชื่อของ Service
-
net.jini.lookup.entry.ServiceInfo : ใช้บอกข้อมูลเบื้องต้นเช่น ผู้ผลิต,
เวอร์ชันของ Service
-
net.jini.lookup.entry.ServiceType : ใช้บอกประเภทของ Service
-
net.jini.lookup.entry.Status : ใช้บอกสถานะปัจจุบันของ Service
ซึ่งรายละเอียดต่าง ๆ และวิธีการใช้คลาสเหล่านี้สามารถดูได้ที่
เอกสารประกอบที่มากับชุดพัฒนา Jini และตัวอย่างวิธีการสร้าง พารามิเตอร์สำหรับ
คอนสตรัคเตอร์ของ ServiceItem คือ
Entry[] attributes = new Entry[3];
attributes[0] = new Name(Laser Printer Service);
attributes[1] = new Location(4,4101,Building 4 );
attributes[2] = new Copyright(AJC
Marketing,9/1999,Oaks);
4.3 การสร้าง Client
ของ Service
4.3.1 ServiceRegistrar
เช่นเดียวกับกระบวนการของ Server หลังจากที่ Client ค้นหา Lookup Service
พบแล้วและได้ ServiceRegistrar มาเพื่อใช้เป็นพรอกซี่ไปยัง Lookup Service
แล้วนั้น Client จะเริ่มทำกระบวนการที่แตกต่างจาก Server โดยแทนที่จะใช้
ServiceRegistrar ในการลงทะเบียนไปยัง Server กลับใช้ในการค้นหา Service
ที่ต้องการ (เนื่องจาก Client ไม่จำเป็นต้องลงทะเบียนกับ Lookup Service )
โดยค้นหาผ่านทางเมธอด lookup() ของ ServiceRegistrar
public Class ServiceRegistrar {
public Object lookup(ServiceTemplate
tmpl)
throws java.rmi.RemoteException;
public ServiceMatches
lookup(ServiceTemplate tmpl,
int maxMatches)
throws java.rmi.RemoteException;
}
โดยที่เมธอด lookup() ตัวแรก จะทำเพียงแค่ค้นหา Service
ที่ต้องการแล้วส่งค่าคืนมาเป็น Service Object โดย เป็น Service Object
ที่ค้นพบเป็นตัวแรกเท่านั้น ส่วน lookup()
ตัวที่สองจะสามารถกำหนดได้ว่าต้องการให้ค้นหา Service
มาทั้งหมดไม่เกินกี่ตัวและคืนค่าออกมาเป็น Service Object ของ Service
ที่ค้นพบทั้งหมดซึ่งอ้างอิงถึงโดยเป็นอาร์เรย์ของ Service Object
อยู่ที่ออบเจ็กต์ServiceMatches.items
วิธีการที่ Client ใช้ในการค้นหา Service คือ ทำการสร้าง Template ในการค้นหา
โดยใช้วิธีการสร้างออบเจ็กต์ของคลาส net.jini.core.lookup.ServiceTemplate
ขึ้นมาเพื่อทำหน้าที่เป็น Template Object
public Class ServiceTemplate {
public ServiceID serviceID;
public java.lang.Class[] serviceTypes;
public Entry[] attributeSetTemplates;
ServiceTemplate(ServiceID serviceID, java.lang.Class[]
serviceTypes, Entry[]
attrSetTemplates);
}
พารามิเตอร์ที่ส่งให้กับ
คอนสตรัคเตอร์ของ ServiceTemplate มีดังนี้
-
ServiceID คือออบเจ็กต์ที่เป็น ตัวบ่งชี้เฉพาะของ Service ซึ่งมักจะเป็น
null เมื่อมีการค้นหาครั้งแรก และถ้ามีการค้นหา Service เดิมในครั้งต่อไป
ก็จะใช้ค่า ID ของ Service ตัวเดิมจากที่ค้นหาในครั้งแรก
-
Class[ ] ที่จะใช้เป็นต้นแบบของการค้นหา ซึ่งมักจะเป็นออบเจ็กต์ที่มี Type
เป็นคลาสซึ่งเป็น Interface ของ Service ที่ต้องการค้นหา
-
Entry[ ] ซึ่งเป็น Attribute สำหรับ Service ที่ต้องการ เพื่อใช้ในการค้นหา
Service ที่ต้องการซึ่งวิธีการสร้างเหมือนกับการสร้าง Attribute สำหรับ Service
หลังจากกระบวนการค้นหา
Service แล้ว หากว่าสามารถค้นหา Service ได้พบ ตัว ServiceRegistrar ก็จะส่ง
Service Object คืนมาในรูปแบบต่าง ๆ ตามรูปแบบของเมธอด lookup() ที่เลือกใช้
หากเลือกใช้ lookup() ตัวแรกก็จะส่งค่าออกมาเป็น Service Object ตัวแรกที่พบ
ซึ่งสามารถนำไปใช้ได้เลย แต่ถ้าหากเลือกใช้ lookup()
ตัวที่สองก็จะคืนค่าออกมาเป็นออบเจ็กต์ของคลาส ServiceMatches
package
net.jini.core.lookup;
public class ServiceMatch{
public
ServiceItem[] items;
public
int totalMatches;
}
กระบวนการต่าง ๆ
ที่กล่าวมาสำหรับทั้ง Client และ Service เป็นกระบวนการที่มีขั้นตอนเยอะและยุ่งยาก
ในชุดพัฒนา Jini ได้มีคลาสต่าง ๆ
ที่มาช่วยให้การทำงานตามขั้นตอนด้านบนนั้นง่ายขึ้น เช่น
net.jini.discovery.LookupLocatorDiscovery
net.jini.lookup.JoinManager
ซึ่งรายละเอียดเกี่ยวกับวิธีใช้คลาสช่วยเหลือเหล่านี้สามารถดูได้ที่เอกสารประกอบที่ให้มากับชุดพัฒนา
Jini
Last update : June 17, 2009 17:00 ( Thailand )
Apple, Mac, iMac, iPhone and iPod are trademarks of Apple, Inc.
Jini, Java and all Java-based are trademarks of Sun Microsystems, Inc.
JiniSoft Corporation
Copyright @ 1990 - 2009 Mr. Roongroj Rojanapo ( )
99/2 Soi Ramindra 14, Ramindra Road, Bangkane, Bangkok 10230, Thailand
|