- Cơ cấu tương tác (interaction schemes) – phương tiện, cách thức mà các thành phần tương tác với nhau và với framework.
- Ràng buộc tài nguyên (resource binding) – ràng buộc thành phần tới một hoặc nhiều tài nguyên.
Công nghệ dựa thành phần của OSGi có thể được xem như là kết quả tất yếu của việc thiếu tính module trong Java và tính phức tạp trong các ứng dụng Java động ngày càng tăng.
2.2.2. Mô hình thành phần trong OSGi Loại thành phần Loại thành phần
Các Bundle là các thành phần trong mô hình thành phần của OSGi ở khía cạnh triển khai. Còn ở khía cạnh xây dựng hệ thống thì người ta nhắc đến các dịch vụ OSGi như là các thành phần.
Bundle không phải là một đặc tính của ngôn ngữ Java, nhưng đơn giản nó là Java archive (file JAR) cộng thêm meta-data trong một file riêng biệt gọi là MANIFEST.MF.
Filterf = ctx.createFilter("(log.type=text)"); ServiceReference[] refs;
refs=ctx.getServiceReferences(Logger.class.getName(), f.toString());
Một mô hình thành phần yêu cầu các thành phần cài đặt một hoặc nhiều giao diện, và theo cách này thì mô hình thành phần có thể định nghĩa một hoặc nhiều loại thành phần.
OSGi Bundle có thể không cần cài đặt bất cứ giao diện nào. Về mặt kỹ thuật thì bất kỳ file JAR với một file MANIFEST.MF có thuộc tính nhận dạng Bundle- symbolicName được coi là một Bundle.
Cơ chế tƣơng tác (interaction scheme)
Cơ cấu tương tác của một mô hình thành phần mô tả thành phần tương tác với nhau như thế nào, hay chúng tương tác với framework thành phần như thế nào.
Tƣơng tác Bundle – Bundle, có hai cơ chế để một bundle tương tác với bundle khác:
cơ chế import – export và dịch vụ. - Cơ chế Import – Export
Xuyên suốt thời gian phát triển người phát triển bundle truy cập các thành phần trong các bundle bằng cách import các package của chúng. Sau đó các lớp và các giao diện trong package đó trở thành hiện diện (visible) với import bundle.
Import này khác với từ khóa import sử dụng trong Java, từ khóa import trong Java chỉ để quản lý không gian tên. Trong Java ta có thể không cần sử dụng từ khóa import mà có thể sử dụng tên đầy đủ của class hay package. Với OSGi ta bắt buộc phải sử dụng import package để có thể nhìn thấy thành phần từ bundle của ta. Import xác định trong định dạng một danh sách các tên package được phân tách bởi dấu phẩy trong file manifest.
Ở thời điểm triển khai, khi bundle được cài đặt, framework sẽ phân giảncác phần phụ thuộc dựa vào meta-data được cung cấp để khớp các gói import và export.
Nếu quá trình phân giải thành công, mỗi bundle sẽ nhận được Classloader của riêng nó và import bundle được nối với export bundle.
Trong thời gian chạy, các yêu cầu sử dụng các lớp trong các package đã được export được ủy quyền cho việc export Classloader tự động bởi framework.
- Dịch vụ
Dạng tương tác thứ hai giữa các bundle là thông qua dịch vụ. Một dịch vụ là một đối tượng Java thông thường, nó được đăng ký dưới một hoặc nhiều giao diện Java với Service Registry.
Một bundle xuất bản một dịch vụ bằng việc đăng ký một đối tượng và giao diện của nó với Service Registry của framework. Các bundle khác tìm kiếm dịch vụ thông
qua tên giao diện. Điều này hướng tới một mô hình hướng dịch vụ (service – oriented model): public – find – bind.
Dịch vụ có thể xuất hiện hoặc biến mất. Người phát triển bundle phải xử lý các trường hợp này. Ở các bản đặc tả cũ (trước 4.2) quản lý các dịch vụ phải làm theo cách thủ công. Trong các phiên bản mới sau này (sau 4.2) lập trình viên có thể sử dụng Declarative Services (DS) hoặc Blueprint. Với DS một bundle định nghĩa các thành phần dịch vụ, những thành phần này bao gồm một lớp Java và một file XML mô tả các dịch vụ được cung cấp và yêu cầu và được quản lý bởi Service Component Runtime (SCR) [7].
Tƣơng tác giữa Bundle-Framework “Quan hệ giữ Framwork và các bundle đã được
cài đặt trong Framework được thể hiện thông qua việc sử dụng các đối tượng BundleContext. Một đối tượng BundleContext biểu diễn ngữ cảnh thực thi của một bundle duy nhất trong OSGi Service Platform và nó hoạt động như một cầu nối với Framework bên dưới” [7].
Đối tượng ngữ cảnh (context) cung cấp các phương thức cho việc triển khai và quản lý vòng đời. Sử dụng đối tượng này một bundle có thể cài đặt, gỡ bỏ, hoặc khởi động các bundle khác hoặc lắng nghe các sự kiện của framework. Đối tượng này cũng có thể được sử dụng để lấy ra các thuộc tính cấu hình của bundle và một vùng lưu trữ riêng. Theo mô tả trong đặc tả của OSGi “Framework nên cung cấp một vùng lưu trữ riêng thường trực cho mỗi bundle đã được cài đặt trên các nền tảng với một số định dạng file hệ thống hỗ trợ” [7]. Vùng này được quản lý bởi framework, ví dụ, nó được sử dụng để lưu trữ trạng thái của một bundle.
Ràng buộc tài nguyên
Quá trình thiết lập (composing) các thành phần là việc ràng buộc một thành phần vào một hoặc nhiều tài nguyên. Một framework coi các thành phần như các tài nguyên cần được quản lý. Theo đó, triển khai là quá trình các framework được ràng buộc tới các thành phần, và một mô hình thành phần sẽ mô tả việc các thành phần được triển khai như thế nào.
Khía cạnh ràng buộc tài nguyên có thể được xem như các quy tắc thiết lập – các thành phần và các thuộc tính của nó có thể được thiết lập và cài đặt như thế nào.
-Kết nối các bundle (wiring bundles)
Framework “nối” các bundle với nhau thông qua quá trình phân giải. “Một kết nối giữa hai bundle thực chất là một kết nối giữa một exporter và một importer bundle. Một kết nối được đi kèm với một số ràng buộc được định nghĩa bởi các manifest
header của importer và exporter của nó. Một kết nối đúng đắn là một kết nối mà thỏa mãn tất cả các ràng buộc của nó” [7].
Người phát triển bundle có thể ràng buộc quá trình này với các thuộc tính và chỉ dẫn trong meta-data.
Nếu chỉ một bundle cung cấp một package được yêu cầu, thì hai bundle được nối với nhau. Nếu có nhiều bundle cung cấp cùng một package thì đặc tả OSGi định nghĩa quy tắc sau theo thứ tự ưu tiên giảm dần:
+ Một exporter đã phân giải được ưu tiên hơn một exporter chưa phân giải.
+ Một exporter có phiên bản cao hơn được ưu tiên hơn exporter có phiên bản thấp hơn. + Một exporter có ID nhỏ hơn được ưu tiên hơn exporter có ID lớn hơn.
Những quy tắc này dẫn đến trường hợp mà các ràng buộc được xác định là không đồng nhất với nhau, nhưng framework vẫn phân giải các bundle. Trường hợp cần thiết người phát triển bundle phải xác định thêm các ràng buộc thông qua các chỉ dẫn (Uses-directive) để ngăn ngừa việc thiếu đồng nhất trong không gian lớp.
- Ràng buộc các dịch vụ
Người phát triển bundle có thể sử dụng một “lookup” hoặc một chiến lược sự kiện để ràng buộc các dịch vụ.
Trong trường hợp của Declarative Services, các thành phần dịch vụ được định nghĩa trong mô tả XML. Trong thời gian chạy Service Component Runtime quản lý các thành phần này.
Một thành phần dịch vụ không thể được active cho đến khi các bundle sử dụng nó được khởi động. Vòng đời của các thành phần dịch vụ được bao gồm bên trong vòng đời của bundle của nó. Các phần phụ thuộc tới dịch vụ được xác định thông qua các tham chiếu. Khi ràng buộc các dịch vụ, các tham chiếu được xử lý theo trình tự chúng được xác định trong mô tả thành phần.
Khi sử dụng chiến lược sự kiện với Declarative Services, các phương thức có thể được khai báo và chúng được tự động gọi khi một dịch vụ là đã được ràng buộc hoặc gỡ bỏ. Với cách này có thể tham chiếu tới các dịch vụ khi chúng xuất hiện, và thực hiện các hành động tương ứng trong trường hợp ngược lại.
Với chiến lược tìm kiếm (lookup-strategy), các lớp cài đặt có thể định nghĩa một phương thức activate với một tham số trong định dạng ComponentContext. Đối tượng này có thể được sử dụng để định vị dịch vụ thông qua tên.
Nhìn chung framework không thể đảm bảo rằng các đối tượng từ một thành phần đã dừng sẽ không được tham chiếu bởi mã nguồn đang hoạt động. Vấn đề này được nhắc đến như “stale references”.
Chƣơng 3. ÁP DỤNG XÂY DỰNG ỨNG DỤNG BÁN HÀNG TRỰC TUYẾN
Các ứng dụng doanh nghiệp luôn đóng vai trò quan trọng trong nền công nghiệp phần mềm. Enterprise Java đã rất thành công trong việc xây dựng các ứng dụng doanh nghiệp, nhưng ngày nay các ứng dụng doanh nghiệp ngày càng trở lên lớn, cồng kềnh và phức tạp với nhu cầu mở rộng và thay đổi thường xuyên. Java chuẩn cũng đã để lộ ra một số vấn đề trong việc quản lý thư viện cũng như vấn đề Class Path. Vì vậy để việc phát triển cũng như bảo trì trở nên dễ dàng, hiệu quả cần có một kiến trúc tốt để khắc phục những vấn đề này. Kiến trúc dựa thành phần là một trong những kiến trúc thích hợp. Như chúng ta đã biết về các ưu điểm của một hệ thống được xây dựng trên kiến trúc dựa thành phần. Kiến trúc dựa thành phần tuy không mới, nhưng cho đến nay trong Java hầu như vẫn chưa hề có giải pháp nào áp dụng hiệu quả và triệt để nó vào thực tiễn. OSGi là một giải pháp rất thích hợp cho việc áp dụng xây dựng ứng dụng với kiến trúc dựa thành phần trong Java. Ngoài những đặc tính của một mô hình dựa thành phần thì OSGi còn bổ sung để khắc phục một số vấn đề còn tồn tại trong Java chuẩn. Giúp tránh được những lỗi khi ứng dụng đang chạy, cung cấp các dịch vụ động, triển khai động từng thành phần, một phần hoặc toàn bộ các thành phần mà không phải khởi động lại hệ thống.
Trong phần nay ta sẽ sử dụng OSGi để xây dựng một ứng dụng bán hàng trực tuyến gọi là Chip Shop. Ứng dụng bao gồm đầy đủ các thành phần chính của một ứng dụng doanh nghiệp. Một Web frontend cho phép người dùng có thể hiển thị các danh mục sản phẩm, tìm kiếm, lựa chọn sản phẩm vào giỏ hàng, tạo tài khoản, đặt hàng. Ngoài ra thì ứng dụng cũng cung cấp một backend cho phép người quản trị có thể thêm hay chỉnh sửa các danh mục sản phẩm, xem và cập nhật trạng thái các đơn hàng. Sơ đồi hình 3.1 minh họa các dối tượng người dùng và chức năng của hệ thống.
Sơ đồ tuần tự hình 3.2 miêu tả một trong những kịch bản điển hình nhất của ứng dụng là các bước tương tác và xử lý khi khách hàng mới (chưa có tài khoản) muốn mua một sản phẩm.
Hình 3.2: Các bước xử lý khi khách hàng đặt hàng
Đối tượng Customer là khách hàng; Web UI là giao diện Web của ứng dụng; REST Orders là thành phần cung cấp dịch vụ Web tiếp nhận các yêu cầu đặt hàng từ Web UI, nó cũng chính là một dịch vụ OSGi; tương tự REST Customers là các dịch vụ Web xử lý các yêu cầu liên quan đến khách hàng; Order Service là thành phần cung cấp dịch vụ OSGi để xử lý các đơn hàng như lưu đơn hàng xuống cơ sở dữ liệu MongoDB; Customer Service là thành phần cung cấp dịch vụ OSGi để xử lý các yêu cầu liên quan
đến thông tin khách hàng, nó cũng giao tiếp với MongoDB thông qua dịch vụ Mongo của một thành phần được cung cấp bởi dự án Amdatu.
Ứng dụng được chia thành các thành phần, mỗi thành phần là một OSGi bundle với ý nghĩa triển khai và là một dịch vụ với ý nghĩa về chức năng hệ thống. Các thành phần giao tiếp với nhau thông qua các dịch vụ, đại diện cho mỗi dịch vụ là một giao diện. Một bundle về mặt lý thuyết và kỹ thuật có thể cung cấp nhiều dịch vụ nhưng ta không nên làm như vậy. Mỗi bundle chỉ nên cung cấp một dịch vụ và nếu cần cung cấp thêm các chức năng ít liên quan tới nhau ta nên xem xét tách thành thành phần và dịch vụ mới, ta sẽ phân tích kỹ hơn về vấn đề này ở phần dưới đây. Mỗi thành phần bao gồm một lượng nhỏ mã nguồn giúp dễ hiểu và có thể được phát triển, cập nhật, nâng cấp và sau đó triển khai một cách độc lập.