Learning Community

ผลงานวิทยานิพนธ์
ของคุณ พนิต เวชศิลป์
ภาควิชาวิศวกรรมคอมพิวเตอร์
สถาบันเทคโนโลยีพระจอมเกล้าเจ้าคุณทหารลาดกระบัง

ปรับแก้ไขโดยนาย รุ่งโรจน์ โรจนโพธิ์ ( ธันวาคม 2548 )




Learning Community
• Home
• Jini Beginning
- บทเรียนที่ 1
- บทเรียนที่ 2
- บทเรียนที่ 3
- บทเรียนที่ 4
- บทเรียนที่ 5
- บทเรียนที่ 6
- บทเรียนที่ 7
- บทเรียนที่ 8
- บทเรียนที่ 9


บทที่ 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 กรณีคือ

  1. null หรือ LookupDiscovert.ALL_GROUPS หมายความว่าการค้นหา Lookup Service นั้นจะค้นหาทุก ๆ Lookup Service ที่ค้นพบโดยไม่สนใจว่า Lookup Service นั้นจะอยู่ในกลุ่มไหน
  2. หากเป็น อาร์เรย์เปล่า ของสตริงก์ หรือ LookupDiscovery.NO_GROUPS หมายความว่า ให้สร้าง ออบเจ็กต์ของ LookupDiscovery ขึ้นมาเฉยๆ แต่ไม่ต้องมีการค้นหา Lookup Service ซึงในกรณีนี้จะใช้ Method ชื่อ setGroups() ในการสั่งให้ทำการค้นหา
  3. อาร์เรย์ของชื่อกลุ่ม หมายความว่าให้ค้นหาเฉพาะ 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 มีดังนี้

  1. ServiceID  คือออบเจ็กต์ที่เป็น ตัวบ่งชี้เฉพาะของ Service ซึ่งมักจะเป็น null เมื่อมีการค้นหาครั้งแรก และถ้ามีการค้นหา Service เดิมในครั้งต่อไป ก็จะใช้ค่า ID ของ Service ตัวเดิมจากที่ค้นหาในครั้งแรก
  2. Class[ ] ที่จะใช้เป็นต้นแบบของการค้นหา ซึ่งมักจะเป็นออบเจ็กต์ที่มี Type เป็นคลาสซึ่งเป็น Interface ของ Service ที่ต้องการค้นหา
  3. 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


E-mail : roongroj @ mac.com
SMS : 081 615-5135  ( iPhone )
FAX : 02   943-6433