ทำความเข้าใจแนวคิด OSGi ลองทำตามแนวทางปริศนา
เผยแพร่แล้ว: 2013-04-20OSGi
ได้รับความนิยมอย่างมากในปัจจุบัน ต้องขอบคุณวิธีการแบบแยกส่วนและความสามารถในการบังคับใช้ขอบเขตเชิงตรรกะระหว่างโมดูล เมื่อเราค้นพบมันในครั้งแรก คำถามคือ จะเริ่มเข้าใจว่ามันทำงานอย่างไร?
เพื่อให้เข้าใจแนวคิดของ OSGi เราจะพยายามทำตามแนวทางปริศนา แนวคิดคือการเริ่มต้นด้วยส่วนเล็กๆ น้อยๆ ของเทคโนโลยีนี้ และค้นหาส่วนอื่นๆ ที่เกี่ยวข้องกับส่วนที่ค้นพบ และในการประกอบปริศนา เราจะได้รับความช่วยเหลือจาก JArchitect ซึ่งจะเป็นประโยชน์ในการตรวจจับการออกแบบภายในของ OSGi
เพื่อให้เป็นรูปธรรมมากขึ้น เราวิเคราะห์กับ JArchitect แอปพลิเคชันที่ใช้เทคโนโลยี OSGi เกี่ยวข้องกับ eclipse IDE ที่มีชื่อเสียงซึ่งใช้ OSGi คอนเทนเนอร์ Equinox
เริ่มต้นด้วยคำจำกัดความ OSGi ทั่วไป:
OSGi ช่วยลดความซับซ้อนด้วยการจัดหาสถาปัตยกรรมโมดูลาร์สำหรับระบบแบบกระจายขนาดใหญ่ในปัจจุบัน เช่นเดียวกับแอปพลิเคชันขนาดเล็กที่ฝังตัว ระบบการสร้างจากโมดูลภายในและนอกชั้นวางช่วยลดความซับซ้อนได้อย่างมาก ส่งผลให้ค่าใช้จ่ายในการพัฒนาและบำรุงรักษา โมเดลการเขียนโปรแกรม OSGi ตระหนักถึงคำมั่นสัญญาของระบบที่ใช้ส่วนประกอบ
ส่วนเล็กๆ น้อยๆ ของ OSGi คือโมดูลาร์ เรามาดูกันว่าโมดูล OSGi คืออะไร?
โมดูล OSGI เรียกว่า Bundles และทุกแอปพลิเคชันประกอบด้วยชุดข้อมูลอย่างน้อยหนึ่งชุด
บันเดิลเหล่านี้ถูกดำเนินการภายในคอนเทนเนอร์ และเนื่องจากแต่ละวิธีโมดูลาร์ที่คอนเทนเนอร์จัดการโมดูล คำถามคือสัญญาใดที่ต้องใช้ทุกโมดูลเพื่อรวมเข้ากับคอนเทนเนอร์
ลองมาดูตัวอย่างบันเดิล org.eclipse.equinox.jsp.jasper และค้นหาอินเทอร์เฟซที่นำไปใช้ทั้งหมด เพื่อที่เราจะได้ดำเนินการตามคำขอ CQLinq ต่อไปนี้:
1 2 3 4 |
from t in Types where t . ParentProject . Name ==” org . eclipse . equinox . jsp . jasper_1 . 0.300.v20110502 “ let interfaces = t . InterfacesImplemented from i in interfaces select i |
มีการใช้งานอินเทอร์เฟซ BundleActivator จากแพ็คเกจ OSGi อินเทอร์เฟซนี้ประกอบด้วยสองวิธีในการเริ่มและหยุด ซึ่งมีประโยชน์ในการปรับแต่งการเริ่มต้นและการหยุดบันเดิล
ความเฉพาะเจาะจงอีกประการหนึ่งของบันเดิลคือไฟล์รายการ ซึ่งเป็นส่วนหนึ่งจากไฟล์รายการ org.eclipse.equinox.jsp.jasper:
1 2 3 4 5 6 7 8 9 |
Manifest - Version : 1.0 Bundle - Localization : plugin Bundle - RequiredExecutionEnvironment : CDC - 1.0 / Foundation - 1.0 , J2SE - 1.3 Bundle - SymbolicName : org . eclipse . equinox . jsp . jasper Eclipse - LazyStart : true Eclipse - SourceReferences : scm : cvs : pserver : dev . eclipse . org : / cvsroot / rt : org . eclipse . equinox / server - side / bundles / org . eclipse . equinox . jsp . jaspe r ; tag = v20110502 Bundle - Activator : org . eclipse . equinox . internal . jsp . jasper . Activator |
เนื่องจากเราสามารถสังเกตได้ว่าไฟล์ Manifest นี้มีข้อมูลเมตาบางอย่างที่จำเป็นสำหรับคอนเทนเนอร์ เช่น การระบุคลาสบันเดิล activator ซึ่งใช้งานอินเทอร์เฟซ BundleActivator
ชุดรวมเป็นตัวแทนของปริศนาชิ้นแรกของเรา และนี่คือการแสดงชุดรวมที่ง่ายขึ้น:
และเพื่อให้มีแนวคิดเกี่ยวกับบันเดิลทั้งหมดที่ใช้โดย eclipse ให้ค้นหาคลาสทั้งหมดที่ใช้อินเทอร์เฟซ BundleActivator
1 2 |
from t in Types where t . Implement ( “ org . osgi . framework . BundleActivator “ ) select new { t , t . NbBCInstructions } |
ใครเป็นคนจัดการบันเดิลและเรียกใช้เมธอด BundleActivator
เพื่อค้นพบว่าให้ค้นหาวิธีการเรียกโดยตรงหรือโดยอ้อม BundleActivator.start
1 2 3 4 |
from m in Methods let depth0 = m . DepthOfIsUsing ( “ org . eclipse . osgi . framework . internal . core . BundleContextImpl . startActivator ( BundleActivator ) “ ) where depth0 > = 0 orderby depth0 select new { m , depth0 } |
บันเดิลถูกเปิดใช้งานโดยเฟรมเวิร์ก OSGi เมื่อเปิดตัว คลาสเฟรมเวิร์กเริ่มต้นโดยคอนเทนเนอร์ Equinox และเพื่อให้เข้าใจดีขึ้นว่าจะเกิดอะไรขึ้นเมื่อคอนเทนเนอร์เริ่มทำงาน ต่อไปนี้คือการดำเนินการบางอย่างที่ดำเนินการเมื่อเปิดคอนเทนเนอร์:
เมื่อคอนเทนเนอร์เปิดตัว มันจะเริ่มต้นเฟรมเวิร์ก OSGi เฟรมเวิร์กได้รับบันเดิลที่ติดตั้งทั้งหมด และสำหรับแต่ละอันจะสร้างอินสแตนซ์ของคลาส BundleHost และจัดเก็บไว้ในที่เก็บบันเดิลที่พบ
คลาส BundleHost ใช้อินเทอร์เฟซของ Bundle ซึ่งมีวิธีการต่างๆ เช่น เริ่ม หยุด ถอนการติดตั้ง และอัปเดต วิธีการเหล่านี้จำเป็นสำหรับการจัดการวงจรชีวิตของบันเดิล
ดังนั้นชิ้นส่วนปริศนาที่สองของเราคือคอนเทนเนอร์ OSGi ซึ่งเปิดตัวโดย Equinoxlauncher ซึ่งเริ่มต้นเฟรมเวิร์ก คลาสเฟรมเวิร์กมีหน้าที่โหลดบันเดิลและเปิดใช้งาน
หลังจากค้นพบแนวคิดพื้นฐานเกี่ยวกับบันเดิลและคอนเทนเนอร์แล้ว มาดูรายละเอียดเกี่ยวกับบันเดิลและค้นพบวิธีการทำงานภายในกัน
ลองมาดูตัวอย่างบันเดิล org.eclipse.equinox.http.servlet และค้นหาเมธอดที่เรียกใช้โดยเมธอด start ของคลาส Activator
บันเดิลนี้สร้างบริการและลงทะเบียนลงในคอนเทนเนอร์ บริการใน OSGi ถูกกำหนดโดยคลาสหรืออินเตอร์เฟส Java มาตรฐาน โดยทั่วไปแล้วอินเตอร์เฟส Java จะใช้เพื่อกำหนดส่วนต่อประสานบริการ บริการนี้เป็นวิธีที่ต้องการที่บันเดิลควรใช้เพื่อสื่อสารระหว่างกัน

ต่อไปนี้คือสถานการณ์ที่เป็นประโยชน์บางประการในการใช้บริการ:
- ฟังก์ชันการส่งออกจากบันเดิลไปยังบันเดิลอื่น
- นำเข้าฟังก์ชันจากบันเดิลอื่นๆ
- ลงทะเบียน Listener สำหรับเหตุการณ์จากบันเดิลอื่น
ข้อสังเกตอีกประการหนึ่งจากกราฟการพึ่งพาครั้งก่อนคือโรงงานบริการถูกใช้เพื่อสร้างอินสแตนซ์บริการ
จิ๊กซอว์ชิ้นที่สามของเราคือเลเยอร์บริการ OSGi แต่ละบันเดิลสามารถใช้หรือประกาศบริการบางอย่าง สิ่งที่บังคับใช้แนวทางการออกแบบส่วนประกอบ นี่คือการนำเสนอใหม่ของบันเดิล OSGi
หาก The Bundle ใช้บริการเพื่อสื่อสารกับ Bundle อื่น ๆ จะสื่อสารกับ Jar อื่น ๆ ได้อย่างไร?
หากเราพัฒนาบันเดิลและพยายามใช้คลาสจากโถอื่น เราอาจแปลกใจว่ามันจะไม่ทำงานตามที่คาดไว้ เหตุผลก็คือ ClassLoader ถูกเกี่ยวโดยคอนเทนเนอร์ OSGi เพื่อตรวจสอบว่าเรามาค้นหาว่าเมธอดใดเรียกใช้จาวา lang.Thread.setContextClassLoader
1 2 |
from m in Methods where m . IsUsing ( “ java . lang . Thread . setContextClassLoader ( ClassLoader ) “ ) select new { m , m . NbBCInstructions } |
หลายวิธีเรียกมันรวมถึง EquinoxLauncher ดังนั้นทุกครั้งที่บันเดิลพยายามสร้างคลาสอินสแตนซ์ คอนเทนเนอร์ OSGi จะตรวจสอบว่าโค้ดได้รับอนุญาตให้ดำเนินการนี้หรือไม่ และบทบาทของแพ็กเกจที่นำเข้าและส่งออกในไฟล์รายการก็มา
1 2 3 4 |
Export - Package : org . eclipse . equinox . http . servlet ; version =” 1.1.0 ″ Import - Package : javax . servlet ; version =” 2.3 ″ , javax . servlet . http ; version =” 2.3 ″ , org . osgi . framework ; version =” 1.3.0 ″ , org . osgi . service . http ; versi on =” [ 1.2 , 1.3 ) ” |
บันเดิลประกาศแพ็คเกจการส่งออกและนำเข้าอย่างชัดเจน และเพื่อตรวจสอบว่า ให้ค้นหาแพ็คเกจที่ใช้โดยบันเดิล org.eclipse.equinox.http.servlet และตรวจสอบว่าใช้เฉพาะแพ็กเกจที่นำเข้าหรือไม่
1 2 |
from n in Packages where n . IsUsedBy ( “ org . eclipse . equinox . http . servlet_1 . 1.200.v20110502 “ ) select new { n , n . NbBCInstructions } |
เนื่องจากเราสามารถสังเกตได้ว่าแพ็คเกจทั้งหมดที่ใช้นั้นระบุไว้ในไฟล์รายการ ในส่วนแพ็คเกจการนำเข้า
อย่างไรก็ตาม แพ็คเกจที่ส่งออกแสดงถึงแพ็คเกจที่สามารถใช้จากบันเดิลอื่นๆ มันแสดงโดยอินเทอร์เฟซ ExportedPackage และเราสามารถค้นหาคลาสคอนเทนเนอร์โดยใช้อินเทอร์เฟซนี้
1 2 |
from t in Types where t . IsUsing ( “ org . osgi . service . packageadmin . ExportedPackage “ ) select new { t , t . NbBCInstructions } |
สิ่งที่น่าสนใจของความสามารถใหม่นี้คือบันเดิลจะมีขอบเขตที่ชัดเจน และสิ่งที่มันใช้และสิ่งที่เปิดเผยในฐานะบริการนั้นได้รับการระบุไว้เป็นอย่างดี
เราสามารถบังคับใช้การตรวจสอบโดยใช้แพ็คเกจที่นำเข้าโดยใช้เครื่องมืออื่น ๆ เช่นด้วย CQLinq เราสามารถเขียนกฎบางอย่างเตือนทุกครั้งที่โครงการใช้แพ็คเกจอื่นนอกเหนือจากที่ระบุ แต่ควรตรวจสอบในสภาพแวดล้อมการดำเนินการดังนั้นผู้พัฒนา ไม่สามารถแหกกฎเหล่านี้ได้
ความสามารถในการจัดการแพ็คเกจที่นำเข้าและส่งออกนี้ได้รับการจัดการโดยเลเยอร์โมดูล OSGi และเป็นปริศนาชิ้นที่สี่ของเรา
กลับมาที่คอนเทนเนอร์ OSGi และค้นพบว่ามีบริการใดบ้าง
บริการตู้คอนเทนเนอร์
ตามที่เราค้นพบก่อนเปิดคอนเทนเนอร์โดยคลาส EquinoxLauncher และคลาสเฟรมเวิร์กเริ่มต้นและเปิดใช้บันเดิล เพื่อตรวจสอบว่ามีบริการใดบ้าง ให้ค้นหาคลาสทั้งหมดที่ใช้ในวิธีการเริ่มต้นเฟรมเวิร์ก
1 2 |
from t in Types where t . IsUsedBy ( “ org . eclipse . osgi . framework . internal . core . Framework . initialize ( FrameworkAdaptor ) “ ) select new { t , t . NbBCInstructions } |
มีการค้นพบคลาสบางคลาสก่อนหน้านี้ เช่น BundleRepository,BundleHost,PackageAdminImpl และ ServiceRegistry
แล้วคลาสอื่นๆ ล่ะ:
- StartLevelManager:
แต่ละบันเดิล OSGi เชื่อมโยงกับระดับเริ่มต้นที่ทำให้เซิร์ฟเวอร์สามารถควบคุมลำดับการเริ่มต้นและการหยุดที่สัมพันธ์กันของบันเดิล เฉพาะบันเดิลที่มีระดับเริ่มต้นน้อยกว่าหรือเท่ากับระดับเริ่มต้นที่แอ็คทีฟของเฟรมเวิร์กเซิร์ฟเวอร์เท่านั้นที่ต้องแอ็คทีฟ โดยปกติ กลุ่มที่มีระดับเริ่มต้นน้อยกว่ามักจะเริ่มต้นเร็วกว่านี้ - ความปลอดภัยผู้ดูแลระบบ:
เลเยอร์ความปลอดภัยจัดการด้านความปลอดภัยโดยจำกัดการทำงานของบันเดิลให้เหลือความสามารถที่กำหนดไว้ล่วงหน้า - ผู้จัดการงานอีเว้นท์:
บริการ Event Admin จัดเตรียมรูปแบบการเผยแพร่และสมัครสมาชิกสำหรับการจัดการเหตุการณ์ เป็นไปตามข้อกำหนดบริการ OSGi Event Admin บริการ Event Admin จะจัดส่งเหตุการณ์ระหว่าง Event Publishers และ Event Subscribers (Event Handlers) โดยการสอดแทรกช่องทางเหตุการณ์ ผู้จัดพิมพ์โพสต์กิจกรรมลงในช่องและช่องทางกิจกรรมจะกำหนดตัวจัดการที่ต้องได้รับแจ้ง ดังนั้นผู้จัดพิมพ์และผู้ดูแลจึงไม่มีความรู้โดยตรงซึ่งกันและกัน ซึ่งทำให้การจัดการเหตุการณ์ง่ายขึ้น
ภาพรวมของ OSGi
มาประกอบชิ้นส่วนจิ๊กซอว์ทั้งหมดที่อธิบายไว้ก่อนหน้านี้ และเราจะมีรูปภาพ OSGi ต่อไปนี้:
สถาปัตยกรรมนี้มีประโยชน์ที่น่าสนใจดังต่อไปนี้:
- เรียบง่าย.
- ลดความซับซ้อน
- ปรับใช้ง่าย
- ปลอดภัย.
อะไรทำให้น่าสนใจและคุ้มค่าแก่การอ้อม และคุณจะไม่เสียเวลาหากศึกษาเชิงลึก