Chapter 9. Develop an Application for the Karaf Image
9.1. Create a Karaf Project using Maven Archetype
To create a Karaf project using a Maven archetype, follow these steps:
- Go to the appropriate directory on your system.
Launch the Maven command to create a Karaf project
mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate \ -DarchetypeCatalog=https://maven.repository.redhat.com/ga/io/fabric8/archetypes/archetypes-catalog/2.2.0.fuse-710017-redhat-00003/archetypes-catalog-2.2.0.fuse-710017-redhat-00003-archetype-catalog.xml \ -DarchetypeGroupId=org.jboss.fuse.fis.archetypes \ -DarchetypeArtifactId=karaf-camel-log-archetype \ -DarchetypeVersion=2.2.0.fuse-710017-redhat-00003
The archetype plug-in switches to interactive mode to prompt you for the remaining fields
Define value for property 'groupId': : org.example.fis Define value for property 'artifactId': : fuse71-karaf-camel-log Define value for property 'version': 1.0-SNAPSHOT: : Define value for property 'package': org.example.fis: : Confirm properties configuration: groupId: org.example.fis artifactId: fuse71-karaf-camel-log version: 1.0-SNAPSHOT package: org.example.fis Y: :
When prompted, enter
org.example.fis
for thegroupId
value andfuse71-karaf-camel-log
for theartifactId
value. Accept the defaults for the remaining fields.
Then, follow the instructions in the quickstart on how to build and deploy the example.
For the full list of available Karaf archetypes, see Section 9.3, “Karaf Archetype Catalog”.
9.2. Structure of the Camel Karaf Application
The directory structure of a Camel Karaf application is as follows:
├── pom.xml ├── README.md ├── configuration │ └── settings.xml └── src ├── main │ ├── fabric8 │ │ └── deployment.yml │ ├── java │ │ └── org │ │ └── example │ │ └── fis │ └── resources │ ├── assembly │ │ └── etc │ │ └── org.ops4j.pax.logging.cfg │ └── OSGI-INF │ └── blueprint │ └── camel-log.xml └── test └── java └── org └── example └── fis
Where the following files are important for developing a Karaf application:
- pom.xml
Includes additional dependencies. You can add dependencies in the
pom.xml
file, for example for logging you can use SLF4J.<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency>
- org.ops4j.pax.logging.cfg
- Demonstrates how to customize log levels, sets logging level to DEBUG instead of the default INFO.
- camel-log.xml
- Contains the source code of the application.
- src/main/fabric8/deployment.yml
Provides additional configuration that is merged with the default OpenShift configuration file generated by the fabric8-maven-plugin.
NoteThis file is not used as part of the Karaf application, but it is used in all quickstarts to limit the resources such as CPU and memory usage.
9.3. Karaf Archetype Catalog
The Karaf archetype catalog includes the following examples.
Name | Description |
---|---|
| Demonstrates a simple Apache Camel application that logs a message to the server log every 5th second. |
| Demonstrates how to use SQL via JDBC along with Camel’s REST DSL to expose a RESTful API. |
| Demonstrates how to create a RESTful(JAX-RS) web service using CXF and expose it through the OSGi HTTP Service. |
9.4. Fabric8 Karaf Features
Fabric8 provides support for Apache Karaf making it easier to develop OSGi apps for Kubernetes.
The important features of Fabric8 are as listed below:
- Different strategies to resolve placeholders in Blueprint XML files.
- Environment variables
- System properties
- Services
- Kubernetes ConfigMap
- Kubernetes Secrets
- Using Kubernetes configuration maps to dynamically update the OSGi configuration administration.
- Provides Kubernetes heath checks for OSGi services.
9.4.1. Adding Fabric8 Karaf Features
To use the features, add fabric8-karaf-features
dependency to the project pom file.
<dependency> <groupId>io.fabric8</groupId> <artifactId>fabric8-karaf-features</artifactId> <version>${fabric8.version}</version> <classifier>features</classifier> <type>xml</type> </dependency>
These features will be installed into the Karaf server.
9.4.2. Fabric8 Karaf Core Bundle functionalities
The bundle fabric8-karaf-core
provides functionalities used by Blueprint and ConfigAdmin extensions.
To add the feature in a custom Karaf distribution, add it to startupFeatures
in the project pom.xml
<startupFeatures> ... <feature>fabric8-karaf-core</feature> ... </startupFeatures>
9.4.2.1. Property placeholders resolvers
The bundle fabric8-karaf-core
exports a service PlaceholderResolver
with the following interface:
public interface PlaceholderResolver { /** * Resolve a placeholder using the strategy indicated by the prefix * * @param value the placeholder to resolve * @return the resolved value or null if not resolved */ String resolve(String value); /** * Replaces all the occurrences of variables with their matching values from the resolver using the given source string as a template. * * @param source the string to replace in * @return the result of the replace operation */ String replace(String value); /** * Replaces all the occurrences of variables within the given source builder with their matching values from the resolver. * * @param value the builder to replace in * @rerurn true if altered */ boolean replaceIn(StringBuilder value); /** * Replaces all the occurrences of variables within the given dictionary * * @param dictionary the dictionary to replace in * @rerurn true if altered */ boolean replaceAll(Dictionary<String, Object> dictionary); /** * Replaces all the occurrences of variables within the given dictionary * * @param dictionary the dictionary to replace in * @rerurn true if altered */ boolean replaceAll(Map<String, Object> dictionary); }
The PlaceholderResolver
service acts as a collector for different property placeholder resolution strategies. The resolution strategies it provides by default are listed in the table.
- List of resolution strategies
Prefix | Example | Description |
env | env:JAVA_HOME | lookup the property from OS environment variables. |
sys | sys:java.version | lookup the property from Java JVM system properties. |
service | service:amq | lookup the property from OS environment variables using the service naming idiom. |
service.host | service.host:amq | lookup the property from OS environment variables using the service naming idiom returning the hostname part only. |
service.port | service.port:amq | lookup the property from OS environment variables using the service naming idiom returning the port part only. |
k8s:map | k8s:map:myMap/myKey | lookup the property from a Kubernetes ConfigMap (via API) |
k8s:secret | k8s:secret:amq/password | lookup the property from a Kubernetes Secrets (via API or volume mounts) |
The property placeholder service supports the following options:
- List of property placeholder service options
Name | Default | Description |
---|---|---|
fabric8.placeholder.prefix | $[ | The prefix for the placeholder |
fabric8.placeholder.suffix | ] | The suffix for the placeholder |
fabric8.k8s.secrets.path | null | A comma delimited list of paths were secrets are mapped |
fabric8.k8s.secrets.api.enabled | false | Enable/Disable consuming secrets via APIs |
To set the property placeholder service options you can use system properties or environment variables or both.
To access ConfigMaps on OpenShift the service account needs view permissions
oc policy add-role-to-user view system:serviceaccount:$(oc project -q):default -n $(oc project -q)
- Mount secret to the POD as access to secrets through API might be restricted.
- Secrets available on the POD as volume mounts are mapped to a directory named as the secret, as shown below
containers: - env: - name: FABRIC8_K8S_SECRETS_PATH value: /etc/secrets volumeMounts: - name: activemq-secret-volume mountPath: /etc/secrets/activemq readOnly: true - name: postgres-secret-volume mountPath: /etc/secrets/postgres readOnly: true volumes: - name: activemq-secret-volume secret: secretName: activemq - name: postgres-secret-volume secret: secretName: postgres
9.4.2.2. Adding a custom property placeholders resolvers
You can add a custom placeholder resolver to support a specific need, such as custom encryption. You can also use the PlaceholderResolver
service to make the resolvers available to Blueprint and ConfigAdmin.
To add a custom property placeholders resolvers, follow these steps:
Add the following mvn dependency to the project
pom.xml
.pom.xml
--- <dependency> <groupId>io.fabric8</groupId> <artifactId>fabric8-karaf-core</artifactId> </dependency> ---
Implement the PropertiesFunction interface and register it as OSGi service using SCR.
import io.fabric8.karaf.core.properties.function.PropertiesFunction; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.ConfigurationPolicy; import org.apache.felix.scr.annotations.Service; @Component( immediate = true, policy = ConfigurationPolicy.IGNORE, createPid = false ) @Service(PropertiesFunction.class) public class MyPropertiesFunction implements PropertiesFunction { @Override public String getName() { return "myResolver"; } @Override public String apply(String remainder) { // Parse and resolve remainder return remainder; } }
- You can reference the resolver in Configuration management as follows.
properties
my.property = $[myResolver:value-to-resolve]
9.4.3. Adding Fabric8 Karaf Config Admin Support
To include Config Admin Support feature in your custom Karaf distribution, add fabric8-karaf-cm
to startupFeatures
in your project pom.xml
pom.xml
<startupFeatures> ... <feature>fabric8-karaf-cm</feature> ... </startupFeatures>
9.4.3.1. Adding ConfigMap injection
The fabric8-karaf-cm
provides a ConfigAdmin
bridge that inject ConfigMap
values in Karaf’s ConfigAdmin
.
To be added by the ConfigAdmin bridge, a ConfigMap has to be labeled with karaf.pid, where its values corresponds to the pid of your component.
kind: ConfigMap apiVersion: v1 metadata: name: myconfig labels: karaf.pid: com.mycompany.bundle data: example.property.1: my property one example.property.2: my property two
Individual properties work for most cases. But to define your configuration, you can use a single property names. It is same as the pid file in karaf/etc
. For example,
kind: ConfigMap apiVersion: v1 metadata: name: myconfig labels: karaf.pid: com.mycompany.bundle data: com.mycompany.bundle.cfg: | example.property.1: my property one example.property.2: my property two
9.4.3.2. Configuration plugin
The fabric8-karaf-cm
provides a ConfigurationPlugin
which resolves configuration property placeholders.
To enable property substitution with the fabric8-karaf-cm
plug-in, you must set the Java property, fabric8.config.plugin.enabled
to true
. For example, you can set this property using the JAVA_OPTIONS
environment variable in the Karaf image, as follows:
JAVA_OPTIONS=-Dfabric8.config.plugin.enabled=true
An example of configuration property placeholders is shown below.
my.service.cfg
amq.usr = $[k8s:secret:$[env:ACTIVEMQ_SERVICE_NAME]/username] amq.pwd = $[k8s:secret:$[env:ACTIVEMQ_SERVICE_NAME]/password] amq.url = tcp://$[env+service:ACTIVEMQ_SERVICE_NAME]
my-service.xml
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd"> <cm:property-placeholder persistent-id="my.service" id="my.service" update-strategy="reload"/> <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent"> <property name="userName" value="${amq.usr}"/> <property name="password" value="${amq.pwd}"/> <property name="brokerURL" value="${amq.url}"/> </bean> </blueprint>
Fabric8 Karaf Config Admin supports the following options.
Name | Default | Description |
---|---|---|
fabric8.config.plugin.enabled | false | Enable ConfigurationPlugin |
fabric8.cm.bridge.enabled | true | Enable ConfigAdmin bridge |
fabric8.config.watch | true | Enable watching for ConfigMap changes |
fabric8.config.merge | false | Enable merge ConfigMap values in ConfigAdmin |
fabric8.config.meta | true | Enable injecting ConfigMap meta in ConfigAdmin bridge |
fabric8.pid.label | karaf.pid | Define the label the ConfigAdmin bridge looks for (that is, a ConfigMap that needs to be selected must have that label; the value of which determines to what PID it gets associated) |
fabric8.pid.filters | empty | Define additional conditions for the ConfigAdmin bridge to select a ConfigMap. The supported syntax is:
For example, a filter like -Dfabric8.pid.filters=appName=A;B,database.name=my.oracle.datasource translates to "give me all the ConfigMaps that have a label appName with values A or B and a label database.name equals to my.oracle.datasource". |
ConfigurationPlugin
requires Aries Blueprint CM 1.0.9
or above.
9.4.4. Fabric8 Karaf Blueprint Support
The fabric8-karaf-blueprint
uses Aries PropertyEvaluator and property placeholders resolvers from fabric8-karaf-core
to resolve placeholders in your Blueprint XML file.
To include the feature for blueprint support in your custom Karaf distribution, add fabric8-karaf-blueprint
to startupFeatures
in your project pom.xml
.
<startupFeatures> ... <feature>fabric8-karaf-blueprint</feature> ... </startupFeatures>
The fabric8 evaluator supports chained evaluators, such as ${env+service:MY_ENV_VAR}
. You need to resolve MY_ENV_VAR
variable against environment variables. The result is then resolved using service function. For example,
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.2.0" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.3.0 http://aries.apache.org/schemas/blueprint-ext/blueprint-ext-1.3.xsd"> <ext:property-placeholder evaluator="fabric8" placeholder-prefix="$[" placeholder-suffix="]"/> <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent"> <property name="userName" value="$[k8s:secret:$[env:ACTIVEMQ_SERVICE_NAME]/username]"/> <property name="password" value="$[k8s:secret:$[env:ACTIVEMQ_SERVICE_NAME]/password]"/> <property name="brokerURL" value="tcp://$[env+service:ACTIVEMQ_SERVICE_NAME]"/> </bean> </blueprint>
Nested property placeholder substitution requires Aries Blueprint Core 1.7.0
or above.
9.4.5. Fabric8 Karaf Health Checks
It is recommended to install the fabric8-karaf-checks
as a startup feature. Once enable, your Karaf server can expose http://0.0.0.0:8181/readiness-check
and http://0.0.0.0:8181/health-check
URLs which can be used by Kubernetes for readiness and liveness probes.
These URLs will only respond with a HTTP 200 status code when the following is true:
- OSGi Framework is started.
- All OSGi bundles are started.
- All boot features are installed.
- All deployed BluePrint bundles are in the created state.
- All deployed SCR bundles are in the active, registered or factory state.
- All web bundles are deployed to the web server.
- All created Camel contexts are in the started state.
You can add the Karaf health checks feature to the project pom.xml
using startupFeatures
.
pom.xml
<startupFeatures> ... <feature>fabric8-karaf-checks</feature> ... </startupFeatures>
The fabric8-maven-plugin:resources
goal will detect if your using the fabric8-karaf-checks
feature and automatically add the Kubernetes for readiness and liveness probes to your container’s configuration.
9.4.5.1. Adding Custom Heath Checks
You can provide additional custom heath checks to prevent the Karaf server from receiving user traffic before it is ready to process the requests. TO enable custom health checks you need to implement the io.fabric8.karaf.checks.HealthChecker
or io.fabric8.karaf.checks.ReadinessChecker
interfaces and register those objects in the OSGi registry.
Your project will need to add the following mvn dependency to the project pom.xml
file.
pom.xml
<dependency> <groupId>io.fabric8</groupId> <artifactId>fabric8-karaf-checks</artifactId> </dependency>
The simplest way to create and registered an object in the OSGi registry is to use SCR.
An example that performs a health check to make sure you have some free disk space, is shown below:
import io.fabric8.karaf.checks.*; import org.apache.felix.scr.annotations.*; import org.apache.commons.io.FileSystemUtils; import java.util.Collections; import java.util.List; @Component( name = "example.DiskChecker", immediate = true, enabled = true, policy = ConfigurationPolicy.IGNORE, createPid = false ) @Service({HealthChecker.class, ReadinessChecker.class}) public class DiskChecker implements HealthChecker, ReadinessChecker { public List<Check> getFailingReadinessChecks() { // lets just use the same checks for both readiness and health return getFailingHeathChecks(); } public List<Check> getFailingHealthChecks() { long free = FileSystemUtils.freeSpaceKb("/"); if (free < 1024 * 500) { return Collections.singletonList(new Check("disk-space-low", "Only " + free + "kb of disk space left.")); } return Collections.emptyList(); } }