Learning Community

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

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




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


บทที่ 5

ลีสซิ่งและรีโมตอีเวนต์ำ



5.1 ลีสซิ่ง

            ลีสซิ่ง เป็นกระบวนการที่สำคัญอันหนึ่งของ Jini เฟรมเวิร์ก เพื่อให้เครือข่ายของ Jini เป็นเครือข่ายที่มีความมั่นคงสูง เนื่องจากใน Jini Network นั้น Service และ Client มีการเชื่อมต่อและออกจากระบบอยู่ตลอดเวลา ซึ่ง Jini จะต้องรับรู้ว่าในขณะนั้นมี Service หรือ Client ใดบ้างที่เชื่อมต่อกับเครือข่ายอยู่และมีใครบ้างที่ออกจากเครือข่ายไปแล้ว ดังนั้นกระบวนการของการทำลีสซิ่ง ได้ถูกสร้างขึ้นเพื่อใช้ในการที่ Service ของ Jini จะติดตามว่า Client ของ Service ในขณะนั้นยังคงทำงานอยู่หรือไม่ซึ่งกระบวนการของการทำลีสซิ่ง นั้นสามารถใช้ได้กับทั้งระหว่าง Lookup Service และ Service ของ Jini ( Service ของ Jini ถือเป็น Client ของ Lookup Service ) หรือระหว่าง Service ของ Jini และ Client ก็ได้

5.1.1 การใช้ ลีสระหว่าง Lookup Service และ Service ของ Jini

            เมื่อ Service ได้ลงทะเบียนตัวเองกับ Lookup Service สถานะของการลงทะเบียนนั้นจะคงอยู่ได้ระยะเวลาหนึ่งโดยที่ Jini ได้ใช้อายุของลีส (ลีสเป็นออบเจ็กต์ตัวหนึ่ง) เพื่อแทนถึงช่วงเวลานั้น Service จะต้องทำการ รีนิว (renew) ลีสภายในช่วงเวลาอายุขัยของลีสนั้น หากอายุขัยของลีสของลีสนั้นหมดลง Service นั้นจะถูก ยกเลิกการลงทะเบียนโดยอัตโนมัติ เพราะฉะนั้นจึงเป็นหน้าที่ของ Service ที่จะต้องทำการ รีนิว ลีสก่อนที่ลีสจะหมดอายุ ซึ่งจุดประสงค์ของการใช้กระบวนการนี้ก็คือทำให้ระบบเครือข่ายของจินี นั้นมีความมั่นคงมากขึ้น ถ้าหาก Server สำหรับ Service นั้นหยุดทำงาน (ไม่สามารถ รีนิวลีสได้อีกต่อไป) Lookup Service จะสามารถทราบได้ และสามารถนำ Service Object ที่มีอยู่ของ Service นั้นออกไปจากระบบ ทำให้ Client ไม่ต้องเสียเวลาในการพยายามที่จะติดต่อกับ Service นั้นหากได้ Service Object ไปแล้ว             สำหรับ Service ของ Jini ลีสของ Lookup Service นั้นสามารถได้มาจากกระบวนการลงทะเบียนของ Service โดยที่ช่วงอายุของลีสนั้นสามารถขอไปได้โดยผ่านทางเมธอด register() ซึ่งใน Method นี้นั้นมีพารามิเตอร์สองตัวที่ส่งไปให้ คือ Service Object และ ช่วงอายุของลีสซึ่งค่าอายุของลีสที่เป็นไปได้ในการส่งไปมี 3 แบบคือ
  1. มีชนิดเป็น long ซึ่งหมายถึงหน่วยเป็นมิลลิวินาที เช่น 1000 หมายถึง 1 วินาที
  2. Lease.ANY หมายถึง ให้ Lookup Service เป็นผู้ตัดสินใจเองว่าจะให้เวลาเท่าใด
  3.   Lease.FOREVER หมายถึง ขอลีสที่ไม่มีเวลาหมดอายุ




รูปที่ 5-1 Service ไดัรับลีสจาก Lookup Service


Lookup Service ทำหน้าที่เป็นผู้กำหนดเวลาให้กับลีสว่าจะให้มีอายุเท่าใดซึ่งไม่จำเป็นว่าจะต้องเท่ากับเวลาที่ Service ขอมาโดยส่วนมากจะน้อยกว่าหรือเท่ากับ โดยที่ลีสที่ส่งไปให้กับ Service นั้น Service สามารถรับมาได้จากเมธอด  getLease() ของ ServiceRegistration ออบเจ็กต์ โดยโครงสร้างของลีสนั้นมี Method ต่าง ๆ ดังนี้

package net.jini.core;

public interface Lease {

    void cancel();

    long getExpiration();

    void renew(long duration);

}

เวลาหมดอายุของลีสสามารถดูได้จากเมธอด getExpiration() ซึ่งคืนค่าออกมาเป็นเวลาในหน่วยมิลลิวินาทีซึ่งมีลักษณะเดียวกับเมธอด  System.currentTimeMillis() เราจึงสามารถหาเวลาที่เหลือของลีสได้จาก

long duration = lease.getExpiration() - System.currentTimeMillis();

เมื่อลีสถูกทำให้หมดอายุโดย Lookup Service นั้น Lookup Service จะไม่ได้แจ้งกลับไปยังผู้ที่ถือลีสว่าขณะนี้ลีสได้หมดอายุแล้ว ซึ่งจะต้องเป็นหน้าที่ของ Service ที่จะต้องทำการ รีนิว ก่อนที่ลีสจะหมดอายุเพื่อที่จะต่ออายุของลีสซึ่งบางทีก็เป็นเรื่องที่ยุ่งยากของโปรแกรมเมอร์ในการที่จะต้องมาดูอายุของลีสว่าหมดอายุหรือหากใกล้หมดอายุก็ต้องทำการ รีนิว แล้วบางทียังอาจจะมีการผิดพลาดเนื่องจากความล่าช้าของเครือข่ายก็ได้ ในชุดพัฒนา Jini จึงได้มีคลาสที่มาช่วยเหลือในกระบวนการนี้ คือ LeaseRenewalManager

LeaseRenewalManager

            LeaseRenewalManager จะคอยช่วยกระบวนการดูแลไม่ให้ลีสหมดอายุ ซึ่งจะทำงานโดยอัตโนมัติเมื่อถึงเวลาโครงสร้างของ LeaseRenewalManager มีดังนี้

package net.jini.lease;

public Class LeaseRenewalManager {

    public LeaseRenewalManager();

    public LeaseRenewalManager(Lease lease, long expiration,

 LeaseListener listener);

    public void renewFor(Lease lease, long duration,

                         LeaseListener listener);

    public void renewUntil(Lease lease, long expiration,

   LeaseListener listener);

    // etc

}

LeaseRenewalManager สามารถดูแลลีสได้ทีละหลาย ๆ ตัวโดยที่การส่งลีสให้ LeaseRenewalManager ดูแลนั้นสามารถส่งไปทาง คอนสตรัคเตอร์หรือส่งโดยเรียกใช้เมธอด  renewFor() และ renewUntil() เวลาที่ร้องขอไปจะเป็นหน่วยมิลลิวินาทีเหมือนกันในทุก Method   โดย expiration หมายความว่าคือเวลาที่ลีสจะหมดอายุ ส่วน duration หมายความว่า เวลานับจากนี้ไปเท่าใด

แต่อย่างไรก็ตามหาก LeaseRenewalManager ไม่สามารถ รีนิวลีสได้ด้วยสาเหตุต่าง ๆ เช่นลีสหมดอายุไปแล้วหรือเครือข่ายขัดข้อง ก็จะมี Exception ส่งออกมาซึ่งจะถูกจับไว้ด้วย LeaseRenewalManager แล้วทำการเรียกใช้เมธอด notify() ของ LeaseListener ที่ลงทะเบียนไว้เมื่อตอนสร้าง LeaseRenewalManager

5.1.2 การใช้ลีสระหว่าง Service ของ Jini และ Client

            กระบวนการการใช้ลีสในการดูแล Client ว่ายังคงเชื่อมต่อ อยู่หรือไม่นั้นสามารถนำมาใช้กับ Service ใด ๆ ก็ได้ ซึ่งการที่ Service จะใช้กระบวนการ ลีสซิ่ง สำหรับ ดูแล Client ของตนนั้น Service จะต้องสร้างกระบวนการ ลีสซิ่ง ขึ้นมาเอง เนื่องจากว่าตัวกระบวนการนั้นไม่ได้เป็นส่วนหนึ่งของ Jini API ตัว ชุดพัฒนา Jini นั้นมีเพียง Interface ของคลาส ต่าง ๆ ที่จำเป็นต้องใช้มาให้เท่านั้น ส่วนลีสและผู้ดูแลลีสนั้นจึงต้องสร้างขึ้นมาเองโดย สร้างตาม Interface ที่มีมาให้ คือ AbstractLease  และ LandLord package

5.1.2.1 AbstractLease

            AbstractLease คือ แอ็บสแทรกต์คลาส ซึ่ง สร้างตาม Interface ของ Leaseใช้ในการสร้างออบเจ็กต์ของ Lease โดยวิธีการใช้จะต้องสร้างซับคลาสของ AbstractLease อีกทีหนึ่งเนื่องจากคลาสนี้เป็นแอ็บสแทรกต์คลาส

package com.sun.jini.lease;

public abstract class AbstractLease implements Lease,java.io.Serializable {

    protected AbstractLease(long expiration);

    public long getExpiration();

    public int getSerialFormat();

    public void setSerialFormat(int format);

    public void renew(long duration);

    protected abstract long doRenew(long duration);

}

คลาสนี้ทำการสร้างตาม Lease โดยตรงโดยวิธีการนำไปใช้มีเงื่อนไข 3 อย่างคือ

  1. คอนสตรัคเตอร์ของคลาส นี้ประกาศว่าเป็นแบบ protected เพราะฉะนั้นการตั้งอายุของลีสนั้นสามารถตั้งได้โดย คลาส ที่เป็น ซับคลาสของคลาสนี้เท่านั้น
  2. เนื่องจากเมธอด  renew() จะทำการเรียกต่อไปยังเมธอด  doRenew() เพื่อทำการ รีนิว ซึ่ง doRenew()เป็น แอ็บสแทรกต์ Method   ดังนั้นจึงเป็นการบังคับให้ซับคลาสจะต้องสร้างวิธีการในการ รีนิว ด้วยการโอเวอร์ไรด์ Method นี้
  3. AbstractLease ไม่ได้ ไม่ได้มีการสร้างเมธอด  cancel() ของ Lease เหลือไว้ให้ ซับคลาสเป็นผู้ สร้าง

5.1.2.2 แพคเกจ LandLord

            แพคเกจนี้สร้างขึ้นเพื่อช่วยในการสร้างระบบลีสที่ซับซ้อนมากขึ้น โดยแพคเกจนี้ประกอบด้วย คลาส และ Interface โดยที่แพคเกจนี้ยังไม่สมบูรณ์ในตัวเอง แต่เหลือบางส่วนไว้เป็น Interface ไว้สำหรับแอพพลิเคชันนำไปสร้างเป็นระบบของตัวเอง LandLord จะใช้สำหรับการสร้างออบเจ็กต์ ซึ่งทำหน้าที่ดูแลและจัดการลีสได้หลาย ๆ ตัวโดยลีสแต่ละตัวที่ LandLord สร้างขึ้นจะถูกอ้างอิงโดย ออบเจ็กต์ ซึ่งลีสแต่ละตัวจะมี ออบเจ็กต์ ที่ใช้อ้างอิงโดยไม่ซ้ำกันในแต่ละ LandLord โดยปรกติแล้วมักจะเป็น ออบเจ็กต์ของคลาส Integer

            เมื่อมีการร้องขอลีสตัวใหม่ Landlord จะใช้ landlord lease factory เป็นตัวสร้างลีส (LandLord สามารถสร้างเองก็ได้ แล้วแต่วิธีการสร้าง LandLord ว่าจะให้ทำอย่างไร) เมื่อลีสต้องการจะ แคนเซิล หรือ รีนิว ก็จะให้ Landlord เป็นผู้จัดการให้ โดยที่ Client ไม่จำเป็นต้องคุยกับ LandLord โดยตรง แต่กระทำผ่านลีสออบเจ็กต์





รูปที่ 5-2 คลาสและ Interface บางส่วนในแพคเกจ Landlord


 ด้านบนนี้คือแผนภาพคลาสและ Interface ต่าง ๆ ที่อยู่ใน แพคเกจ นี้ โดย Interface จะแสดงโดยตัวอักษรเอียง ส่วนคลาสจะแสดงโดยตัวอักษรธรรมดา โดยรายละเอียดของคลาสหรือ Interface เหล่านี้สามารถดูได้จากคู่มือ API ของ Jini

com.sun.jini.lease.landlord.LeaseResource ( Interface )

            Interface นี้จะถูกนำไปสร้างสำหรับ คลาส ที่ทำการเก็บข้อมูลของลีสต่าง ๆ (เช่น session data ออบเจ็กต์) สร้างขึ้นเพื่อให้สามารถรวบรวมข้อมูลต่าง ๆ ของลีสไว้เป็น ออบเจ็กต์ เดียว ซึ่ง คลาส นี้จะจำเป็นต้องใช้เมื่อมาการใช้ LeaseManager หรือ LeasePolicy

com.sun.jini.lease.landlord.LeasePolicy ( Interface )

            Interface นี้จะถูกนำไปสร้าง สำหรับ คลาส ที่ทำการจัดการกับ LeaseResource ออบเจ็กต์ โดยที่จะเก็บวิธีการรายละเอียดในการ รีนิว หรือแคนเซิล Lease สำหรับ LeaseResource ต่าง ๆ ที่ส่งมาให้จัดการ ซึ่งกระบวนการหลัก ๆ ที่คลาสนี้จัดการนั้นก็คือการแก้ไขเวลาหมดอายุของลีสที่อยู่ภายใน LeaseResource ที่ส่งมาให้มันทำงาน

com.sun.jini.lease.landlord.LeaseManager( Interface )

            Interface นี้จะถูกนำไปสร้าง สำหรับคลาสที่ทำการจัดการกระบวนการหลังจากที่ระบบลีสซิ่ง ได้มีการทำงานต่าง ๆ เช่น มีการสร้างลีสใหม่ หรือลีสที่มีอยู่แล้วถูก รีนิว ซึ่ง LeaseManager นี้จะใช้จัดกระกระบวนการเช่น เก็บรักษา LeaseResource ที่เกิดใหม่ทั้งหมด ทำการทำลาย LeaseResource ที่หมดอายุแล้ว และกระบวนการต่าง ๆ อีกมากมาย แล้วแต่จะสร้างขึ้นอย่างไรก็ได้ โดยจะมีเมธอด  register() และ renew() สำหรับรับการแจ้งเตือนว่ามีลีสที่เกิดใหม่หรือมีลีสที่ถูกรีนิว ซึ่ง LeaseManager นี้อาจจะถูกแจ้งเตือนโดย Landlord ก็ได้

com.sun.jini.lease.landlord.LeaseDurationPolicy (คลาส)

คลาส นี้สร้างขึ้นโดยสร้างมาจาก Interface ของ LeasePolicy ซึ่งออบเจ็กต์นี้จะถูกใช้โดย Landlord ในการสร้างลีสใหม่ หรือทำการ รีนิวลีสโดยที่สามารถส่ง LandlordLeaseFactory และ LeaseManager ให้เป็นพารามิเตอร์สำหรับคอนสตรัคเตอร์ได้เพื่อให้ไว้ใช้สำหรับการสร้างลีสใหม่ และจัดการลีสที่เกิดขึ้น จากแผนภาพ UML ด้านล่างนี้ แสดงตัวอย่างการสร้างระบบลีสซิ่งด้วย แพคเกจ Landlord





รูปที่ 5-3 ตัวอย่างการออกแบบกระบวนการลิสซิ่ง


  • Landlord (FooLandlord) สามารถจัดการกับลีสหลาย ๆ ตัว แต่ว่าสามารถอ้างอิงถึงลีส(LandlordLease) แต่ละตัวได้จาก คุกกี้ (Cookie) ซึ่งลีสแต่ละตัวนั้นจะมีตัวอ้างอิงไปยัง Landlord ของตัวเอง และเรียกใช้ Method ของ Landlord โดยใช้คุ๊กกี้ของตัวเอง
  • การที่ Client จะกระทำใด ๆ กับลีส (เช่น รีนิวหรือแคนเซิล) การร้องขอนั้นจะถูกส่งต่อไปยัง Landlord (FooLandlord) เนื่องมาจากการจะจัดการกับลีสอย่างไรนั้นจะต้องขึ้นอยู่กับ วิธีการและการตัดสินใจของ Landlord โดยที่ Landlord จะส่งไปให้กับ LeaseDurationPolicy เป็นตัวจัดการ
  • LeaseFactory สามารถถูกใช้ได้จากหลาย ๆ LeasePolicy
  • Landlord หนึ่งสามารถมีได้หลาย LeasePolicy และสามารถเลือกได้ว่าจะใช้ Policy ไหน
  • LeaseManager หนึ่งสามารถถูกใช้ได้จาก หลาย ๆ LeasePolicy โดยส่งผ่านเป็นคอนสตรัคเตอร์ให้กับ LeaseDurationPolicy

5.2 ระบบรีโมตอีเวนต์

            แนวคิดของการใช้อีเวนต์นั้นเป็นแนวคิดที่มีมานานแล้ว และใช้อยู่ในภาษาทั่วไป พื้นฐานของแนวความคิดนี้คือ ออบเจ็กต์หนึ่งสามารถที่จะแจ้งให้กับ ออบเจ็กต์อื่น ๆ ทราบถึงการเปลี่ยนแปลงบางอย่างกับตัวมันได้ ซึ่งวิธีการนี้ทำให้ Client ไม่จำเป็นต้องคอยไปตรวจสอบที่ Service อยู่ตลอดเวลาว่ามีเหตุการณ์อะไรเกิดขึ้นหรือไม่ ทำให้ช่วยลดการใช้ ซีพียู ลง ลดปริมาณข้อมูลที่จะต้องใช้ในการสื่อสารลง            แนวความคิดของอีเวนต์เริ่มใช้ครั้งแรกใน ข้อกำหนดของจาวาบีน (JavaBeans Specification) ซึ่งใน ข้อกำหนดนี้อีเวนต์ออบเจ็กต์ ทั้งหมดจะอินเฮอริตมาจากคลาส java.util.EventObject แล้วถูกส่งไปให้กับออบเจ็กต์ที่ทำหน้าที่เฝ้าดู (Listener) ซึ่งถูกสร้างจาก java.util.EventListener ซึ่งใน ข้อกำหนดของจาวาบีน ผู้ที่จะต้องการรับอีเวนต์จะต้องสร้างออบเจ็กต์ที่เป็นตัวดักฟัง (Listener) แล้วส่งออบเจ็กต์นี้ให้กับออบเจ็กต์ที่ส่งอีเวนต์ออกมาซึ่งออบเจ็กต์นั้นจะเรียกใช้ Method ของ ออบเจ็กต์ตัวดักฟัง ที่เก็บรักษาไว้อยู่            Jini ได้ขยายหลักการของอีเวนต์นี้ออกไปโดยที่ ออบเจ็กต์ตัวดักฟังนั้นได้กลายเป็น รีโมตออบเจ็กต์ ซึ่งทำให้อีเวนต์นั้นสามารถถูกส่งผ่านข้าม จาวาเวอร์ชวลมาชีนได้ โดยที่ออบเจ็กต์ที่เป็นตัวดักฟังในโมเดลใหม่นี้นั้นจะต้องสร้างขึ้นตาม net.jini.core.event.RemoteEventListener และตัวอีเวนต์ออบเจ็กต์ นั้นจะต้องอินเฮอร์ริตมาจากคลาส net.jini.core.event.RemoteEvent โดยที่กระบวนการในการส่งอีเวนต์นั้นก็ได้ใช้ Java RMI เป็นหลักโดยที่ RemoteEventListener ที่อยู่ที่ฝั่งผู้ส่งอีเวนต์นั้นจะเป็น สตับของ Java RMI ทำให้สามารถสามารถส่งอีเวนต์กลับไปยังผู้รับอีเวนต์ได้

5.2.1 RemoteEvent

            ในอีเวนต์โมเดลของ AWT นั้น มีการใช้อีเวนต์แบบต่าง ๆ มากมาย แต่ว่าใน จินี นั้นจะใช้อีเวนต์ชนิดเดียวคือ RemoteEvent และมี ซับคลาสเพียงเล็กน้อยเท่านั้น ซึ่ง RemoteEvent มี Method ต่าง ๆ ดังนี้

package net.jini.core.event;

public class RemoteEvent {

    public long getID();

    public long getSequenceNumber();

    public java.rmi.MarshalledObject getRegistrationObject();

}

ซึ่งอีเวนต์ใน AWT จะมีข้อมูลของสถานะของผู้ส่งอีเวนต์ที่ซับซ้อนมาก แต่ใน จินี กลับหลีกเลี่ยงโดยส่งข้อมูลผ่านไปทางอีเวนต์แค่ที่จำเป็นเท่านั้น ทำให้อีเวนต์ออบเจ็กต์ นั้นมีขนาดเล็กและถูกส่งผ่านไปทางเครือข่ายได้ง่ายขึ้น

5.2.2 การลงทะเบียน Event

            ใน จินี นั้นไม่ได้มีการกำหนดเรื่องของวิธีการในการลงทะเบียนอีเวนต์สำหรับ ออบเจ็กต์ ซึ่งแตกต่างจากอีเวนต์โมเดลอื่น ๆ ของจาวาซึ่งออบเจ็กต์ที่เป็นต้นกำเนิดอีเวนต์มักจะเตรียม Method สำหรับออบเจ็กต์ที่ต้องการดักจับอีเวนต์เช่น

public void addActionListener(ActionListener listener);

ซึ่งผู้พัฒนาจินี จำเป็นต้องสร้าง Method ในลักษณะนี้เองสำหรับ Service ที่ต้องการให้ Client สามารถได้รับอีเวนต์ได้โดย Client จะต้องสร้างออบเจ็กต์ที่ทำหน้าที่ดักจับอีเวนต์แล้วส่งไปลงทะเบียนให้กับ Service โดยที่จินีได้มีออบเจ็กต์ที่มาช่วยในการลงทะเบียนซึ่งมีชนิดเป็น EventRegistration โดยออบเจ็กต์นี้จะถูกส่งคืนให้กับออบเจ็กต์ที่ทำการลงทะเบียนอีเวนต์ทำให้ Method ที่ใช้ในการลงทะเบียนอีเวนต์สำหรับจินีจะมีลักษณะดังนี้

public RventRegistration registerForEvent(RemoteEventListener listener);

ซึ่งพารามิเตอร์สำหรับ Method นี้ก็คือ RemoteEventListener ซึ่งทำหน้าที่เป็นตัวดักจับอีเวนต์นั่นเองสำหรับโครงสร้างของ EventRegistration คือ

package net.jini.core.event;

import net.jini.core.lease.Lease;

public class EventRegistration implements java.io.Serializable {

    public EventRegistration(long eventID, Object source, Lease lease

                             ,long seqNum);

    public long getID();

    public Object getSource();

    public Lease getLease();

    public long getSequenceNumber();

}

ในการลงทะเบียนในแต่ละครั้งสำหรับ Jini จะมีอายุเพียงช่วงหนึ่งเท่านั้น เนื่องจากเหตุผลเดียวกับการลงทะเบียน Service ที่ Lookup Service เพราะฉะนั้นจึงมีการทำกระบวนการของลีส มาใช้ในที่นี้ด้วยเช่นกัน โดย ลีสสำหรับกระบวนการลงทะเบียนอีเวนต์นี้จะถูกส่งมาให้ผ่านทางออบเจ็กต์ EventRegistration ซึ่งออบเจ็กต์ที่ทำการลงทะเบียนอีเวนต์สามารถรับลีสมาจากเมธอด getLease() ของออบเจ็กต์ EventRegistration เพื่อนำไปส่งต่อให้กับ LeaseRenewalManager เพื่อดูแลและทำการรีนิวต่อไป

5.2.3 RemoteEventListener

            การสร้างออบเจ็กต์ที่ทำการดักจับรีโมตอีเวนต์จะต้องทำการสร้างโดยจะต้องสร้างตาม Interface ที่ชื่อ RemoteEventListener

public interface RemoteEventListener

                 extends java.rmi.Remote, java.util.EventListener {

    public void notify(RemoteEvent theEvent) throws UnknownEventException,java.rmi.RemoteException;

}

เนื่องจาก Interface นี้อินเฮอริตมาจาก Remote Interface ทำให้ออบเจ็กต์ที่ดักจับรีโมตอีเวนต์ซึ่ง สร้างตาม Interface นี้ทำตัวเป็น รีโมตออบเจ็กต์และจึงต้องสร้าง Java RMI สตับสำหรับรีโมตออบเจ็กต์ ด้วย โดยที่ออบเจ็กต์ที่เป็นต้นกำเนิดของอีเวนต์จะเรียกใช้เมธอด notify() ของ RemoteEventListener เพื่อส่งออบเจ็กต์ RemoteEvent กลับไปยังผู้ที่ต้องการรับอีเวนต์            ในการออกแบบระบบอีเวนต์สำหรับเราสามารถออกแบบให้ ออบเจ็กต์ ที่ให้กำเนิดอีเวนต์นั้นสามารถรองรับผู้ดักฟังเพียงตัวเดียว หรือจะออกแบบให้รองรับผู้ดักฟังหลายตัวก็ได้โดยมีหลักการออกแบบดังนี้

5.2.4 Single Listener

            หากมีออบเจ็กต์ผู้ดักฟังเพียงตัวเดียวเราสามารถใช้ตัวแปรที่มีค่าเดียว (Single-valued variable) มารองรับออบเจ็กต์ผู้ดักฟังอีเวนต์ได้





รูปที่ 5-4 คลาสไดอะแกรมของระบบ Single Listener


โดยตัวอย่าง Method ที่ใช้ทำการลงทะเบียนดักฟังอีเวนต์คือ

//Use landlord for create new lease

protected ServerLandlord landlord = new ServerLandlord()

protected final long defaultDuration = 10*1000  //10 Seconds

protected RemoteEventListener listener = null;

public EventRegistration addRemoteListener(RemoteEventListener listener)

                           throws java.util.TooManyListenersException {

    if (this.listener == null){

this.listener = listener;

    } else { throw new java.util.TooManyListenersException(); }

    //Create new Lease for EventRefgsitration

    Lease l = landlord.newLease(defaultDuration);

    return new EventRegistration(0L, this, l, 0L);

}

ซึ่งลีสที่ส่งให้กับ Client Landlord จะเป็นผู้สร้าง แต่ว่าในตัวอย่างนี้ยังไม่ได้มีการจัดการกับลีสออบเจ็กต์ที่หมดอายุ และถ้าหากมีอีเวนต์เกิดขึ้น Service สามารถส่งอีเวนต์ให้กับ Client ได้โดยส่งอีเวนต์ให้กับตัวดักฟังอีเวนต์ผ่านทาง เมธอด notify() โดยมีตัวอย่างดังต่อไปนี้

public void eventNotify(long eventID, long seqNum){

    if(listener == null){

       return;

    }

    //Create Remote event and send event to listener object

    RemoteEvent remoteEvent = new RemoteEvent(this, eventID, seqNum, null);

    Listener.notify(remoteEvent);

}

5.2.5 Multiple Listener

            ในกรณีที่จะต้องรองรับออบเจ็กต์ที่ทำการดักฟังอีเวนต์หลายตัวนั้น ในการออกแบบอาจจะใช้ออบเจ็กต์ประเภท คอนเทนเนอร์มาช่วยในการเก็บรักษาออบเจ็กต์ตัวดักฟังอีเวนต์เช่น Vector, Hashtable หรืออาจจะใช้ javax.swing.event.EventListenerList มาช่วยก็สามารถทำได้





รูปที่ 5-5 คลาสไดอะแกรมของระบบ Multiple Listener


โดยตัวอย่าง Method ที่ใช้ทำการลงทะเบียนดักฟังอีเวนต์ซึ่งยังไม่ได้จัดการกับลีสที่หมดอายุแล้วคือ

//Use landlord for create new lease

protected ServerLandlord landlord = new ServerLandlord()

protected final long defaultDuration = 10*1000  //10 Seconds

protected Vector listeners = new Vector();

public EventRegistration addRemoteListener(RemoteEventListener listener)

                           throws java.util.TooManyListenersException {

    //Add listener to Vector

    listeners.addElement(listener);

    //Create new Lease for EventRefgsitration

    Lease l = landlord.newLease(defaultDuration);

    return new EventRegistration(0L, this, l, 0L);

}

และตัวอย่าง Method ที่ทำการส่งอีเวนต์ให้กับตัวดักฟังคือ

public void eventNotify(long eventID, long seqNum){

    if(listeners.isEmpty()){

       return;

    }

    //Create Remote event object

    RemoteEvent remoteEvent = new RemoteEvent(this, eventID, seqNum, null);

    //Send RemoteEvent to all listeners

    RemoteEventListener listener = null;

    For(Enumeration e = listeners.elements();e.hasMoreElements();){

listener = (RemoteEventListener)e.nextElement();

Listener.notify(remoteEvent);

    }

}

5.3 รีโมตอีเวนต์สำหรับ Lookup Service

            ในกรณีที่ Client กำลังทำงานอยู่ Client สามารถได้รับข้อมูลต่าง ๆ จาก Lookup Service ได้เช่น มี Service ที่ต้องการมาลงทะเบียนใหม่ Service ที่กำลังใช้อยู่ได้ออกจากระบบจินีไปแล้ว หรือ Service ที่มีอยู่มีการเปลี่ยนแปลง Attribute

            ซึ่งกระบวนการที่กล่าวมานี้นั้น Lookup Service จะใช้หลักการของ รีโมตอีเวนต์ เพื่อส่งอีเวนต์ไปบอกยัง Client ของ Lookup Service กระบวนการที่จะทำให้ Client สามารถรับอีเวนต์จาก Lookup Service ได้ก็เหมือนกับกระบวนการของ รีโมตอีเวนต์ดังที่กล่าวมาแล้วข้างต้น ก็คือ

  • สร้างออบเจ็กต์สำหรับดักฟังอีเวนต์
  • ลงทะเบียนออบเจ็กต์นั้นกับ ต้นกำเนิดของอีเวนต์ซึ่งกรณีนี้ก็คือ Lookup Service นั่นเอง

ตัวอย่างของคลาสสำหรับออบเจ็กต์ตัวดักฟังคือ

import java.rmi.*;

import java.rmi.server.*;

import net.jini.core.lookup.*;

import net.jini.core.event.*;

public class LookupServiceListener extends UnicastRemoteObject

                                     implements RemoteEventListener {

    public void notify(RemoteEvent event) throws UnknownEventException

                                                   RemoteException{

       if(!(event instanceof ServiceEvent)){

           Throw new UnknownEventException(“LookupServiceListener”);

}

ServiceEvent sevent = (ServiceEvent)event;

ServiceItem item = sevent.getServiceItem();

if(sevent.getTransition()==ServiceRegistrar.TRANSITION_NOMATH_MATCH){

    System.out.println(“Found new Service”);

}

    }

}

สำหรับอีเวนต์ที่ได้รับมานั้น Client สามารถรู้ถึงเหตุการณ์ที่ Lookup Service ส่งมาได้ว่าคืออะไรโดยดึงข้อมูลมาจากเมธอด getTransition() ของออบเจ็กต์ ServiceEvent ซึ่งข้อมูลที่ได้เป็นข้อมูลที่แสดงเหตุการณ์สำหรับ Lookup Service มีข้อมูลต่าง ๆ ดังนี้

TRANSITION_NOMATH_MATH

            หมายความว่ามี Service ซึ่งเหมือนกับ Template ที่ต้องการมาลงทะเบียนใหม่

TRANSITION_MATH_NOMATH

หมายความว่า Service ที่เคยค้นหาพบได้ถูกนำออกไปจากระบบแล้ว หรือมีการเปลี่ยน Attribute ของ Service นั้นจนทำให้ไม่เหมือนกับ Template ที่ต้องการอีกต่อไป

TRANSITION_MATH_MATH

            หมายความว่า Service ได้มีการเปลี่ยนแปลง Attribute แต่ว่ายังคงเข้ากันได้กับ Template ที่ต้องการขั้นตอนในการลงทะเบียนออบเจ็กต์ผู้ดักฟัง จะกระทำผ่านเมธอด notify() ของ ServiceRegistrar ออบเจ็กต์ที่ได้มาจาก Lookup Service โดยที่ Method นี้ต้องการพารามิเตอร์ 5 ตัวสำหรับเป็นข้อมูลของ Service ที่ต้องการค้นหา คือ
  • Template Object สำหรับ Service ที่ต้องการค้นหา
  • ลักษณะการเปลี่ยนแปลงที่ต้องการจะรู้
  • RemoteEventListener
  • มาแชลด์ออบเจ็กต์ (Marshaled Object)
  • ระยะเวลาที่ต้องการจะรับอีเวนต์

ซึ่งหลังจากเรียกใช้ Method นี้แล้ว ก็จะคืนค่าออกมาเป็นออบเจ็กต์ของคลาส EventRegistration ซึ่งจะนำมาใช้เช่นเดียวกับกระบวนการของ RemoteEvent ทั่วไปคือนำลีสที่ได้จาก EventRegistration ออบเจ็กต์ ไปส่งให้กับ LeaseRenewalManager เพื่อดูแลต่อไป






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