rovarga commented on code in PR #2135: URL: https://github.com/apache/karaf/pull/2135#discussion_r2467156853
########## manual/src/main/asciidoc/user-guide/provisioning.adoc: ########## @@ -406,6 +408,229 @@ A prerequisite feature is a special kind of dependency. If you add the `prerequi } ---- +[[optional-feature-dependencies]] +===== Optional feature dependencies + +A feature dependency can be marked as optional using the `dependency="true"` attribute. This creates a flexible dependency mechanism where the feature dependency is only installed if it's actually needed. + +---- +<feature name="my-feature" version="1.0.0"> + <feature dependency="true">optional-feature</feature> + <requirement>some-capability</requirement> +</feature> +---- + +**How optional dependencies work:** + +* **Default behavior** (without `dependency="true"`): The feature dependency is always installed +* **Optional behavior** (with `dependency="true"`): The feature dependency is only installed if the required capabilities are not already provided by the system + +This mechanism enables: +* **Alternative implementations**: Multiple features can provide the same capability, and only one will be installed +* **Conflict avoidance**: Prevents multiple implementations of the same service from being installed simultaneously +* **Flexible deployment**: Features can work with different providers without modification + +**Note**: While the `dependency="true"` attribute is supported for feature dependencies, the current Karaf standard features primarily use this pattern for bundle dependencies. Feature-level optional dependencies are more commonly used in custom feature definitions where alternative implementations need to be selected at deployment time. + +===== Capabilities and requirements + +Features can declare what they provide (capabilities) and what they need (requirements). This enables the feature resolver to automatically install the right dependencies and avoid conflicts. + +**Capabilities** + +A capability declares what a feature provides to the system: + +---- +<feature name="http-provider" version="1.0.0"> + <bundle>mvn:com.example/http-bundle/1.0.0</bundle> + <capability>http-service;provider:=my-http</capability> +</feature> +---- + +The capability syntax is: `namespace;attribute1:=value1;attribute2:=value2` + +**Requirements** + +A requirement declares what a feature needs from the system: + +---- +<feature name="web-app" version="1.0.0"> + <requirement>http-service</requirement> + <bundle>mvn:com.example/web-bundle/1.0.0</bundle> +</feature> +---- + +The requirement syntax can include OSGi filter expressions: + +---- +<requirement>osgi.ee;filter:="(&(osgi.ee=JavaSE)(!(version>=1.8)))"</requirement> +---- + +**How the resolver works:** + +1. When a feature with requirements is installed, the resolver checks if the required capabilities are already available +2. If capabilities are missing, it looks for features that provide them +3. If multiple features provide the same capability, it chooses one (typically the first available) +4. Conditional dependencies (with `dependency="true"`) are only installed if their capabilities are needed + +This enables automatic dependency resolution and prevents conflicts between alternative implementations. + +===== Conditional bundles and features + +The `<conditional>` element allows features to include bundles or other features only when specific conditions are met. + +**Feature-based conditions** + +Install content only when a specific feature is present: + +---- +<feature name="my-feature" version="1.0.0"> + <bundle>mvn:com.example/core-bundle/1.0.0</bundle> + <conditional> + <condition>webconsole</condition> + <bundle>mvn:com.example/webconsole-plugin/1.0.0</bundle> + </conditional> +</feature> +---- + +**Requirement-based conditions** + +Install content only when specific OSGi requirements are satisfied: + +---- +<feature name="my-feature" version="1.0.0"> + <bundle>mvn:com.example/core-bundle/1.0.0</bundle> + <conditional> + <condition>req:osgi.extender;filter:="(&(osgi.extender=osgi.blueprint)(version>=1.0))"</condition> + <bundle>mvn:com.example/blueprint-support/1.0.0</bundle> + </conditional> +</feature> +---- + +**Condition syntax:** + +* **Feature condition**: `<condition>feature-name</condition>` - installs when the named feature is present +* **Requirement condition**: `<condition>req:osgi.requirement.syntax</condition>` - installs when the OSGi requirement is satisfied + +===== Example: Database Service Feature Resolution + +This example demonstrates how optional dependencies, capabilities, and requirements work together to provide flexible service selection. Consider a web application that needs a database service. + +**Feature Definitions** + +Here's how you might define features for database service selection: + +---- +<feature name="webapp" version="1.0.0" description="Web Application"> + <feature dependency="true">h2-database</feature> + <requirement>database-service</requirement> + <bundle>mvn:com.example/webapp/1.0.0</bundle> +</feature> + +<feature name="h2-database" version="1.0.0" description="H2 Database Service"> + <bundle start-level="20">mvn:com.h2database/h2/2.1.214</bundle> + <bundle start-level="30">mvn:com.example/h2-service/1.0.0</bundle> + <capability>database-service;provider:=h2</capability> + <conditional> + <condition>webconsole</condition> + <bundle start-level="30">mvn:com.example/h2-webconsole/1.0.0</bundle> + </conditional> +</feature> + +<feature name="postgresql-database" version="1.0.0" description="PostgreSQL Database Service"> + <bundle start-level="20">mvn:org.postgresql/postgresql/42.5.0</bundle> + <bundle start-level="30">mvn:com.example/postgresql-service/1.0.0</bundle> + <capability>database-service;provider:=postgresql</capability> +</feature> +---- + +**How it works:** + +1. **The `webapp` feature** declares a requirement for `database-service` capability +2. **The `webapp` feature** has an optional dependency on `h2-database` (marked with `dependency="true"`) +3. **The `h2-database` feature** provides the `database-service;provider:=h2` capability +4. **The `h2-database` feature** includes a conditional bundle for webconsole integration +5. **Alternative providers** like `postgresql-database` also provide `database-service` capabilities with different providers + +**Resolution Scenarios:** + +**Scenario A: Clean Installation** +1. No `database-service` capability exists in the system +2. Installing the `webapp` feature triggers the requirement for `database-service` +3. The resolver finds that `h2-database` provides this capability +4. `h2-database` is installed (because the dependency is needed) +5. The `database-service;provider:=h2` capability is provided +6. The `webapp` feature requirement is satisfied + +**Scenario B: Alternative Provider Already Present** +1. `postgresql-database` feature is already installed, providing `database-service;provider:=postgresql` +2. Installing the `webapp` feature triggers the requirement for `database-service` +3. The resolver finds that the capability is already provided by `postgresql-database` +4. `h2-database` is **not installed** (because the dependency is not needed) +5. The `webapp` feature requirement is satisfied by the existing provider + +**Benefits of this design:** + +* **Provider agnostic**: The `webapp` feature works with any database service provider +* **Conflict free**: Only one database service provider is active at a time +* **Flexible**: Users can choose their preferred database implementation +* **Automatic**: No manual configuration needed to avoid conflicts + +This pattern is used throughout Karaf for services where multiple implementations are available, such as HTTP services, transaction managers, logging frameworks, and database services. + +===== Bundle Refresh Handling + +During feature resolution and deployment, Karaf automatically detects bundles that need to be refreshed to ensure proper wiring and package resolution. This refresh mechanism is crucial for maintaining a consistent OSGi environment. + +**When bundles are refreshed:** Review Comment: I very much like this section, but in my experience users are flabbergasted by the various causes. Can we perhaps include example messages logged in karaf.log when these happen? Perhaps in a 'Diagnosing causes of bundle refresh' section? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
