-
Notifications
You must be signed in to change notification settings - Fork 135
UNOMI-919: Refactor the UNOMI startFeatures configuration to use a Karaf feature #742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jsinovassin
requested changes
Dec 23, 2025
...mands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java
Outdated
Show resolved
Hide resolved
jsinovassin
approved these changes
Dec 30, 2025
diff --git c/bom/artifacts/pom.xml i/bom/artifacts/pom.xml
index b0c0ec5b2..ecc78948d 100644
--- c/bom/artifacts/pom.xml
+++ i/bom/artifacts/pom.xml
@@ -60,6 +60,21 @@
<artifactId>unomi-persistence-elasticsearch-core</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-elasticsearch-conditions</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-opensearch-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-opensearch-conditions</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.unomi</groupId>
<artifactId>unomi-json-schema-services</artifactId>
@@ -243,6 +258,13 @@
<classifier>features</classifier>
<type>xml</type>
</dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-distribution</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git c/distribution/pom.xml i/distribution/pom.xml
new file mode 100644
index 000000000..0c469361a
--- /dev/null
+++ i/distribution/pom.xml
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-root</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>unomi-distribution</artifactId>
+ <name>Apache Unomi :: Distribution</name>
+ <description>Apache Unomi Distribution's Karaf features assemblies for the Apache Unomi Context Server</description>
+ <packaging>kar</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-bom</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-wab</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-metrics</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-scripting</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-elasticsearch-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-elasticsearch-conditions</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-opensearch-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-opensearch-conditions</artifactId>
+ </dependency>
+
+ <!-- plugins -->
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-plugins-base</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-plugins-request</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-plugins-mail</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-plugins-optimization-test</artifactId>
+ </dependency>
+
+ <!-- extensions -->
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>cxs-lists-extension-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>cxs-lists-extension-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>cxs-lists-extension-actions</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>cxs-geonames-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>cxs-geonames-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>cxs-privacy-extension-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>cxs-privacy-extension-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-json-schema-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-json-schema-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>shell-dev-commands</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-web-tracker-wab</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>joda-time</groupId>
+ <artifactId>joda-time</artifactId>
+ </dependency>
+ <!-- Apache HTTP Client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore-osgi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient-osgi</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <includeTransitiveDependency>false</includeTransitiveDependency>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <configuration>
+ <startLevel>85</startLevel>
+ </configuration>
+ <executions>
+ <execution>
+ <id>generate-features</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>features-generate-descriptor</goal>
+ </goals>
+ <configuration>
+ <enableGeneration>true</enableGeneration>
+ </configuration>
+ </execution>
+ <execution>
+ <id>verify</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>mvn:org.apache.karaf.features/standard/${karaf.version}/xml/features</descriptor>
+ <descriptor>mvn:org.apache.karaf.features/enterprise/${karaf.version}/xml/features</descriptor>
+ <descriptor>file:${project.build.directory}/feature/feature.xml</descriptor>
+ </descriptors>
+ <distribution>org.apache.karaf:apache-karaf:zip:${karaf.version}</distribution>
+ <javase>17</javase>
+ <framework>
+ <feature>framework</feature>
+ </framework>
+
+ <features>
+ <feature>unomi-distribution-opensearch</feature>
+ <feature>unomi-distribution-elasticsearch</feature>
+ </features>
+
+ <ignoreMissingConditions>false</ignoreMissingConditions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git c/docker/README.md i/docker/README.md
index 83cce28a2..ae1e4f93b 100644
--- c/docker/README.md
+++ i/docker/README.md
@@ -45,55 +45,75 @@ If you want to run it without docker-compose you should then make sure you setup
For ElasticSearch:
- docker pull docker.elastic.co/elasticsearch/elasticsearch:7.4.2
- docker network create unomi
- docker run --name elasticsearch --net unomi -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e cluster.name=contextElasticSearch docker.elastic.co/elasticsearch/elasticsearch:7.4.2
+```bash
+docker pull docker.elastic.co/elasticsearch/elasticsearch:9.2.1
+docker network create unomi
+docker run -d --name elasticsearch --net unomi -p 9200:9200 -p 9300:9300 \
+ -e "discovery.type=single-node" \
+ -e "xpack.security.enabled=false" \
+ -e cluster.name=contextElasticSearch \
+ docker.elastic.co/elasticsearch/elasticsearch:9.2.1
+```
For OpenSearch:
- docker pull opensearchproject/opensearch:3.0.0
- docker network create unomi
- export OPENSEARCH_ADMIN_PASSWORD=enter_your_custom_admin_password_here
- docker run --name opensearch --net unomi -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} opensearchproject/opensearch:3.0.0
+```bash
+docker pull opensearchproject/opensearch:3.0.0
+docker network create unomi
+export OPENSEARCH_ADMIN_PASSWORD=enter_your_custom_admin_password_here
+docker run -d --name opensearch --net unomi -p 9200:9200 -p 9300:9300 \
+ -e "discovery.type=single-node" \
+ -e OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \
+ opensearchproject/opensearch:3.0.0
+```
For Unomi (with ElasticSearch):
- docker pull apache/unomi:3.0.0-SNAPSHOT
- docker run --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
- -e UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 \
- apache/unomi:3.0.0-SNAPSHOT
+```bash
+docker pull apache/unomi:3.1.0-SNAPSHOT
+docker run -d --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
+ -e UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 \
+ apache/unomi:3.1.0-SNAPSHOT
+```
For Unomi (with OpenSearch):
- docker pull apache/unomi:3.0.0-SNAPSHOT
- docker run --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
- -e UNOMI_AUTO_START=opensearch \
- -e UNOMI_OPENSEARCH_ADDRESSES=opensearch:9200 \
- -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \
- apache/unomi:3.0.0-SNAPSHOT
+```bash
+docker pull apache/unomi:3.1.0-SNAPSHOT
+docker run -d --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
+ -e UNOMI_DISTRIBUTION=unomi-distribution-opensearch \
+ -e UNOMI_OPENSEARCH_ADDRESSES=opensearch:9200 \
+ -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD}
+ apache/unomi:3.1.0-SNAPSHOT
+```
## Using a host OS Search Engine installation (only supported on macOS & Windows)
For ElasticSearch:
- docker run --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
- -e UNOMI_ELASTICSEARCH_ADDRESSES=host.docker.internal:9200 \
- apache/unomi:3.0.0-SNAPSHOT
+```bash
+docker run -d --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
+ -e UNOMI_ELASTICSEARCH_ADDRESSES=host.docker.internal:9200 \
+ apache/unomi:3.1.0-SNAPSHOT
+```
For OpenSearch:
- docker run --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
- -e UNOMI_AUTO_START=opensearch \
- -e UNOMI_OPENSEARCH_ADDRESSES=host.docker.internal:9200 \
- -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \
- apache/unomi:3.0.0-SNAPSHOT
+```bash
+docker run -d --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \
+ -e UNOMI_DISTRIBUTION=unomi-distribution-opensearch
+ -e UNOMI_OPENSEARCH_ADDRESSES=host.docker.internal:9200 \
+ -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \
+ apache/unomi:3.1.0-SNAPSHOT
+```
Note: Linux doesn't support the host.docker.internal DNS lookup method yet, it should be available in an upcoming version of Docker. See https://github.com/docker/for-linux/issues/264
## Environment Variables
### Common Variables
-- `UNOMI_AUTO_START`: Specifies the search engine type (`elasticsearch` or `opensearch`, defaults to `elasticsearch`)
+- `UNOMI_AUTO_START`: Boolean to specify if unomi auto start with karaf (defaults to `true`)
+- `UNOMI_DISTRIBUTION`: Specifies the Unomi Distribution Feature to use (`unomi-distribution-elasticsearch` or `unomi-distribution-opensearch`, defaults to `unomi-distribution-elasticsearch`)
### ElasticSearch-specific Variables
- `UNOMI_ELASTICSEARCH_ADDRESSES`: ElasticSearch host:port (default: localhost:9200)
diff --git c/docker/src/main/docker/docker-compose-build-es.yml i/docker/src/main/docker/docker-compose-build-es.yml
index af71f7717..454e10364 100644
--- c/docker/src/main/docker/docker-compose-build-es.yml
+++ i/docker/src/main/docker/docker-compose-build-es.yml
@@ -17,7 +17,7 @@
version: '2.4'
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
+ image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1
volumes:
- unomi-3-elasticsearch-data:/usr/share/elasticsearch/data
environment:
@@ -35,7 +35,8 @@ services:
build: .
image: apache/unomi:${project.version}
environment:
- - UNOMI_AUTO_START=elasticsearch
+ - UNOMI_AUTO_START=true
+ - UNOMI_DISTRIBUTION=unomi-distribution-elasticsearch
- UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200
# Debug settings
- KARAF_DEBUG=${DEBUG:-false}
diff --git c/docker/src/main/docker/docker-compose-build-os.yml i/docker/src/main/docker/docker-compose-build-os.yml
index 0f736e004..fb2d27d27 100644
--- c/docker/src/main/docker/docker-compose-build-os.yml
+++ i/docker/src/main/docker/docker-compose-build-os.yml
@@ -95,7 +95,8 @@ services:
image: apache/unomi:${project.version}
container_name: unomi
environment:
- - UNOMI_AUTO_START=opensearch
+ - UNOMI_AUTO_START=true
+ - UNOMI_DISTRIBUTION=unomi-distribution-opensearch
- UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200
- UNOMI_OPENSEARCH_USERNAME=admin
- UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD}
diff --git c/docker/src/main/docker/docker-compose-es.yml i/docker/src/main/docker/docker-compose-es.yml
index 28e800b4b..43f3bbba5 100644
--- c/docker/src/main/docker/docker-compose-es.yml
+++ i/docker/src/main/docker/docker-compose-es.yml
@@ -23,7 +23,7 @@ networks:
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
+ image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1
container_name: elasticsearch
volumes:
- unomi-3-elasticsearch-data:/usr/share/elasticsearch/data
@@ -41,7 +41,8 @@ services:
node-1:
image: apache/unomi:${project.version}
environment:
- - UNOMI_AUTO_START=elasticsearch
+ - UNOMI_AUTO_START=true
+ - UNOMI_DISTRIBUTION=unomi-distribution-elasticsearch
- UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200
# Debug settings
- KARAF_DEBUG=${DEBUG:-false}
diff --git c/docker/src/main/docker/docker-compose-os.yml i/docker/src/main/docker/docker-compose-os.yml
index cf229dedf..03abe5263 100644
--- c/docker/src/main/docker/docker-compose-os.yml
+++ i/docker/src/main/docker/docker-compose-os.yml
@@ -79,7 +79,8 @@ services:
image: apache/unomi:${project.version}
container_name: unomi
environment:
- - UNOMI_AUTO_START=opensearch
+ - UNOMI_AUTO_START=true
+ - UNOMI_DISTRIBUTION=unomi-distribution-opensearch
- UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200
- UNOMI_OPENSEARCH_USERNAME=admin
- UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD}
diff --git c/docker/src/main/docker/entrypoint.sh i/docker/src/main/docker/entrypoint.sh
index 275f0edb0..1cfc68456 100755
--- c/docker/src/main/docker/entrypoint.sh
+++ i/docker/src/main/docker/entrypoint.sh
@@ -29,14 +29,9 @@ if [ "$KARAF_DEBUG" = "true" ]; then
export KARAF_DEBUG=true
fi
-# Determine search engine type from UNOMI_AUTO_START
-SEARCH_ENGINE="${UNOMI_AUTO_START:-elasticsearch}"
-export KARAF_OPTS="-Dunomi.autoStart=${UNOMI_AUTO_START}"
+UNOMI_DISTRIBUTION="${UNOMI_DISTRIBUTION:-unomi-distribution-elasticsearch}"
+export KARAF_OPTS="-Dunomi.autoStart=${UNOMI_AUTO_START} -Dunomi.distribution=${UNOMI_DISTRIBUTION}"
-if [ "$SEARCH_ENGINE" = "true" ]; then
- SEARCH_ENGINE="elasticsearch"
-fi
-echo "SEARCH_ENGINE: $SEARCH_ENGINE"
echo "KARAF_OPTS: $KARAF_OPTS"
# Function to check cluster health for a specific node
@@ -51,9 +46,11 @@ check_node_health() {
fi
}
-# Configure connection parameters based on search engine type
-if [ "$SEARCH_ENGINE" = "opensearch" ]; then
+# Configure connection parameters based on distribution name
+WAIT_SEARCH_ENGINE=true
+if [[ "$UNOMI_DISTRIBUTION" == *opensearch* ]]; then
# OpenSearch configuration
+ SEARCH_ENGINE="opensearch"
if [ -z "$UNOMI_OPENSEARCH_PASSWORD" ]; then
echo "Error: UNOMI_OPENSEARCH_PASSWORD must be set when using OpenSearch"
exit 1
@@ -65,8 +62,9 @@ if [ "$SEARCH_ENGINE" = "opensearch" ]; then
curl_opts="-k -H \"${auth_header}\" -H \"Content-Type: application/json\""
# Build array of node URLs
IFS=',' read -ra NODES <<< "${UNOMI_OPENSEARCH_ADDRESSES}"
-else
+elif [[ "$UNOMI_DISTRIBUTION" == *elasticsearch* ]]; then
# Elasticsearch configuration
+ SEARCH_ENGINE="elasticsearch"
if [ "$UNOMI_ELASTICSEARCH_SSL_ENABLE" = 'true' ]; then
schema='https'
else
@@ -80,43 +78,49 @@ else
health_endpoint="_cluster/health"
# Build array of node URLs
IFS=',' read -ra NODES <<< "${UNOMI_ELASTICSEARCH_ADDRESSES}"
+else
+ WAIT_SEARCH_ENGINE=false
+ echo "WARNING: unable to determine search engine from distribution name: $UNOMI_DISTRIBUTION"
+ echo " Skipping waiting for engine to startup before starting Karaf, ensure it is expected"
fi
-# Wait for search engine to be ready
-echo "Waiting for ${SEARCH_ENGINE} to be ready..."
-echo "Checking nodes: ${NODES[@]}"
-health_check=""
+# Wait for search engine to be ready (only if enabled)
+if [ "$WAIT_SEARCH_ENGINE" = true ]; then
+ echo "Waiting for ${SEARCH_ENGINE} to be ready..."
+ echo "Checking nodes: ${NODES[@]}"
+ health_check=""
-while ([ -z "$health_check" ] || ([ "$health_check" != 'yellow' ] && [ "$health_check" != 'green' ])); do
- # Try each node until we get a successful response
- for node in "${NODES[@]}"; do
- node_url="${schema}://${node}/${health_endpoint}"
- echo "Checking health at: ${node_url}"
- health_check=$(check_node_health "$node_url" "$curl_opts")
+ while ([ -z "$health_check" ] || ([ "$health_check" != 'yellow' ] && [ "$health_check" != 'green' ])); do
+ # Try each node until we get a successful response
+ for node in "${NODES[@]}"; do
+ node_url="${schema}://${node}/${health_endpoint}"
+ echo "Checking health at: ${node_url}"
+ health_check=$(check_node_health "$node_url" "$curl_opts")
- if [ ! -z "$health_check" ]; then
- echo "Successfully connected to node: $node (status: ${health_check})"
- break
+ if [ ! -z "$health_check" ]; then
+ echo "Successfully connected to node: $node (status: ${health_check})"
+ break
+ else
+ >&2 echo "Connection failed to node: $node"
+ fi
+ done
+
+ if [ -z "$health_check" ]; then
+ >&2 echo "${SEARCH_ENGINE^} is not yet available - all nodes unreachable"
+ sleep 3
+ continue
+ fi
+
+ if [ "$health_check" != 'yellow' ] && [ "$health_check" != 'green' ]; then
+ >&2 echo "${SEARCH_ENGINE^} health status: ${health_check} (waiting for yellow or green)"
+ sleep 3
else
- >&2 echo "Connection failed to node: $node"
+ >&2 echo "${SEARCH_ENGINE^} health status: ${health_check}"
fi
done
- if [ -z "$health_check" ]; then
- >&2 echo "${SEARCH_ENGINE^} is not yet available - all nodes unreachable"
- sleep 3
- continue
- fi
-
- if [ "$health_check" != 'yellow' ] && [ "$health_check" != 'green' ]; then
- >&2 echo "${SEARCH_ENGINE^} health status: ${health_check} (waiting for yellow or green)"
- sleep 3
- else
- >&2 echo "${SEARCH_ENGINE^} health status: ${health_check}"
- fi
-done
-
-echo "${SEARCH_ENGINE^} is ready with health status: ${health_check}"
+ echo "${SEARCH_ENGINE^} is ready with health status: ${health_check}"
+fi
# Run Unomi in current bash session
exec "$UNOMI_HOME/bin/karaf" run
diff --git c/extensions/healthcheck/pom.xml i/extensions/healthcheck/pom.xml
index e16bd9ac7..e1fca175d 100644
--- c/extensions/healthcheck/pom.xml
+++ i/extensions/healthcheck/pom.xml
@@ -150,10 +150,17 @@
<artifacts>
<artifact>
<file>
- src/main/resources/org.apache.unomi.healthcheck.cfg
+ src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg
</file>
<type>cfg</type>
- <classifier>healthcheck</classifier>
+ <classifier>healthcheck-elasticsearch</classifier>
+ </artifact>
+ <artifact>
+ <file>
+ src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg
+ </file>
+ <type>cfg</type>
+ <classifier>healthcheck-opensearch</classifier>
</artifact>
</artifacts>
</configuration>
diff --git c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java i/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java
index 278eb8a1e..b07f279e7 100644
--- c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java
+++ i/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java
@@ -30,9 +30,14 @@ import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.unomi.healthcheck.HealthCheckConfig;
+import org.apache.unomi.healthcheck.HealthCheckProvider;
import org.apache.unomi.healthcheck.HealthCheckResponse;
import org.apache.unomi.healthcheck.util.CachedValue;
import org.apache.unomi.shell.migration.utils.HttpUtils;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,13 +48,17 @@ import java.util.concurrent.TimeUnit;
* A Health Check that checks the status of the ElasticSearch connectivity according to the provided configuration.
* This connectivity should be LIVE before any try to start Unomi.
*/
-public class ElasticSearchHealthCheckProvider implements PersistenceEngineHealthProvider {
+@Component(service = HealthCheckProvider.class, immediate = true)
+public class ElasticSearchHealthCheckProvider implements HealthCheckProvider {
public static final String NAME = "elasticsearch";
private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchHealthCheckProvider.class.getName());
private final CachedValue<HealthCheckResponse> cache = new CachedValue<>(10, TimeUnit.SECONDS);
+ private final ObjectMapper mapper = new ObjectMapper();
+ private boolean isActive = false;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
private HealthCheckConfig config;
private CloseableHttpClient httpClient;
@@ -58,21 +67,25 @@ public class ElasticSearchHealthCheckProvider implements PersistenceEngineHealth
LOGGER.info("Building elasticsearch health provider service...");
}
+ @Activate
public void activate() {
- LOGGER.info("Activating elasticsearch health provider service...");
- CredentialsProvider credentialsProvider = null;
- String login = config.get(HealthCheckConfig.CONFIG_ES_LOGIN);
- if (StringUtils.isNotEmpty(login)) {
- credentialsProvider = new BasicCredentialsProvider();
- UsernamePasswordCredentials credentials
- = new UsernamePasswordCredentials(login, config.get(HealthCheckConfig.CONFIG_ES_PASSWORD));
- credentialsProvider.setCredentials(AuthScope.ANY, credentials);
- }
- try {
- httpClient = HttpUtils.initHttpClient(
- Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_ES_TRUST_ALL_CERTIFICATES)), credentialsProvider);
- } catch (IOException e) {
- LOGGER.error("Unable to initialize http client", e);
+ if (config.isEnabled() && config.getEnabledProviders().stream().anyMatch(NAME::equals)) {
+ LOGGER.info("Activating elasticsearch health provider service...");
+ this.isActive = true;
+ CredentialsProvider credentialsProvider = null;
+ String login = config.get(HealthCheckConfig.CONFIG_ES_LOGIN);
+ if (StringUtils.isNotEmpty(login)) {
+ credentialsProvider = new BasicCredentialsProvider();
+ UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(login,
+ config.get(HealthCheckConfig.CONFIG_ES_PASSWORD));
+ credentialsProvider.setCredentials(AuthScope.ANY, credentials);
+ }
+ try {
+ httpClient = HttpUtils.initHttpClient(Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_ES_TRUST_ALL_CERTIFICATES)),
+ credentialsProvider);
+ } catch (IOException e) {
+ LOGGER.error("Unable to initialize http client", e);
+ }
}
}
@@ -92,56 +105,64 @@ public class ElasticSearchHealthCheckProvider implements PersistenceEngineHealth
return cache.getValue();
}
- @Override public HealthCheckResponse detailed() {
- return execute();
- }
-
private HealthCheckResponse refresh() {
LOGGER.debug("Refresh");
HealthCheckResponse.Builder builder = new HealthCheckResponse.Builder();
builder.name(NAME).down();
- String url = (config.get(HealthCheckConfig.CONFIG_ES_SSL_ENABLED).equals("true") ? "https://" : "http://")
- .concat(config.get(HealthCheckConfig.CONFIG_ES_ADDRESSES).split(",")[0].trim())
- .concat("/_cluster/health");
- CloseableHttpResponse response = null;
- try {
- response = httpClient.execute(new HttpGet(url));
- if (response != null && response.getStatusLine().getStatusCode() == 200) {
- builder.up();
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- String content = EntityUtils.toString(entity);
- try {
- ObjectMapper mapper = new ObjectMapper();
- JsonNode root = mapper.readTree(content);
- if (root.has("status") && "green".equals(root.get("status").asText())) {
- builder.live();
- }
- if (root.has("cluster_name")) builder.withData("cluster_name", root.get("cluster_name").asText());
- if (root.has("status")) builder.withData("status", root.get("status").asText());
- if (root.has("timed_out")) builder.withData("timed_out", root.get("timed_out").asBoolean());
- if (root.has("number_of_nodes")) builder.withData("number_of_nodes", root.get("number_of_nodes").asLong());
- if (root.has("number_of_data_nodes")) builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong());
- if (root.has("active_primary_shards")) builder.withData("active_primary_shards", root.get("active_primary_shards").asLong());
- if (root.has("active_shards")) builder.withData("active_shards", root.get("active_shards").asLong());
- if (root.has("relocating_shards")) builder.withData("relocating_shards", root.get("relocating_shards").asLong());
- if (root.has("initializing_shards")) builder.withData("initializing_shards", root.get("initializing_shards").asLong());
- if (root.has("unassigned_shards")) builder.withData("unassigned_shards", root.get("unassigned_shards").asLong());
- } catch (Exception parseEx) {
- // Fallback to simple LIVE detection
- if (content.contains("\"status\":\"green\"")) {
- builder.live();
+ if (isActive) {
+ String url = (config.get(HealthCheckConfig.CONFIG_ES_SSL_ENABLED).equals("true") ? "https://" : "http://").concat(
+ config.get(HealthCheckConfig.CONFIG_ES_ADDRESSES).split(",")[0].trim()).concat("/_cluster/health");
+ CloseableHttpResponse response = null;
+ try {
+ response = httpClient.execute(new HttpGet(url));
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ builder.up();
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ String content = EntityUtils.toString(entity);
+ try {
+ JsonNode root = mapper.readTree(content);
+ if (root.has("status") && "green".equals(root.get("status").asText())) {
+ builder.live();
+ }
+ if (root.has("cluster_name"))
+ builder.withData("cluster_name", root.get("cluster_name").asText());
+ if (root.has("status"))
+ builder.withData("status", root.get("status").asText());
+ if (root.has("timed_out"))
+ builder.withData("timed_out", root.get("timed_out").asBoolean());
+ if (root.has("number_of_nodes"))
+ builder.withData("number_of_nodes", root.get("number_of_nodes").asLong());
+ if (root.has("number_of_data_nodes"))
+ builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong());
+ if (root.has("active_primary_shards"))
+ builder.withData("active_primary_shards", root.get("active_primary_shards").asLong());
+ if (root.has("active_shards"))
+ builder.withData("active_shards", root.get("active_shards").asLong());
+ if (root.has("relocating_shards"))
+ builder.withData("relocating_shards", root.get("relocating_shards").asLong());
+ if (root.has("initializing_shards"))
+ builder.withData("initializing_shards", root.get("initializing_shards").asLong());
+ if (root.has("unassigned_shards"))
+ builder.withData("unassigned_shards", root.get("unassigned_shards").asLong());
+ } catch (Exception parseEx) {
+ // Fallback to simple LIVE detection
+ if (content.contains("\"status\":\"green\"")) {
+ builder.live();
+ }
}
}
}
+ } catch (IOException e) {
+ builder.error().withData("error", e.getMessage());
+ LOGGER.error("Error while checking elasticsearch health", e);
+ } finally {
+ if (response != null) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ }
}
- } catch (IOException e) {
- builder.error().withData("error", e.getMessage());
- LOGGER.error("Error while checking elasticsearch health", e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ } else {
+ builder.error().withData("error", "Elasticsearch health check provider is not active");
}
return builder.build();
}
diff --git c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java i/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java
index 7b97cfa12..f64f0df84 100644
--- c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java
+++ i/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java
@@ -30,9 +30,14 @@ import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.unomi.healthcheck.HealthCheckConfig;
+import org.apache.unomi.healthcheck.HealthCheckProvider;
import org.apache.unomi.healthcheck.HealthCheckResponse;
import org.apache.unomi.healthcheck.util.CachedValue;
import org.apache.unomi.shell.migration.utils.HttpUtils;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,13 +48,17 @@ import java.util.concurrent.TimeUnit;
* A Health Check that checks the status of the OpenSearch connectivity according to the provided configuration.
* This connectivity should be LIVE before any try to start Unomi.
*/
-public class OpenSearchHealthCheckProvider implements PersistenceEngineHealthProvider {
+@Component(service = HealthCheckProvider.class, immediate = true)
+public class OpenSearchHealthCheckProvider implements HealthCheckProvider {
public static final String NAME = "opensearch";
private static final Logger LOGGER = LoggerFactory.getLogger(OpenSearchHealthCheckProvider.class.getName());
private final CachedValue<HealthCheckResponse> cache = new CachedValue<>(10, TimeUnit.SECONDS);
+ private final ObjectMapper mapper = new ObjectMapper();
+ private boolean isActive = false;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
private HealthCheckConfig config;
private CloseableHttpClient httpClient;
@@ -58,21 +67,25 @@ public class OpenSearchHealthCheckProvider implements PersistenceEngineHealthPro
LOGGER.info("Building OpenSearch health provider service...");
}
+ @Activate
public void activate() {
- LOGGER.info("Activating OpenSearch health provider service...");
- CredentialsProvider credentialsProvider = null;
- String login = config.get(HealthCheckConfig.CONFIG_OS_LOGIN); // Reuse ElasticSearch credentials key
- if (StringUtils.isNotEmpty(login)) {
- credentialsProvider = new BasicCredentialsProvider();
- UsernamePasswordCredentials credentials
- = new UsernamePasswordCredentials(login, config.get(HealthCheckConfig.CONFIG_OS_PASSWORD));
- credentialsProvider.setCredentials(AuthScope.ANY, credentials);
- }
- try {
- httpClient = HttpUtils.initHttpClient(
- Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_OS_TRUST_ALL_CERTIFICATES)), credentialsProvider);
- } catch (IOException e) {
- LOGGER.error("Unable to initialize http client", e);
+ if (config.isEnabled() && config.getEnabledProviders().stream().anyMatch(NAME::equals)) {
+ LOGGER.info("Activating OpenSearch health provider service...");
+ this.isActive = true;
+ CredentialsProvider credentialsProvider = null;
+ String login = config.get(HealthCheckConfig.CONFIG_OS_LOGIN); // Reuse ElasticSearch credentials key
+ if (StringUtils.isNotEmpty(login)) {
+ credentialsProvider = new BasicCredentialsProvider();
+ UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(login,
+ config.get(HealthCheckConfig.CONFIG_OS_PASSWORD));
+ credentialsProvider.setCredentials(AuthScope.ANY, credentials);
+ }
+ try {
+ httpClient = HttpUtils.initHttpClient(Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_OS_TRUST_ALL_CERTIFICATES)),
+ credentialsProvider);
+ } catch (IOException e) {
+ LOGGER.error("Unable to initialize http client", e);
+ }
}
}
@@ -92,63 +105,72 @@ public class OpenSearchHealthCheckProvider implements PersistenceEngineHealthPro
return cache.getValue();
}
- @Override public HealthCheckResponse detailed() {
- return execute();
- }
-
private HealthCheckResponse refresh() {
LOGGER.debug("Refresh");
HealthCheckResponse.Builder builder = new HealthCheckResponse.Builder();
builder.name(NAME).down();
- String minimalClusterState = config.get(HealthCheckConfig.CONFIG_OS_MINIMAL_CLUSTER_STATE);
- if (StringUtils.isEmpty(minimalClusterState)) {
- minimalClusterState = "green";
- } else {
- minimalClusterState = minimalClusterState.toLowerCase();
- }
- String url = (config.get(HealthCheckConfig.CONFIG_OS_SSL_ENABLED).equals("true") ? "https://" : "http://")
- .concat(config.get(HealthCheckConfig.CONFIG_OS_ADDRESSES).split(",")[0].trim())
- .concat("/_cluster/health");
- CloseableHttpResponse response = null;
- try {
- response = httpClient.execute(new HttpGet(url));
- if (response != null && response.getStatusLine().getStatusCode() == 200) {
- builder.up();
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- String content = EntityUtils.toString(entity);
- try {
- ObjectMapper mapper = new ObjectMapper();
- JsonNode root = mapper.readTree(content);
- String status = root.has("status") ? root.get("status").asText() : null;
- if ("green".equals(status) || ("yellow".equals(status) && "yellow".equals(minimalClusterState))) {
- builder.live();
- }
- if (root.has("cluster_name")) builder.withData("cluster_name", root.get("cluster_name").asText());
- if (root.has("status")) builder.withData("status", root.get("status").asText());
- if (root.has("timed_out")) builder.withData("timed_out", root.get("timed_out").asBoolean());
- if (root.has("number_of_nodes")) builder.withData("number_of_nodes", root.get("number_of_nodes").asLong());
- if (root.has("number_of_data_nodes")) builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong());
- if (root.has("active_primary_shards")) builder.withData("active_primary_shards", root.get("active_primary_shards").asLong());
- if (root.has("active_shards")) builder.withData("active_shards", root.get("active_shards").asLong());
- if (root.has("relocating_shards")) builder.withData("relocating_shards", root.get("relocating_shards").asLong());
- if (root.has("initializing_shards")) builder.withData("initializing_shards", root.get("initializing_shards").asLong());
- if (root.has("unassigned_shards")) builder.withData("unassigned_shards", root.get("unassigned_shards").asLong());
- } catch (Exception parseEx) {
- if (content.contains("\"status\":\"green\"") ||
- (content.contains("\"status\":\"yellow\"") && "yellow".equals(minimalClusterState))) {
- builder.live();
+ if (isActive) {
+ String minimalClusterState = config.get(HealthCheckConfig.CONFIG_OS_MINIMAL_CLUSTER_STATE);
+ if (StringUtils.isEmpty(minimalClusterState)) {
+ minimalClusterState = "green";
+ } else {
+ minimalClusterState = minimalClusterState.toLowerCase();
+ }
+ String url = (config.get(HealthCheckConfig.CONFIG_OS_SSL_ENABLED).equals("true") ? "https://" : "http://").concat(
+ config.get(HealthCheckConfig.CONFIG_OS_ADDRESSES).split(",")[0].trim()).concat("/_cluster/health");
+ CloseableHttpResponse response = null;
+ try {
+ response = httpClient.execute(new HttpGet(url));
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ builder.up();
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ String content = EntityUtils.toString(entity);
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode root = mapper.readTree(content);
+ String status = root.has("status") ? root.get("status").asText() : null;
+ if ("green".equals(status) || ("yellow".equals(status) && "yellow".equals(minimalClusterState))) {
+ builder.live();
+ }
+ if (root.has("cluster_name"))
+ builder.withData("cluster_name", root.get("cluster_name").asText());
+ if (root.has("status"))
+ builder.withData("status", root.get("status").asText());
+ if (root.has("timed_out"))
+ builder.withData("timed_out", root.get("timed_out").asBoolean());
+ if (root.has("number_of_nodes"))
+ builder.withData("number_of_nodes", root.get("number_of_nodes").asLong());
+ if (root.has("number_of_data_nodes"))
+ builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong());
+ if (root.has("active_primary_shards"))
+ builder.withData("active_primary_shards", root.get("active_primary_shards").asLong());
+ if (root.has("active_shards"))
+ builder.withData("active_shards", root.get("active_shards").asLong());
+ if (root.has("relocating_shards"))
+ builder.withData("relocating_shards", root.get("relocating_shards").asLong());
+ if (root.has("initializing_shards"))
+ builder.withData("initializing_shards", root.get("initializing_shards").asLong());
+ if (root.has("unassigned_shards"))
+ builder.withData("unassigned_shards", root.get("unassigned_shards").asLong());
+ } catch (Exception parseEx) {
+ if (content.contains("\"status\":\"green\"") || (content.contains("\"status\":\"yellow\"") && "yellow".equals(
+ minimalClusterState))) {
+ builder.live();
+ }
}
}
}
+ } catch (IOException e) {
+ builder.error().withData("error", e.getMessage());
+ LOGGER.error("Error while checking OpenSearch health", e);
+ } finally {
+ if (response != null) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ }
}
- } catch (IOException e) {
- builder.error().withData("error", e.getMessage());
- LOGGER.error("Error while checking OpenSearch health", e);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- }
+ } else {
+ builder.error().withData("error", "Elasticsearch health check provider is not active");
}
return builder.build();
}
diff --git c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceEngineHealthProvider.java i/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceEngineHealthProvider.java
deleted file mode 100644
index 276ba2272..000000000
--- c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceEngineHealthProvider.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.unomi.healthcheck.provider;
-
-import org.apache.unomi.healthcheck.HealthCheckProvider;
-import org.apache.unomi.healthcheck.HealthCheckResponse;
-
-/**
- * Common contract for persistence engine health providers to expose
- * richer, implementation-specific health details.
- */
-public interface PersistenceEngineHealthProvider extends HealthCheckProvider {
-
- /**
- * Build a detailed response that may include implementation-specific data.
- *
- * @return a detailed {@link HealthCheckResponse}
- */
- HealthCheckResponse detailed();
-}
-
-
diff --git c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java i/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java
index ec136fa33..b0d2725dc 100644
--- c/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java
+++ i/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java
@@ -20,7 +20,6 @@ package org.apache.unomi.healthcheck.provider;
import org.apache.unomi.api.PropertyType;
import org.apache.unomi.healthcheck.HealthCheckResponse;
import org.apache.unomi.healthcheck.HealthCheckProvider;
-import org.apache.unomi.healthcheck.HealthCheckConfig;
import org.apache.unomi.healthcheck.util.CachedValue;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.osgi.service.component.annotations.Component;
@@ -47,25 +46,16 @@ public class PersistenceHealthCheckProvider implements HealthCheckProvider {
@Reference(service = PersistenceService.class, cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, bind = "bind", unbind = "unbind")
private volatile PersistenceService service;
- @Reference(cardinality = ReferenceCardinality.OPTIONAL)
- private volatile HealthCheckConfig healthCheckConfig;
-
- // Lazily created delegate depending on the current persistence implementation
- private volatile HealthCheckProvider delegate;
-
public PersistenceHealthCheckProvider() {
LOGGER.info("Building persistence health provider service...");
}
public void bind(PersistenceService service) {
this.service = service;
- // Reset delegate when persistence changes
- this.delegate = null;
}
public void unbind(PersistenceService service) {
this.service = null;
- this.delegate = null;
}
@Override public String name() {
@@ -74,17 +64,6 @@ public class PersistenceHealthCheckProvider implements HealthCheckProvider {
@Override public HealthCheckResponse execute() {
LOGGER.debug("Health check persistence");
-
- // If we can detect the underlying persistence, delegate to the appropriate provider
- HealthCheckProvider resolved = resolveDelegate();
- if (resolved != null) {
- if (resolved instanceof PersistenceEngineHealthProvider) {
- return ((PersistenceEngineHealthProvider) resolved).detailed();
- }
- return resolved.execute();
- }
-
- // Fallback to legacy behavior if no delegate is available yet
if (cache.isStaled() || cache.getValue().isDown() || cache.getValue().isError()) {
cache.setValue(refresh());
}
@@ -109,49 +88,4 @@ public class PersistenceHealthCheckProvider implements HealthCheckProvider {
}
return builder.build();
}
-
- private HealthCheckProvider resolveDelegate() {
- try {
- if (delegate != null) {
- return delegate;
- }
- if (service == null) {
- return null;
- }
- String persistenceName;
- try {
- persistenceName = service.getName();
- } catch (Throwable t) {
- // Older SPI might not expose getName(); fallback to class inspection
- persistenceName = service.getClass().getName().toLowerCase();
- }
-
- if (persistenceName == null) {
- return null;
- }
-
- if (persistenceName.contains("opensearch")) {
- OpenSearchHealthCheckProvider provider = new OpenSearchHealthCheckProvider();
- if (healthCheckConfig != null) {
- provider.setConfig(healthCheckConfig);
- }
- provider.activate();
- delegate = provider;
- } else if (persistenceName.contains("elasticsearch")) {
- ElasticSearchHealthCheckProvider provider = new ElasticSearchHealthCheckProvider();
- if (healthCheckConfig != null) {
- provider.setConfig(healthCheckConfig);
- }
- provider.activate();
- delegate = provider;
- } else {
- // Unknown persistence implementation, no delegate
- return null;
- }
- return delegate;
- } catch (Exception e) {
- LOGGER.warn("Unable to resolve delegated health check provider", e);
- return null;
- }
- }
}
diff --git c/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg i/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg
new file mode 100644
index 000000000..20a8c2a6b
--- /dev/null
+++ i/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Elasticsearch configuration
+esAddresses = ${org.apache.unomi.elasticsearch.addresses:-localhost:9200}
+esSSLEnabled = ${org.apache.unomi.elasticsearch.sslEnable:-false}
+esLogin = ${org.apache.unomi.elasticsearch.username:-}
+esPassword = ${org.apache.unomi.elasticsearch.password:-}
+esHttpClient.trustAllCertificates = ${org.apache.unomi.elasticsearch.sslTrustAllCertificates:-false}
+
+# Security configuration
+authentication.realm = ${org.apache.unomi.security.realm:-karaf}
+
+# Health check configuration
+healthcheck.enabled = ${org.apache.unomi.healthcheck.enabled:-false}
+healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,unomi,persistence}
+healthcheck.timeout = ${org.apache.unomi.healthcheck.timeout:-400}
diff --git c/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg i/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg
similarity index 78%
rename from extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg
rename to extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg
index 9c6083ab9..98fef8fdf 100644
--- c/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg
+++ i/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg
@@ -15,13 +15,6 @@
# limitations under the License.
#
-# Elasticsearch configuration
-esAddresses = ${org.apache.unomi.elasticsearch.addresses:-localhost:9200}
-esSSLEnabled = ${org.apache.unomi.elasticsearch.sslEnable:-false}
-esLogin = ${org.apache.unomi.elasticsearch.username:-}
-esPassword = ${org.apache.unomi.elasticsearch.password:-}
-esHttpClient.trustAllCertificates = ${org.apache.unomi.elasticsearch.sslTrustAllCertificates:-false}
-
# OpenSearch configuration
osAddresses = ${org.apache.unomi.opensearch.addresses:-localhost:9200}
osSSLEnabled = ${org.apache.unomi.opensearch.sslEnable:-true}
@@ -35,5 +28,5 @@ authentication.realm = ${org.apache.unomi.security.realm:-karaf}
# Health check configuration
healthcheck.enabled = ${org.apache.unomi.healthcheck.enabled:-false}
-healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,opensearch,unomi,persistence}
+healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,opensearch,unomi,persistence}
healthcheck.timeout = ${org.apache.unomi.healthcheck.timeout:-400}
diff --git c/itests/src/test/java/org/apache/unomi/itests/BaseIT.java i/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
index d7b6de159..9bb734c91 100644
--- c/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
+++ i/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
@@ -182,7 +182,8 @@ public abstract class BaseIT extends KarafTestSupport {
} else if (SEARCH_ENGINE_OPENSEARCH.equals(searchEngine)){
LOGGER.info("Starting Unomi with opensearch search engine...");
System.out.println("==== Starting Unomi with opensearch search engine...");
- executeCommand("unomi:start " + SEARCH_ENGINE_OPENSEARCH);
+ executeCommand("unomi:setup -d unomi-distribution-opensearch --force true");
+ executeCommand("unomi:start");
} else {
LOGGER.error("Unknown search engine: " + searchEngine);
throw new InterruptedException("Unknown search engine: " + searchEngine);
@@ -306,7 +307,7 @@ public abstract class BaseIT extends KarafTestSupport {
"unomi-shell-dev-commands",
"unomi-wab",
"unomi-web-tracker",
- "unomi-healthcheck",
+ "unomi-healthcheck-elasticsearch",
"unomi-router-karaf-feature",
"unomi-groovy-actions",
"unomi-rest-ui",
@@ -332,7 +333,7 @@ public abstract class BaseIT extends KarafTestSupport {
"unomi-shell-dev-commands",
"unomi-wab",
"unomi-web-tracker",
- "unomi-healthcheck",
+ "unomi-healthcheck-opensearch",
"unomi-router-karaf-feature",
"unomi-groovy-actions",
"unomi-rest-ui",
diff --git c/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java i/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java
index 882259525..d68bcd6f1 100644
--- c/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java
+++ i/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java
@@ -59,10 +59,11 @@ public class HealthCheckIT extends BaseIT {
List<HealthCheckResponse> response = get(HEALTHCHECK_ENDPOINT, new TypeReference<>() {});
LOGGER.info("health check response: {}", response);
Assert.assertNotNull(response);
- Assert.assertEquals(4, response.size());
+ Assert.assertEquals(5, response.size());
Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("karaf") && r.getStatus() == HealthCheckResponse.Status.LIVE));
Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals(searchEngine) && r.getStatus() == HealthCheckResponse.Status.LIVE));
Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("unomi") && r.getStatus() == HealthCheckResponse.Status.LIVE));
+ Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("persistence") && r.getStatus() == HealthCheckResponse.Status.LIVE));
Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("cluster") && r.getStatus() == HealthCheckResponse.Status.LIVE));
} catch (Exception e) {
LOGGER.error("Error while executing health check", e);
diff --git c/itests/src/test/resources/org.apache.unomi.healthcheck.cfg i/itests/src/test/resources/org.apache.unomi.healthcheck.cfg
index 9de18615b..4f5e824ea 100644
--- c/itests/src/test/resources/org.apache.unomi.healthcheck.cfg
+++ i/itests/src/test/resources/org.apache.unomi.healthcheck.cfg
@@ -35,5 +35,5 @@ authentication.realm = ${org.apache.unomi.security.realm:-karaf}
# Health check configuration
healthcheck.enabled = ${org.apache.unomi.healthcheck.enabled:-true}
-healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,opensearch,unomi,persistence}
+healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,unomi,persistence}
healthcheck.timeout = ${org.apache.unomi.healthcheck.timeout:-400}
diff --git c/kar/src/main/feature/feature.xml i/kar/src/main/feature/feature.xml
index d4f83fe94..ed3d3a483 100644
--- c/kar/src/main/feature/feature.xml
+++ i/kar/src/main/feature/feature.xml
@@ -75,7 +75,6 @@
<feature name="unomi-startup" description="Apache Unomi :: Startup Tools" version="${project.version}">
<feature>unomi-base</feature>
<configfile finalname="/etc/org.apache.unomi.migration.cfg">mvn:org.apache.unomi/shell-commands/${project.version}/cfg/migration</configfile>
- <configfile finalname="/etc/org.apache.unomi.start.cfg">mvn:org.apache.unomi/shell-commands/${project.version}/cfg/start</configfile>
<bundle>mvn:org.apache.unomi/shell-commands/${project.version}</bundle>
</feature>
@@ -184,9 +183,15 @@
<bundle start="false">mvn:org.apache.unomi/unomi-web-tracker-wab/${project.version}</bundle>
</feature>
- <feature name="unomi-healthcheck" description="Apache Unomi :: Healthcheck" version="${project.version}">
+ <feature name="unomi-healthcheck-elasticsearch" description="Apache Unomi :: Healthcheck ElasticSearch" version="${project.version}">
<feature>unomi-web-tracker</feature>
- <configfile finalname="/etc/org.apache.unomi.healthcheck.cfg">mvn:org.apache.unomi/healthcheck/${project.version}/cfg/healthcheck</configfile>
+ <configfile finalname="/etc/org.apache.unomi.healthcheck.cfg">mvn:org.apache.unomi/healthcheck/${project.version}/cfg/healthcheck-elasticsearch</configfile>
+ <bundle start="false">mvn:org.apache.unomi/healthcheck/${project.version}</bundle>
+ </feature>
+
+ <feature name="unomi-healthcheck-opensearch" description="Apache Unomi :: Healthcheck OpenSearch" version="${project.version}">
+ <feature>unomi-web-tra…
…e bundle propose it's own cfg file.
diff --git c/manual/src/main/asciidoc/configuration.adoc i/manual/src/main/asciidoc/configuration.adoc index b3d97a9..4d923ad 100644 --- c/manual/src/main/asciidoc/configuration.adoc +++ i/manual/src/main/asciidoc/configuration.adoc @@ -876,60 +876,143 @@ The following permissions are required by Unomi: - required cluster privileges: `manage` OR `all` - required index privileges on unomi indices: `write, manage, read` OR `all` -=== Customizing Start Features Configuration +=== Unomi Distribution (features configuration) -Apache Unomi allows you to customize which features and bundles are installed and started when using the `unomi:start` command. This is controlled through the `org.apache.unomi.start.cfg` configuration file. +You can define a specific distribution for Apache Unomi to configure desired features when the server boots up. Is it a classic Karaf feature that defines desired features for Unomi. +Be aware that, even if a distribution is a classic Karaf feature XML file, it must only be composed feature's dependencies as this file is interpreted by the Unomi ManagementService. +No bundle nor config file will be processed from this file. -==== Default Configuration +To set the desired distribution, set the `unomi.distribution` system property or `UNOMI_DISTRIBUTION` environment variable to the name of your desired distribution. +You can also use the dedicated `unomi:setup` command to set the distribution interactively. -By default, Apache Unomi comes with two predefined start features configurations: +Apache Unomi comes with some predefined distributions that you can use directly. -[source] ----- -startFeatures = [ - "elasticsearch=unomi-base,unomi-startup,unomi-elasticsearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-elasticsearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete", - "opensearch=unomi-base,unomi-startup,unomi-opensearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-opensearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete" -] ----- +==== Default Distributions -**Key Differences Between Configurations:** +By default, Apache Unomi comes with four predefined distributions: + +unomi-distribution-elasticsearch, unomi-distribution-elasticsearch-graphql, unomi-distribution-opensearch, unomi-distribution-opensearch-graphql + +**Key Differences Between Distributions:** The only difference between the Elasticsearch and OpenSearch configurations is the persistence layer: * **Elasticsearch**: Uses `unomi-elasticsearch-core` and `unomi-elasticsearch-conditions` * **OpenSearch**: Uses `unomi-opensearch-core` and `unomi-opensearch-conditions` -All other features remain identical between both configurations. +All other features remain identical between both configurations. Each one is derived with or without GraphQL support. -==== Environment-Specific Configurations +==== Environment-Specific Distributions -You can create different configurations for different deployment environments by including or excluding certain features: +You can create different distributions for different deployment environments by creating a dedicated distribution feature. + +Be aware that in distribution's feature, you should only reference other features, not bundles or configuration directly. **Development Environment** (includes development tools and debugging features): -[source] +[source,xml] ---- -startFeatures = [ - "elasticsearch-dev=unomi-base,unomi-startup,unomi-elasticsearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-elasticsearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete", - "opensearch-dev=unomi-base,unomi-startup,unomi-opensearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-opensearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete" -] +<features name="unomi-distributions" xmlns="http://karaf.apache.org/xmlns/features/v1.6.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.6.0 https://karaf.apache.org/xmlns/features/v1.6.0"> + + <repository>mvn:org.apache.karaf.features/specs/${karaf.version}/xml/features</repository> + <repository>mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features</repository> + <repository>mvn:org.apache.unomi/unomi-kar/${project.version}/xml/features</repository> + + <feature name="unomi-distribution-elasticsearch-dev" description="Apache Unomi :: ElasticSearch Development Distribution" version="${project.version}"> + <feature dependency="true" version="${project.version}">unomi-base</feature> + <feature dependency="true" version="${project.version}">unomi-startup</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-core</feature> + <feature dependency="true" version="${project.version}">unomi-persistence-core</feature> + <feature dependency="true" version="${project.version}">unomi-services</feature> + <feature dependency="true" version="${project.version}">unomi-rest-api</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-lists-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-geonames-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-privacy-extension</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-conditions</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-base</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-request</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-mail</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-optimization-test</feature> + <feature dependency="true" version="${project.version}">unomi-shell-dev-commands</feature> + <feature dependency="true" version="${project.version}">unomi-wab</feature> + <feature dependency="true" version="${project.version}">unomi-web-tracker</feature> + <feature dependency="true" version="${project.version}">unomi-healthcheck-elasticsearch</feature> + <feature dependency="true" version="${project.version}">unomi-router-karaf-feature</feature> + <feature dependency="true" version="${project.version}">unomi-groovy-actions</feature> + <feature dependency="true" version="${project.version}">unomi-rest-ui</feature> + <feature dependency="true" version="${project.version}">unomi-startup-complete</feature> + </feature> +</features> ---- **Staging Environment** (production-like but with some development features): -[source] +[source,xml] ---- -startFeatures = [ - "elasticsearch-staging=unomi-base,unomi-startup,unomi-elasticsearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-elasticsearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete", - "opensearch-staging=unomi-base,unomi-startup,unomi-opensearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-opensearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete" -] +<features name="unomi-distributions" xmlns="http://karaf.apache.org/xmlns/features/v1.6.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.6.0 https://karaf.apache.org/xmlns/features/v1.6.0"> + + <repository>mvn:org.apache.karaf.features/specs/${karaf.version}/xml/features</repository> + <repository>mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features</repository> + <repository>mvn:org.apache.unomi/unomi-kar/${project.version}/xml/features</repository> + + <feature name="unomi-distribution-elasticsearch-staging" description="Apache Unomi :: ElasticSearch Staging Distribution" version="${project.version}"> + <feature dependency="true" version="${project.version}">unomi-base</feature> + <feature dependency="true" version="${project.version}">unomi-startup</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-core</feature> + <feature dependency="true" version="${project.version}">unomi-persistence-core</feature> + <feature dependency="true" version="${project.version}">unomi-services</feature> + <feature dependency="true" version="${project.version}">unomi-rest-api</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-lists-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-geonames-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-privacy-extension</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-conditions</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-base</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-request</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-mail</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-optimization-test</feature> + <feature dependency="true" version="${project.version}">unomi-shell-dev-commands</feature> + <feature dependency="true" version="${project.version}">unomi-wab</feature> + <feature dependency="true" version="${project.version}">unomi-web-tracker</feature> + <feature dependency="true" version="${project.version}">unomi-healthcheck-elasticsearch</feature> + <feature dependency="true" version="${project.version}">unomi-router-karaf-feature</feature> + <feature dependency="true" version="${project.version}">unomi-groovy-actions</feature> + <feature dependency="true" version="${project.version}">unomi-rest-ui</feature> + <feature dependency="true" version="${project.version}">unomi-startup-complete</feature> + </feature> +</features> ---- **Production Environment** (minimal, secure configuration): -[source] +[source,xml] ---- -startFeatures = [ - "elasticsearch-prod=unomi-base,unomi-startup,unomi-elasticsearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-elasticsearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete", - "opensearch-prod=unomi-base,unomi-startup,unomi-opensearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-opensearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete" -] +<features name="unomi-distributions" xmlns="http://karaf.apache.org/xmlns/features/v1.6.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.6.0 https://karaf.apache.org/xmlns/features/v1.6.0"> + + <repository>mvn:org.apache.karaf.features/specs/${karaf.version}/xml/features</repository> + <repository>mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features</repository> + <repository>mvn:org.apache.unomi/unomi-kar/${project.version}/xml/features</repository> + + <feature name="unomi-distribution-elasticsearch-prod" description="Apache Unomi :: ElasticSearch Production Distribution" version="${project.version}"> + <feature dependency="true" version="${project.version}">unomi-base</feature> + <feature dependency="true" version="${project.version}">unomi-startup</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-core</feature> + <feature dependency="true" version="${project.version}">unomi-persistence-core</feature> + <feature dependency="true" version="${project.version}">unomi-services</feature> + <feature dependency="true" version="${project.version}">unomi-rest-api</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-lists-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-geonames-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-privacy-extension</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-conditions</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-base</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-request</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-mail</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-optimization-test</feature> + <feature dependency="true" version="${project.version}">unomi-wab</feature> + <feature dependency="true" version="${project.version}">unomi-web-tracker</feature> + <feature dependency="true" version="${project.version}">unomi-healthcheck-elasticsearch</feature> + <feature dependency="true" version="${project.version}">unomi-router-karaf-feature</feature> + <feature dependency="true" version="${project.version}">unomi-groovy-actions</feature> + <feature dependency="true" version="${project.version}">unomi-rest-ui</feature> + <feature dependency="true" version="${project.version}">unomi-startup-complete</feature> + </feature> +</features> ---- **Environment Differences Summary:** @@ -949,37 +1032,32 @@ startFeatures = [ |✗ (excluded) |=== -==== Configuration Format +==== Distribution Feature Format -Each start features configuration follows this format: +Each start features configuration follows the classic Karaf features XML format, but you must only reference other features in your distribution feature. +If you reference bundles or configuration files directly, they will be ignored by Unomi. + +To use a custom distribution, the corresponding feature must be accessible by Karaf. It can be done by publishing the feature maven artefact and adding it as a feature repository in Karaf os for any other feature. Of course you can also add your distribution feature in the existing source code and repackage Unomi making it available directly. + +==== Using Custom Distributions + +You can use your custom distribution with: [source] ---- -"configuration-name=feature1,feature2,feature3,..." +# Using system property +-Dunomi.distribution=unomi-distribution-elasticsearch + +# Using environment variable +UNOMI_DISTRIBUTION=unomi-distribution-opensearch + +# Using the setup command +unomi:setup unomi-distribution-elasticsearch-staging ---- -Where: -* `configuration-name` is the identifier you'll use with the `unomi:start` command -* The comma-separated list contains the Karaf features to install and start - -==== Using Custom Configurations - -You can use your custom configurations with: - -[source] ----- -# Development environment -unomi:start elasticsearch-dev -unomi:start opensearch-dev - -# Staging environment -unomi:start elasticsearch-staging -unomi:start opensearch-staging - -# Production environment -unomi:start elasticsearch-prod -unomi:start opensearch-prod ----- +Once you have called the `unomi:setup` command with your desired distribution, you can start Apache Unomi using the `unomi:start` command. +The setup command only needs to be called once unless you want to change the distribution ; it creates a marker file to remember the chosen distribution. +In case you want to change it, you can use the `unomi:setup --force` command again with the desired distribution name (using the --force option will override the existing distribution). ==== Auto-Start @@ -989,27 +1067,17 @@ To enable auto-start, set the `unomi.autoStart` system property or `UNOMI_AUTO_S Note: Auto-start only works when Apache Unomi is not already running. If the server is already started, the auto-start setting will be ignored. -==== Unomi Distribution +=== Customizing Apache Unomi Distribution -You can define a specific distribution for Apache Unomi to configure desired features when the server boots up. Is it a classic Karaf feature that defines desired features for Unomi. -Be aware that, even if a distribution is a classic Karaf feature XML file, it must only be composed feature's dependencies as this file is interpreted by the Unomi ManagementService. -No bundle nor config file will be processed from this file. +Apache Unomi allows you to create custom distributions by repackaging the standard distribution with your own configuration files and customizations. This is useful for creating deployment-specific packages that include your custom configurations, plugins, and settings. -To set the desired distribution, set the `unomi.distribution` system property or `UNOMI_DISTRIBUTION` environment variable to the name of your desired distribution: +==== Note on Custom Distributions (Advanced) -[source] ----- -# Using system property --Dunomi.distribution=unomi-distribution-elasticsearch +You can build custom distributions of Unomi using the Karaf Maven Plugin to assemble features and overlay configuration. This is an advanced path and not required for most users. We recommend Docker-based packaging and configuration overrides as documented above. -# Using environment variable (Docker) -UNOMI_DISTRIBUTION=unomi-distribution-opensearch +For details about custom distributions, see the Karaf documentation (Karaf Maven Plugin): -# Using custom distribution -UNOMI_DISTRIBUTION=unomi-distribution-elasticsearch-staging ----- - -To use a custom distribution, the corresponding feature must be accessible by Karaf. It can be done by publishing the feature maven artefact and adding it as a feature repository in Karaf os for any other feature. Of course you can also add your distribution feature in the existing source code and repackage Unomi making it available directly. +`https://karaf.apache.org/manual/latest/#_karaf_maven_plugin` ==== Important Notes @@ -1059,18 +1127,6 @@ services: - Declare the feature repository in Karaf using KARAF_FEATURES_REPOSITORIES environment variable pointing to your mounted file or a online maven artifact (mvn:com.example/custom-unomi-distribs/1.0.0/xml/features) - Use the `UNOMI_DISTRIBUTION` environment variable to specify which distribution (using the feature name) to use from that feature's repository -=== Customizing Apache Unomi Distribution - -Apache Unomi allows you to create custom distributions by repackaging the standard distribution with your own configuration files and customizations. This is useful for creating deployment-specific packages that include your custom configurations, plugins, and settings. - -==== Note on Custom Distributions (Advanced) - -You can build custom distributions of Unomi using the Karaf Maven Plugin to assemble features and overlay configuration. This is an advanced path and not required for most users. We recommend Docker-based packaging and configuration overrides as documented above. - -For details about custom distributions, see the Karaf documentation (Karaf Maven Plugin): - -`https://karaf.apache.org/manual/latest/#_karaf_maven_plugin` - ==== Configuration Override Priority Apache Unomi follows this priority order for configuration files (highest to lowest priority): diff --git c/manual/src/main/asciidoc/shell-commands.adoc i/manual/src/main/asciidoc/shell-commands.adoc index 5bcb32d..515d63c 100644 --- c/manual/src/main/asciidoc/shell-commands.adoc +++ i/manual/src/main/asciidoc/shell-commands.adoc @@ -65,6 +65,11 @@ The commands control the lifecycle of the Apache Unomi server and are used to mi |=== |Command|Arguments|Description +|setup +|distribution-feature-name,--force +|This command must be used only when the Apache Unomi application is NOT STARTED. It will perform initial setup of the application +using the specified distribution feature name (unomi-distribution-elasticsearch for example). + |migrate |fromVersion |This command must be used only when the Apache Unomi application is NOT STARTED. It will perform migration of the data stored in search engine using the argument fromVersion as a starting point. @@ -74,8 +79,8 @@ The commands control the lifecycle of the Apache Unomi server and are used to mi |Shutsdown the Apache Unomi application |start -|startFeatures -|Starts the Apache Unomi application with the specified start features configuration (elasticsearch or opensearch). Note that this state will be remembered between Apache Karaf launches, so in general it is only needed after a first installation or after a `migrate` command +|n/a +|Starts the Apache Unomi application with the specified distribution (or by default unomi-distribution-elasticsearch). Note that this state will be remembered between Apache Karaf launches, so in general it is only needed after a first installation |version |n/a @@ -195,4 +200,4 @@ and interactive mode except that it undeploys definitions instead of deploying t working on a plugin. For example to remove all the definitions deployed by a plugin you can simply use the following command: `undeploy-definition BUNDLE_ID * *` when `BUNDLE_ID` is the identifier of the bundle that contains your plugin. -|=== \ No newline at end of file +|=== diff --git c/manual/src/main/asciidoc/writing-plugins.adoc i/manual/src/main/asciidoc/writing-plugins.adoc index c2b49fe..b485f0c 100644 --- c/manual/src/main/asciidoc/writing-plugins.adoc +++ i/manual/src/main/asciidoc/writing-plugins.adoc @@ -162,14 +162,40 @@ OpenSearch Bundle (my-plugin-opensearch): ==== Configuration and Deployment -Administrators can control which search engine implementation to use through the `org.apache.unomi.start.cfg` configuration file. This file determines which features (including your plugin's bundles) are deployed based on the chosen search engine: +Administrators can control which search engine implementation to use by setting up a distribution. This distribution 'macro' feature determines which features (including your plugin's bundles) are deployed based on the chosen search engine: -[source] +[source,xml] ---- -startFeatures = [ - "elasticsearch=unomi-persistence-elasticsearch,my-plugin-elasticsearch,unomi-services,...", - "opensearch=unomi-persistence-opensearch,my-plugin-opensearch,unomi-services,..." -] +<features> + <feature name="my-plugin-feature" version="${project.version}"> + <bundle>mvn:my.group.id/my-plugin-common/${project.version}</bundle> + <bundle>mvn:my.group.id/my-plugin-${search.engine}/${project.version}</bundle> + </feature> + + <feature name="my-unomi-distribution-with-my-plugin" version="${project.version}"> + <feature dependency="true" version="${project.version}">unomi-base</feature> + <feature dependency="true" version="${project.version}">unomi-startup</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-core</feature> + <feature dependency="true" version="${project.version}">unomi-persistence-core</feature> + <feature dependency="true" version="${project.version}">unomi-services</feature> + <feature dependency="true" version="${project.version}">unomi-rest-api</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-lists-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-geonames-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-privacy-extension</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-conditions</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-base</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-request</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-mail</feature> + <feature dependency="true" version="${project.version}">unomi-wab</feature> + <feature dependency="true" version="${project.version}">unomi-web-tracker</feature> + <feature dependency="true" version="${project.version}">unomi-healthcheck-elasticsearch</feature> + <feature dependency="true" version="${project.version}">unomi-router-karaf-feature</feature> + <feature dependency="true" version="${project.version}">unomi-groovy-actions</feature> + <feature dependency="true" version="${project.version}">unomi-rest-ui</feature> + <feature dependency="true" version="${project.version}">unomi-startup-complete</feature> + <feature dependency="true" version="${project.version}">my-plugin-feature</feature> + </feature> +</features> ---- ==== Custom Plugins @@ -680,14 +706,14 @@ import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatc import java.util.Map; public class MyCustomQueryBuilder implements ConditionESQueryBuilder { - + @OverRide - public Query buildQuery(Condition condition, Map<String, Object> context, + public Query buildQuery(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) { // Get parameters from the condition String fieldName = (String) condition.getParameter("fieldName"); String fieldValue = (String) condition.getParameter("fieldValue"); - + // Build Elasticsearch-specific query using the new client return Query.of(q -> q .bool(b -> b @@ -700,9 +726,9 @@ public class MyCustomQueryBuilder implements ConditionESQueryBuilder { ) ); } - + @OverRide - public long count(Condition condition, Map<String, Object> context, + public long count(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) { // Implement count logic if needed return 0; @@ -723,14 +749,14 @@ import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher import java.util.Map; public class MyCustomQueryBuilder implements ConditionOSQueryBuilder { - + @OverRide - public Query buildQuery(Condition condition, Map<String, Object> context, + public Query buildQuery(Condition condition, Map<String, Object> context, ConditionOSQueryBuilderDispatcher dispatcher) { // Get parameters from the condition String fieldName = (String) condition.getParameter("fieldName"); String fieldValue = (String) condition.getParameter("fieldValue"); - + // Build OpenSearch-specific query return Query.of(q -> q .bool(b -> b @@ -743,9 +769,9 @@ public class MyCustomQueryBuilder implements ConditionOSQueryBuilder { ) ); } - + @OverRide - public long count(Condition condition, Map<String, Object> context, + public long count(Condition condition, Map<String, Object> context, ConditionOSQueryBuilderDispatcher dispatcher) { // Implement count logic if needed return 0; @@ -800,7 +826,7 @@ public class MyCustomQueryBuilder implements ConditionOSQueryBuilder { "multivalued": false }, { - "id": "fieldValue", + "id": "fieldValue", "type": "string", "multivalued": false } @@ -854,14 +880,14 @@ my-custom-plugin/ <artifactId>my-custom-plugin</artifactId> <version>1.0.0</version> <packaging>pom</packaging> - + <modules> <module>my-custom-plugin-common</module> <module>my-custom-plugin-elasticsearch</module> <module>my-custom-plugin-opensearch</module> <module>my-custom-plugin-features</module> </modules> - + <properties> <unomi.version>3.0.0</unomi.version> </properties> @@ -877,10 +903,10 @@ my-custom-plugin/ <artifactId>my-custom-plugin</artifactId> <version>1.0.0</version> </parent> - + <artifactId>my-custom-plugin-elasticsearch</artifactId> <packaging>bundle</packaging> - + <dependencies> <dependency> <groupId>org.apache.unomi</groupId> @@ -898,7 +924,7 @@ my-custom-plugin/ <version>1.0.0</version> </dependency> </dependencies> - + <build> <plugins> <plugin> @@ -926,14 +952,14 @@ my-custom-plugin/ ---- <?xml version="1.0" encoding="UTF-8"?> <features name="my-custom-plugin" version="1.0.0"> - + <!-- Elasticsearch feature --> <feature name="my-custom-plugin-elasticsearch" version="1.0.0"> <bundle>mvn:org.apache.unomi.plugins/my-custom-plugin-common/1.0.0</bundle> <bundle>mvn:org.apache.unomi.plugins/my-custom-plugin-elasticsearch/1.0.0</bundle> <feature>unomi-elasticsearch</feature> </feature> - + <!-- OpenSearch feature --> <feature name="my-custom-plugin-opensearch" version="1.0.0"> <bundle>mvn:org.apache.unomi.plugins/my-custom-plugin-common/1.0.0</bundle> @@ -981,24 +1007,43 @@ my-custom-plugin/ - For OpenSearch: `feature:install my-custom-plugin-opensearch` - Document dependencies and requirements clearly -==== Custom Start Configuration +==== Custom Distribution Feature -For production deployments, you can create a custom `org.apache.unomi.start.cfg` file to automatically include your plugin features in the startup configuration. This approach ensures your plugin is automatically deployed when Apache Unomi starts. +For production deployments, you can create a custom distribution's feature file to automatically include your plugin features in the startup configuration. This approach ensures your plugin is automatically deployed when Apache Unomi starts. -**Creating a Custom Start Configuration:** +**Creating a Custom Distribution Feature:** -1. **Create the configuration file** in your deployment directory: - ``` - etc/org.apache.unomi.start.cfg - ``` +1. **Create the feature file** in a dedicated maven module. You can use the unomi-distribution module as a reference. -2. **Define your custom configurations** by extending the default ones: +2. **Define your custom distribution** by extending the default ones: -[source,properties] +[source,xml] ---- -# Custom start configurations that include your plugin features -startFeatures = [ "elasticsearch=unomi-base,unomi-startup,unomi-elasticsearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-elasticsearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete,my-custom-plugin-elasticsearch", \ - "opensearch=unomi-base,unomi-startup,unomi-opensearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-opensearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete,my-custom-plugin-opensearch" ] +<features name="unomi-distributions" xmlns="http://karaf.apache.org/xmlns/features/v1.6.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.6.0 https://karaf.apache.org/xmlns/features/v1.6.0"> + <feature name="unomi-distribution-custom" version="${project.version}"> + <feature dependency="true" version="${project.version}">unomi-base</feature> + <feature dependency="true" version="${project.version}">unomi-startup</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-core</feature> + <feature dependency="true" version="${project.version}">unomi-persistence-core</feature> + <feature dependency="true" version="${project.version}">unomi-services</feature> + <feature dependency="true" version="${project.version}">unomi-rest-api</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-lists-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-geonames-extension</feature> + <feature dependency="true" version="${project.version}">unomi-cxs-privacy-extension</feature> + <feature dependency="true" version="${project.version}">unomi-elasticsearch-conditions</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-base</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-request</feature> + <feature dependency="true" version="${project.version}">unomi-plugins-mail</feature> + <feature dependency="true" version="${project.version}">unomi-wab</feature> + <feature dependency="true" version="${project.version}">unomi-web-tracker</feature> + <feature dependency="true" version="${project.version}">unomi-healthcheck-elasticsearch</feature> + <feature dependency="true" version="${project.version}">unomi-router-karaf-feature</feature> + <feature dependency="true" version="${project.version}">unomi-groovy-actions</feature> + <feature dependency="true" version="${project.version}">unomi-rest-ui</feature> + <feature dependency="true" version="${project.version}">unomi-startup-complete</feature> + <feature dependency="true" version="${project.version}">custom-feature</feature> + </feature> +</features> ---- **Key Points:** @@ -1008,14 +1053,14 @@ startFeatures = [ "elasticsearch=unomi-base,unomi-startup,unomi-elasticsearch-co - **Dependency management**: Ensure your plugin features are listed after their dependencies (e.g., after `unomi-elasticsearch-core` or `unomi-opensearch-core`) -**Benefits of Custom Start Configuration:** +**Benefits of Custom Distribution Feature:** - **Automatic deployment**: Your plugin is automatically installed when Apache Unomi starts - **Consistent environments**: Ensures the same features are deployed across all environments - **Production ready**: No manual feature installation required - **Version control**: Configuration can be versioned and managed with your deployment -**Note**: For more detailed information about customizing start features configurations, including environment-specific examples, see the <<Customizing Start Features Configuration,Configuration>> section of the documentation. +**Note**: For more detailed information about custom distributions, including environment-specific examples, see the <<Custom Distribution Feature,Configuration>> section of the documentation. 6. **Migration from Legacy Implementations** - **DO NOT** use legacy mappings for custom query builders diff --git c/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java i/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java index 607f087..de76351 100644 --- c/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java +++ i/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java @@ -36,7 +36,7 @@ public interface UnomiManagementService { /** * This method will start Apache Unomi * @param mustStartFeatures true if features should be started, false if they should not - * @throws Exception if there was an error starting Unomi's bundles + * @throws Exception if there was an error starting Unomi's features */ void startUnomi(boolean mustStartFeatures) throws Exception; diff --git c/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java i/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java index 5ae3499..8d4ecae 100644 --- c/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java +++ i/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java @@ -43,28 +43,24 @@ import java.util.concurrent.*; * * <p>This service handles the following responsibilities:</p> * <ul> - * <li>Loading configuration from the OSGi Configuration Admin service, including start features configuration and feature lists.</li> - * <li>Starting Apache Unomi by installing and starting the configured features for a selected start features configuration.</li> + * <li>Loading distribution's feature from an environment variable, a java option or by calling the setupUnomiDistribution method.</li> + * <li>Starting Apache Unomi by installing and starting the configured features for a selected distribution.</li> * <li>Stopping Apache Unomi by uninstalling features in reverse order to ensure proper teardown.</li> * <li>Interfacing with the {@link org.apache.unomi.shell.migration.MigrationService} for migration tasks during startup.</li> * </ul> * - * <p>The class is designed to be used within an OSGi environment and integrates with the Configuration Admin service - * to dynamically adjust its behavior based on external configurations. It leverages the {@link FeaturesService} to + * <p>The class is designed to be used within an OSGi environment. It leverages the {@link FeaturesService} to * manage Karaf features dynamically.</p> * * <p><b>Configuration</b></p> - * <p>The service reads its configuration from the OSGi Configuration Admin under the PID <code>org.apache.unomi.start</code>. - * The configuration includes:</p> - * <ul> - * <li><b>startFeatures</b>: A semicolon-separated list of features mapped to persistence implementations - * in the format <code>persistenceImplementation:feature1,feature2</code>.</li> - * </ul> + * <p>The service stores its distribution's name using the OSGi Configuration Admin under the PID <code>org.apache.unomi.setup</code>. + * This allows the service to persist the selected distribution across restarts. The default distribution is unomi-distribution-elasticsearch</p> * * <p><b>Usage</b></p> * <p>This service can be controlled programmatically through its methods:</p> * <ul> - * <li>{@link #startUnomi(String, boolean)}: Installs and starts features for the specified start features configuration.</li> + * <li>{@link #setupUnomiDistribution(String, boolean)}: Sets up the Unomi distribution's feature name.</li> + * <li>{@link #startUnomi(boolean)}: Installs and starts features for the configured distribution.</li> * <li>{@link #stopUnomi()}: Stops and uninstalls the previously started features.</li> * </ul> *
e3bd6b0 to
2b7aa27
Compare
jsinovassin
approved these changes
Dec 31, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR refactors how Apache Unomi selects and installs its startup feature set (formerly driven by the
startFeaturesOSGi configuration and the semantic overloading ofUNOMI_AUTO_START). It introduces proper Karaf features to represent Unomi “distributions” (search backend variant + ancillary feature set) and restoresUNOMI_AUTO_STARTto its original purpose (auto–start true/false). A new environment variable and/or setup mechanism is added to select the desired search backend distribution cleanly and persistently.It also removes the HealthCheck delegator introduced earlier and reverts to the standard pattern of provider activation via configuration, now split per backend through dedicated healthcheck features.
Motivation
The previous approach:
UNOMI_AUTO_STARTto include backend selection (e.g.elasticsearch | opensearch) which was confusing to new users.startFeatures) holding a comma–separated list of features, duplicating what Karaf feature descriptors already provide (dependency management, versioning, externalization).This PR adopts standard Karaf feature mechanisms for clarity, maintainability, and easier custom packaging, while simplifying health checks.
Key Changes
Distribution Feature Introduction
unomi-distribution-elasticsearchunomi-distribution-opensearchdistributionhousing the built-in feature XML descriptors (included in the package).Environment Variable / Setup Refactor
UNOMI_AUTO_STARTto a boolean/legacy behavior (e.g. auto-start service on container launch).UNOMI_DISTRIBUTIONto specify which distribution feature to use (defaults to Elasticsearch if unset).UNOMI_DISTRIBUTIONinto an internalunomi:setuppersisted value.unomi-distribution-elasticsearchif not set in environmentunomi:setup <distributionFeatureName>command:Removal of Custom
startFeaturesOSGi ConfigstartFeaturesOSGi config key and associated management logic fromUnomiManagementService.HealthCheck Simplification
unomi-healthcheck-elasticsearch,unomi-healthcheck-opensearch).init()to avoid unnecessary client construction when not active.Docker & Packaging Adaptations
UNOMI_DISTRIBUTION.UNOMI_AUTO_STARTfor backend selection.UNOMI_AUTO_START=trueUNOMI_DISTRIBUTION=unomi-distribution-opensearch(or elasticsearch).Upgrade / Migration Notes
If you previously used Unomi Docker images with:
(or
opensearch)You must now use:
(or
unomi-distribution-opensearch).If you previously started Unomi in Karaf shell with:
(or
opensearch)You must now use:
(or
unomi-distribution-opensearch).Custom startup feature lists should be converted into a Karaf feature XML and published as a Maven artifact; then set
UNOMI_DISTRIBUTIONto that feature name.Testing Performed
UNOMI_DISTRIBUTIONvalues.UNOMI_DISTRIBUTIONdefaults to elasticsearch.Related JIRA / PR Links