OSGi Framework in iDempiere

Level: Intermediate Module: Architecture 17 min read Lesson 18 of 47

Overview

  • What you’ll learn:
    • The OSGi specification and how Eclipse Equinox serves as the OSGi container in iDempiere
    • Bundle lifecycle management, MANIFEST.MF configuration, and Declarative Services component model
    • How to work with extension points, service references, bundle dependencies, hot deployment, and debug common OSGi resolution failures
  • Prerequisites: Lessons 1–17 (especially Lesson 2: iDempiere Architecture Overview)
  • Estimated reading time: 22 minutes

Introduction

iDempiere’s modular architecture is built on top of the OSGi (Open Services Gateway initiative) framework. This is not a superficial design choice — OSGi permeates every aspect of how iDempiere is structured, deployed, and extended. When you install a plugin, register a model validator, or add a custom process, you are using OSGi whether you realize it or not.

In Lesson 2, we introduced OSGi at a high level. This lesson takes a deep dive into the framework itself. We will examine how bundles work internally, trace a bundle through its entire lifecycle, dissect the MANIFEST.MF metadata file, explore the Declarative Services component model that iDempiere uses for dependency injection, and learn how to diagnose the OSGi issues that inevitably arise during plugin development. By the end, you will have a thorough understanding of the plumbing that makes iDempiere’s extensibility possible.

The OSGi Specification

OSGi is a Java modularity specification originally developed for embedded systems and home gateways in the late 1990s. It has since evolved into the standard modularity layer for enterprise Java applications. The specification defines:

  • A module system — Java code is organized into bundles, each with explicit declarations of what it provides and what it requires.
  • A lifecycle layer — Bundles can be installed, started, stopped, updated, and uninstalled at runtime without restarting the JVM.
  • A service layer — Bundles can publish services (Java objects) into a service registry and look up services published by other bundles.

The key insight of OSGi is that Java’s default classloading model (a flat classpath) is inadequate for large, modular applications. Without OSGi, all JAR files share a single classpath and any class can see any other class. This leads to version conflicts, hidden dependencies, and fragile applications that break when a single library is updated. OSGi solves this by giving each bundle its own classloader with strictly controlled visibility.

Eclipse Equinox: iDempiere’s OSGi Container

Several OSGi implementations exist: Apache Felix, Knopflerfish, and Eclipse Equinox. iDempiere uses Eclipse Equinox, the reference implementation of the OSGi specification and the same runtime that powers the Eclipse IDE.

Equinox provides:

  • A compliant OSGi R7+ framework implementation
  • The osgi.console — a command-line console for inspecting and managing bundles at runtime
  • Integration with Eclipse PDE (Plugin Development Environment) for development tooling
  • The p2 provisioning system for managing bundle installation and updates

When iDempiere starts, the Equinox framework initializes first, then loads and resolves all configured bundles, and finally starts them in dependency order. The entire iDempiere application — including the web server, business logic, and UI — runs as a collection of OSGi bundles inside this container.

Bundles: The Building Blocks

A bundle is the fundamental unit of modularity in OSGi. Physically, a bundle is a JAR file with special metadata in its META-INF/MANIFEST.MF file. Logically, a bundle is an independently deployable module with well-defined boundaries.

iDempiere’s core consists of many bundles. Some of the most important ones are:

  • org.adempiere.base — Core model classes, business logic, and utility functions
  • org.adempiere.ui.zk — The ZK-based web user interface layer
  • org.adempiere.server — Server processes, schedulers, and background tasks
  • org.adempiere.report.jasper — JasperReports integration
  • org.adempiere.plugin.utils — Utility classes for plugin development
  • org.compiere.db.postgresql — PostgreSQL database driver and dialect

Each of these bundles exports specific Java packages that other bundles (including your plugins) can import and use.

The Bundle Lifecycle

Every OSGi bundle goes through a well-defined lifecycle with the following states:

INSTALLED → RESOLVED → STARTING → ACTIVE → STOPPING → UNINSTALLED

Understanding these states is critical for troubleshooting:

  1. INSTALLED — The bundle’s JAR file has been placed into the framework, but its dependencies have not yet been verified. The framework knows the bundle exists but has not checked whether all required packages are available.
  2. RESOLVED — The framework has verified that all of the bundle’s declared dependencies (Import-Package, Require-Bundle) can be satisfied by other installed bundles. The bundle’s classloader has been created and wired to the appropriate provider bundles. The bundle is ready to start but is not yet active.
  3. STARTING — The bundle’s BundleActivator.start() method (if one is declared) is currently executing. This is a transient state.
  4. ACTIVE — The bundle is fully operational. Its activator has completed, its services are registered, and its code is available to other bundles.
  5. STOPPING — The bundle’s BundleActivator.stop() method is executing. Resources are being released and services are being unregistered. This is also a transient state.
  6. UNINSTALLED — The bundle has been removed from the framework. Its classloader is discarded, and its packages are no longer available.

The most common problem you will encounter is a bundle stuck in the INSTALLED state when it should be ACTIVE. This means the framework could not resolve one or more of its dependencies. We will cover debugging this in the final section of this lesson.

MANIFEST.MF: The Bundle Descriptor

The META-INF/MANIFEST.MF file is the heart of every OSGi bundle. It declares the bundle’s identity, version, dependencies, and capabilities. Here is a typical MANIFEST.MF for an iDempiere plugin:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: My Custom Plugin
Bundle-SymbolicName: com.example.myplugin;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.example.myplugin.Activator
Bundle-Vendor: Example Corp
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: org.compiere.model;version="0.0.0",
 org.compiere.process;version="0.0.0",
 org.compiere.util;version="0.0.0",
 org.adempiere.base.event;version="0.0.0",
 org.osgi.framework;version="1.9.0",
 org.osgi.service.event;version="1.4.0"
Export-Package: com.example.myplugin.api
Service-Component: OSGI-INF/component.xml

Let us break down each critical header:

Bundle-SymbolicName

This is the unique identifier for the bundle within the OSGi framework. By convention, it follows the reverse domain name pattern of the bundle’s base Java package. The singleton:=true directive means only one version of this bundle can be active at a time — this is required for bundles that contribute to extension points.

Bundle-Version

The version follows the format major.minor.micro.qualifier. OSGi uses semantic versioning to resolve dependencies. The qualifier part is a string typically set to a timestamp or “qualifier” (replaced during the build). Version ranges in dependency declarations allow the framework to handle version compatibility automatically.

Bundle-Activator

Specifies a class that implements org.osgi.framework.BundleActivator. This class receives callbacks when the bundle starts and stops. Not every bundle needs an activator — Declarative Services (covered below) often eliminate the need for one.

Import-Package

Declares the Java packages that this bundle requires from other bundles. The OSGi framework will not resolve (and therefore will not start) a bundle unless every imported package is available from some other active bundle. The version attribute specifies the minimum acceptable version of the package.

Import-Package: org.compiere.model;version="[11.0.0,12.0.0)",
 org.compiere.util;version="0.0.0"

Version ranges use interval notation: [11.0.0,12.0.0) means version 11.0.0 (inclusive) up to but not including 12.0.0.

Export-Package

Declares the Java packages from this bundle that are visible to other bundles. Any package not listed in Export-Package is private to the bundle and invisible to other bundles — even if it exists in the JAR file. This is how OSGi enforces encapsulation.

Service-Component

Points to the XML files that define Declarative Services components (covered in the next section). Multiple component files can be listed, separated by commas.

Require-Bundle vs Import-Package

You may also see a Require-Bundle header, which declares a dependency on an entire bundle rather than on specific packages:

Require-Bundle: org.adempiere.base;bundle-version="11.0.0"

While this is simpler to write, the OSGi community generally recommends Import-Package because it creates a looser coupling — your bundle depends on a capability (a package) rather than a specific provider (a bundle). However, in the iDempiere ecosystem, Require-Bundle is common because the core bundles are the only providers of their packages.

Declarative Services (DS)

Declarative Services is an OSGi component model that simplifies the creation and consumption of services. Instead of writing procedural code in an Activator to register and look up services, you declare your components and their dependencies in XML, and the DS runtime handles the wiring automatically.

iDempiere relies heavily on Declarative Services for its extension mechanisms — model factories, callout factories, event handlers, and more are all registered as DS components.

Component XML

A DS component is defined in an XML file, typically placed in the OSGI-INF/ directory. Here is a representative example:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
    name="com.example.myplugin.MyEventHandler"
    immediate="true">
  <implementation class="com.example.myplugin.MyEventHandler"/>
  <service>
    <provide interface="org.osgi.service.event.EventHandler"/>
  </service>
  <property name="event.topics" type="String"
      value="org/adempiere/base/event/PO_BEFORE_NEW"/>
  <property name="component.name" type="String"
      value="com.example.myplugin.MyEventHandler"/>
  <reference name="eventManager"
      interface="org.adempiere.base.event.IEventManager"
      bind="bindEventManager"
      unbind="unbindEventManager"
      cardinality="1..1"
      policy="dynamic"/>
</scr:component>

Key elements of a component definition:

  • implementation class — The Java class that implements the component.
  • service / provide interface — The service interface(s) this component implements and registers in the OSGi service registry.
  • property — Configuration properties for the component. Event handlers use properties to declare which event topics they subscribe to.
  • reference — A dependency on another OSGi service. The DS runtime will inject the referenced service automatically. cardinality="1..1" means exactly one instance is required. policy="dynamic" means the reference can be updated without restarting the component.
  • immediate=”true” — The component is activated as soon as its dependencies are satisfied, rather than waiting until the service is first requested (lazy activation).

Service References and Binding

When your component declares a reference, the DS runtime calls the specified bind method with the service instance when the service becomes available, and the unbind method when it goes away. Here is how the corresponding Java code looks:

public class MyEventHandler implements EventHandler {

    private IEventManager eventManager;

    // Called by DS runtime when IEventManager service is available
    protected void bindEventManager(IEventManager em) {
        this.eventManager = em;
    }

    // Called by DS runtime when IEventManager service is removed
    protected void unbindEventManager(IEventManager em) {
        this.eventManager = null;
    }

    @Override
    public void handleEvent(Event event) {
        // Handle the event
    }
}

The bind/unbind methods must be protected or public (not private) for the DS runtime to invoke them.

Annotations vs XML

Modern OSGi (DS 1.3+) supports annotations as an alternative to XML component definitions:

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

@Component(
    immediate = true,
    property = {
        "event.topics=org/adempiere/base/event/PO_BEFORE_NEW"
    }
)
public class MyEventHandler implements EventHandler {

    @Reference
    private IEventManager eventManager;

    @Override
    public void handleEvent(Event event) {
        // Handle the event
    }
}

While annotations are more concise, many iDempiere plugins and examples still use XML component definitions. Both approaches are valid. The annotations are processed at build time and generate the XML automatically.

Extension Points vs Declarative Services

iDempiere’s history spans two different OSGi extension mechanisms, and you will encounter both:

Eclipse Extension Points

Extension points come from the Eclipse runtime (a layer on top of OSGi). They use a plugin.xml file where you declare extensions to named extension points defined by other bundles. iDempiere originally used extension points for its factory registrations. Some older documentation and plugins still reference this mechanism.

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
  <extension point="org.adempiere.base.IModelFactory">
    <factory class="com.example.myplugin.MyModelFactory"/>
  </extension>
</plugin>

Declarative Services (Preferred)

The iDempiere project has been migrating toward pure OSGi Declarative Services, which are more standard, more flexible, and do not require Eclipse-specific runtime dependencies. The preferred approach for new plugins is to register factories and handlers as DS components.

<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
    name="com.example.myplugin.MyModelFactory">
  <implementation class="com.example.myplugin.MyModelFactory"/>
  <service>
    <provide interface="org.adempiere.base.IModelFactory"/>
  </service>
  <property name="service.ranking" type="Integer" value="100"/>
</scr:component>

The service.ranking property determines priority when multiple implementations of the same service exist. Higher values take precedence.

Bundle Dependencies and Versioning

Managing dependencies correctly is essential for plugin stability. Here are the key principles:

Dependency Resolution

When the OSGi framework resolves a bundle, it matches each Import-Package entry against the Export-Package entries of all installed bundles. If a matching package with a compatible version is found, the importing bundle’s classloader is wired to the exporting bundle’s classloader for that package.

Version Ranges

Using precise version ranges protects your plugin from incompatible changes:

# Exact version (fragile — avoids any updates):
Import-Package: org.compiere.model;version="11.0.0"

# Minimum version (accepts any version 11.0.0 or higher):
Import-Package: org.compiere.model;version="11.0.0"

# Version range (recommended — accepts 11.x but not 12.x):
Import-Package: org.compiere.model;version="[11.0.0,12.0.0)"

Optional Dependencies

Some imports can be marked as optional if your plugin can function without them:

Import-Package: org.compiere.model;version="0.0.0",
 com.example.optional.feature;version="0.0.0";resolution:=optional

If the optional package is not available, the bundle will still resolve. However, your code must handle the case where classes from that package are not loadable (typically using try-catch around ClassNotFoundException).

Hot Deployment

One of the most powerful features of OSGi is the ability to deploy and update bundles at runtime without restarting the application. In iDempiere, this works as follows:

  1. Drop the JAR — Place your plugin’s JAR file into the plugins/ directory of your iDempiere installation.
  2. Automatic detection — iDempiere’s file installer monitors the plugins/ directory and automatically installs new bundles.
  3. Resolution and activation — The framework resolves the new bundle’s dependencies and, if successful, starts it. Your services become available immediately.

To update a plugin, simply replace the JAR file in the plugins/ directory with the new version. The framework will stop the old version, install the new one, and start it.

You can also manage bundles through the OSGi console. Connect to the console (typically via telnet on port 11612) and use these commands:

# List all bundles and their states
ss

# Install a bundle
install file:///path/to/plugin.jar

# Start a bundle (use the bundle ID from the 'ss' output)
start 285

# Stop a bundle
stop 285

# Update a bundle (reloads from the same location)
update 285

# Uninstall a bundle
uninstall 285

# Refresh bundles (recalculates wiring after changes)
refresh

# Show details of a specific bundle
bundle 285

Debugging OSGi Issues

OSGi problems can be mystifying if you do not know where to look. Here are the most common issues and how to solve them.

Bundle Stuck in INSTALLED State

This is the most frequent problem. It means the bundle cannot resolve — one or more of its dependencies are not satisfied. To diagnose:

# In the OSGi console, use the 'diag' command:
diag 285

# Output will show exactly which packages cannot be resolved:
# com.example.myplugin [285]
#   Unresolved requirement: Import-Package: org.compiere.model; version="12.0.0"
#     -> No matching export found

The fix is usually one of:

  • Correct the version range in your Import-Package to match what is actually exported
  • Install the missing dependency bundle
  • Remove an import for a package you do not actually use

ClassNotFoundException at Runtime

If your bundle is ACTIVE but you get a ClassNotFoundException, the problem is usually that you are trying to use a class from a package you did not import. Add the missing package to your Import-Package header.

Service Not Found

If your DS component is not activating or a service reference is not being injected:

# List all registered services
services

# List DS components and their states
scr:list

# Show details of a specific component
scr:info com.example.myplugin.MyEventHandler

Common causes:

  • The Service-Component header in MANIFEST.MF does not point to the correct XML file
  • The component XML has a typo in the interface name or implementation class
  • A required service reference has cardinality 1..1 but the service it depends on is not registered
  • The implementation class does not have a no-argument constructor

Stale Bundle Cache

Sometimes the framework caches bundle data that becomes stale. If you are experiencing unexplainable behavior after updating a plugin, try clearing the cache:

# Stop iDempiere, clear the OSGi configuration area, then restart:
# The configuration area is typically in the 'configuration/' directory
# Delete the 'org.eclipse.osgi/' subdirectory to force a clean resolve

Key Takeaways

  • OSGi is the modularity framework that enables iDempiere’s plugin architecture. Eclipse Equinox is the specific OSGi implementation used.
  • Bundles are the fundamental unit of deployment, with a well-defined lifecycle: INSTALLED, RESOLVED, STARTING, ACTIVE, STOPPING, UNINSTALLED.
  • The MANIFEST.MF file declares a bundle’s identity, dependencies (Import-Package), and exports (Export-Package). Getting this right is essential.
  • Declarative Services (DS) provides a declarative, XML-based (or annotation-based) way to register and consume services — this is the preferred extension mechanism in modern iDempiere.
  • Hot deployment allows plugins to be installed, updated, and removed without restarting iDempiere.
  • When bundles fail to resolve, the diag command in the OSGi console is your best diagnostic tool.

What’s Next

Now that you understand the OSGi framework, the next lesson explores iDempiere’s extension point and factory pattern in detail. You will learn about IModelFactory, IColumnCallout, IProcessFactory, and the Event Handler pattern — the specific mechanisms by which plugins integrate with iDempiere’s core functionality.

繁體中文

概述

  • 學習內容:OSGi 規範以及 Eclipse Equinox 如何在 iDempiere 中作為 OSGi 容器;Bundle 生命週期管理、MANIFEST.MF 配置和 Declarative Services 元件模型;如何使用擴展點、服務參照、Bundle 相依性、熱部署,以及除錯常見的 OSGi 解析失敗。
  • 先修條件:第 1-17 課(尤其是第 2 課:iDempiere 架構概覽)
  • 預估閱讀時間:22 分鐘

簡介

iDempiere 的模組化架構建立在 OSGi(Open Services Gateway initiative)框架之上。這不是一個表面的設計選擇——OSGi 滲透到 iDempiere 結構、部署和擴展的每個面向。當您安裝 Plugin、註冊 Model Validator 或新增自訂流程時,無論您是否意識到,都在使用 OSGi。

本課深入探討框架本身。我們將檢視 Bundle 的內部運作方式、追蹤 Bundle 的完整生命週期、剖析 MANIFEST.MF 中繼資料檔案、探索 iDempiere 用於相依性注入的 Declarative Services 元件模型,並學習如何診斷 Plugin 開發中必然出現的 OSGi 問題。

OSGi 規範

OSGi 是一個 Java 模組化規範。它定義了:

  • 模組系統——Java 程式碼被組織成 Bundle,每個 Bundle 都明確宣告它提供什麼和需要什麼。
  • 生命週期層——Bundle 可以在執行時安裝、啟動、停止、更新和解除安裝,無需重新啟動 JVM。
  • 服務層——Bundle 可以將服務(Java 物件)發佈到服務登錄處,並查詢其他 Bundle 發佈的服務。

Eclipse Equinox:iDempiere 的 OSGi 容器

iDempiere 使用 Eclipse Equinox,它是 OSGi 規範的參考實作,也是驅動 Eclipse IDE 的相同執行環境。

Bundle:建構區塊

Bundle 是 OSGi 中模組化的基本單元。實體上,Bundle 是一個在其 META-INF/MANIFEST.MF 檔案中具有特殊中繼資料的 JAR 檔案。

Bundle 生命週期

INSTALLED → RESOLVED → STARTING → ACTIVE → STOPPING → UNINSTALLED
  1. INSTALLED——Bundle 的 JAR 檔案已放入框架,但其相依性尚未驗證。
  2. RESOLVED——框架已驗證所有宣告的相依性可以被滿足。Bundle 已準備好啟動。
  3. STARTING——Bundle 的 BundleActivator.start() 方法正在執行。
  4. ACTIVE——Bundle 已完全運作。
  5. STOPPING——Bundle 的 BundleActivator.stop() 方法正在執行。
  6. UNINSTALLED——Bundle 已從框架中移除。

最常見的問題是 Bundle 卡在 INSTALLED 狀態而不是 ACTIVE。這表示框架無法解析其一個或多個相依性。

MANIFEST.MF:Bundle 描述器

META-INF/MANIFEST.MF 檔案是每個 OSGi Bundle 的核心。它宣告 Bundle 的身份、版本、相依性和能力。

  • Bundle-SymbolicName——Bundle 在框架中的唯一識別碼。
  • Bundle-Version——版本遵循 major.minor.micro.qualifier 格式。
  • Bundle-Activator——指定實作 BundleActivator 的類別。
  • Import-Package——宣告此 Bundle 需要的 Java 套件。
  • Export-Package——宣告此 Bundle 中對其他 Bundle 可見的套件。
  • Service-Component——指向定義 Declarative Services 元件的 XML 檔案。

Declarative Services(DS)

Declarative Services 是一個簡化服務建立和使用的 OSGi 元件模型。iDempiere 大量依賴 DS 實現其擴展機制——Model Factory、Callout Factory、Event Handler 等都是作為 DS 元件註冊的。

DS 元件在 XML 檔案中定義(通常放在 OSGI-INF/ 目錄中),或使用註解。元件定義的關鍵元素包括:

  • implementation class——實作元件的 Java 類別。
  • service / provide interface——元件實作的服務介面。
  • property——元件的配置屬性。
  • reference——對另一個 OSGi 服務的相依性。

擴展點與 Declarative Services

iDempiere 的歷史跨越兩種不同的 OSGi 擴展機制。Eclipse 擴展點使用 plugin.xml,而 Declarative Services(首選)使用 component.xml。iDempiere 專案一直在向純 OSGi Declarative Services 遷移。

Bundle 相依性和版本管理

正確管理相依性對 Plugin 穩定性至關重要。使用精確的版本範圍保護您的 Plugin 免受不相容的變更。可以使用 resolution:=optional 將匯入標記為可選。

熱部署

OSGi 最強大的功能之一是能夠在執行時部署和更新 Bundle,無需重新啟動應用程式。在 iDempiere 中,只需將 Plugin 的 JAR 檔案放入 plugins/ 目錄即可。

您也可以透過 OSGi 控制台管理 Bundle,使用 ssinstallstartstopupdateuninstall 等命令。

除錯 OSGi 問題

最常見的問題和解決方案:

  • Bundle 卡在 INSTALLED 狀態:使用 diag 命令診斷。通常是版本範圍不匹配或缺少相依性 Bundle。
  • 執行時的 ClassNotFoundException:新增缺少的套件到 Import-Package。
  • 找不到服務:使用 scr:listscr:info 檢查 DS 元件。
  • 過期的 Bundle 快取:清除 configuration/org.eclipse.osgi/ 目錄。

重點摘要

  • OSGi 是啟用 iDempiere Plugin 架構的模組化框架。Eclipse Equinox 是使用的特定 OSGi 實作。
  • Bundle 是部署的基本單元,具有明確定義的生命週期。
  • MANIFEST.MF 檔案宣告 Bundle 的身份、相依性和匯出。正確配置至關重要。
  • Declarative Services 提供宣告式、基於 XML(或基於註解)的方式來註冊和使用服務——這是現代 iDempiere 中首選的擴展機制。
  • 熱部署允許在不重新啟動 iDempiere 的情況下安裝、更新和移除 Plugin。
  • 當 Bundle 無法解析時,OSGi 控制台中的 diag 命令是最佳的診斷工具。

下一步

現在您了解了 OSGi 框架,下一課將詳細探索 iDempiere 的擴展點和工廠模式。您將學習 IModelFactory、IColumnCallout、IProcessFactory 和 Event Handler 模式——Plugin 與 iDempiere 核心功能整合的具體機制。

日本語

概要

  • 学習内容:OSGi仕様とEclipse EquinoxがiDempiereでOSGiコンテナとしてどのように機能するか。バンドルライフサイクル管理、MANIFEST.MF設定、Declarative Servicesコンポーネントモデル。拡張ポイント、サービス参照、バンドル依存関係、ホットデプロイメント、一般的なOSGi解決障害のデバッグ。
  • 前提条件:レッスン1〜17(特にレッスン2:iDempiereアーキテクチャ概要)
  • 推定読了時間:22分

はじめに

iDempiereのモジュラーアーキテクチャはOSGi(Open Services Gateway initiative)フレームワークの上に構築されています。プラグインのインストール、Model Validatorの登録、カスタムプロセスの追加時に、意識するかどうかに関わらずOSGiを使用しています。

このレッスンではフレームワーク自体を深く掘り下げます。バンドルの内部動作、完全なライフサイクル、MANIFEST.MFメタデータファイル、Declarative Servicesコンポーネントモデル、OSGi問題の診断方法を学びます。

OSGi仕様

OSGiはJavaのモジュラリティ仕様で、以下を定義します:

  • モジュールシステム——Javaコードはバンドルに組織され、各バンドルが提供するものと必要なものを明示的に宣言。
  • ライフサイクルレイヤー——バンドルはJVMを再起動せずに実行時にインストール、開始、停止、更新、アンインストール可能。
  • サービスレイヤー——バンドルはサービスレジストリにサービスを公開し、他のバンドルが公開したサービスを検索可能。

Eclipse Equinox:iDempiereのOSGiコンテナ

iDempiereはOSGi仕様のリファレンス実装であるEclipse Equinoxを使用しています。

バンドル:ビルディングブロック

バンドルはOSGiにおけるモジュラリティの基本単位です。物理的にはバンドルはMETA-INF/MANIFEST.MFファイルに特別なメタデータを持つJARファイルです。

バンドルライフサイクル

INSTALLED → RESOLVED → STARTING → ACTIVE → STOPPING → UNINSTALLED
  1. INSTALLED——バンドルのJARファイルがフレームワークに配置されたが、依存関係はまだ検証されていない。
  2. RESOLVED——フレームワークがすべての宣言された依存関係を満たせることを確認済み。起動準備完了。
  3. STARTING——バンドルのBundleActivator.start()メソッドが実行中。
  4. ACTIVE——バンドルは完全に動作可能。
  5. STOPPING——バンドルのBundleActivator.stop()メソッドが実行中。
  6. UNINSTALLED——バンドルがフレームワークから削除済み。

最も一般的な問題は、バンドルがACTIVEであるべきなのにINSTALLED状態で停止することです。

MANIFEST.MF:バンドルディスクリプタ

META-INF/MANIFEST.MFファイルはすべてのOSGiバンドルの中心です。主要なヘッダー:

  • Bundle-SymbolicName——フレームワーク内でのバンドルの一意識別子。
  • Bundle-Version——major.minor.micro.qualifier形式。
  • Bundle-Activator——BundleActivatorを実装するクラスを指定。
  • Import-Package——このバンドルが必要とするJavaパッケージを宣言。
  • Export-Package——他のバンドルに公開するパッケージを宣言。
  • Service-Component——Declarative Servicesコンポーネントを定義するXMLファイルを指す。

Declarative Services(DS)

Declarative Servicesはサービスの作成と利用を簡素化するOSGiコンポーネントモデルです。iDempiereはDSを広く活用しています。コンポーネントはXMLファイルまたはアノテーションで定義されます。

拡張ポイント vs Declarative Services

Eclipse拡張ポイント(plugin.xml使用)は古いアプローチで、Declarative Services(推奨)は現代的なアプローチです。iDempiereプロジェクトは純粋なOSGi DSに移行しています。

バンドル依存関係とバージョニング

依存関係の正しい管理はプラグインの安定性に不可欠です。正確なバージョン範囲を使用し、オプションの依存関係にはresolution:=optionalを使用します。

ホットデプロイメント

OSGiの最も強力な機能の1つは、アプリケーションを再起動せずに実行時にバンドルをデプロイおよび更新できることです。プラグインのJARファイルをplugins/ディレクトリに配置するだけです。

OSGiコンソール(ssinstallstartstopupdatediagコマンド)でもバンドルを管理できます。

OSGi問題のデバッグ

  • INSTALLED状態で停止:diagコマンドで診断。通常はバージョン範囲の不一致か依存バンドルの不足。
  • 実行時のClassNotFoundException:不足しているパッケージをImport-Packageに追加。
  • サービスが見つからない:scr:listscr:infoでDSコンポーネントを確認。
  • 古いバンドルキャッシュ:configuration/org.eclipse.osgi/ディレクトリを削除。

重要ポイント

  • OSGiはiDempiereのプラグインアーキテクチャを可能にするモジュラリティフレームワーク。Eclipse Equinoxが使用される特定のOSGi実装。
  • バンドルはデプロイメントの基本単位で、明確に定義されたライフサイクルを持つ。
  • MANIFEST.MFファイルはバンドルの識別、依存関係、エクスポートを宣言。正確な設定が不可欠。
  • Declarative Servicesは宣言的なサービス登録・利用方法を提供——現代のiDempiereで推奨される拡張メカニズム。
  • ホットデプロイメントにより、iDempiereを再起動せずにプラグインのインストール、更新、削除が可能。
  • バンドルが解決に失敗した場合、OSGiコンソールのdiagコマンドが最良の診断ツール。

次のステップ

OSGiフレームワークを理解した今、次のレッスンではiDempiereの拡張ポイントとファクトリーパターンを詳しく探索します。IModelFactory、IColumnCallout、IProcessFactory、Event Handlerパターン——プラグインがiDempiereのコア機能と統合する具体的なメカニズムについて学びます。

You Missed