This is an automated email from the ASF dual-hosted git repository.

sergehuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git


The following commit(s) were added to refs/heads/master by this push:
     new 9eeac6d21 UNOMI-955: Fix CI Javadoc failures and a dead shutdown 
crash-recovery path (#780)
9eeac6d21 is described below

commit 9eeac6d21baf8aa30c59d286861651a5d2d07955
Author: Serge Huber <[email protected]>
AuthorDate: Thu Jun 25 13:12:39 2026 +0200

    UNOMI-955: Fix CI Javadoc failures and a dead shutdown crash-recovery path 
(#780)
    
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .gitignore                                         |   2 +
 .../java/org/apache/unomi/api/ClusterNode.java     |   1 +
 .../java/org/apache/unomi/api/ConsentStatus.java   |   3 +
 .../java/org/apache/unomi/api/ContextRequest.java  |  20 ++
 .../java/org/apache/unomi/api/ContextResponse.java |  27 +++
 .../main/java/org/apache/unomi/api/CustomItem.java |  10 +
 api/src/main/java/org/apache/unomi/api/Item.java   |  77 +++++++
 .../org/apache/unomi/api/actions/ActionType.java   |   1 +
 .../apache/unomi/api/conditions/ConditionType.java |   1 +
 .../unomi/api/conditions/ConditionValidation.java  |  93 ++++++++
 .../exceptions/BadSegmentConditionException.java   |  14 ++
 .../java/org/apache/unomi/api/query/Aggregate.java |  78 ++++++-
 .../api/services/ConditionValidationService.java   |  92 +++++++-
 .../unomi/api/services/ConfigSharingService.java   | 102 ++++++++-
 .../apache/unomi/api/services/ProfileService.java  |   1 +
 .../unomi/api/services/SchedulerService.java       |   2 +-
 .../api/services/cache/CacheableTypeConfig.java    |   7 +
 .../org/apache/unomi/api/tasks/ScheduledTask.java  | 255 ++++++++-------------
 .../tenants/security/TenantSecurityService.java    |   1 -
 .../apache/unomi/api/utils/ConditionBuilder.java   |   3 +
 build.sh                                           |  59 ++++-
 .../unomi/router/api/IRouterCamelContext.java      |   2 -
 .../router/api/ImportExportConfiguration.java      |   3 -
 .../apache/unomi/router/api/ProfileToImport.java   |   2 -
 .../exceptions/BadProfileDataFormatException.java  |   2 -
 .../services/ImportExportConfigurationService.java |   3 -
 .../router/api/services/ProfileExportService.java  |   3 -
 .../router/api/services/ProfileImportService.java  |   3 -
 .../unomi/router/core/bean/CollectProfileBean.java |   1 -
 .../router/core/context/RouterCamelContext.java    |   1 -
 .../processor/ExportRouteCompletionProcessor.java  |   2 -
 .../processor/ImportConfigByFileNameProcessor.java |   4 +-
 .../processor/ImportRouteCompletionProcessor.java  |   2 -
 .../router/core/processor/LineBuildProcessor.java  |   1 -
 .../core/processor/LineSplitFailureHandler.java    |   3 -
 .../router/core/processor/LineSplitProcessor.java  |   2 -
 .../core/processor/UnomiStorageProcessor.java      |   2 -
 .../route/ProfileExportCollectRouteBuilder.java    |   2 -
 .../route/ProfileExportProducerRouteBuilder.java   |   2 -
 .../route/ProfileImportFromSourceRouteBuilder.java |   2 -
 .../route/ProfileImportOneShotRouteBuilder.java    |   2 -
 .../route/ProfileImportToUnomiRouteBuilder.java    |   2 -
 .../core/route/RouterAbstractRouteBuilder.java     |   2 -
 .../strategy/ArrayListAggregationStrategy.java     |   2 -
 .../strategy/StringLinesAggregationStrategy.java   |   2 -
 .../apache/unomi/itests/graphql/GraphQLListIT.java |   3 +-
 .../spi/conditions/ConditionContextHelper.java     |  13 +-
 .../PastEventConditionPersistenceQueryBuilder.java |   1 -
 .../evaluator/ConditionEvaluatorDispatcher.java    |   1 -
 .../cache/AbstractMultiTypeCachingService.java     |  63 ++++-
 .../services/common/security/AuditServiceImpl.java |  22 ++
 .../security/ExecutionContextManagerImpl.java      |  15 ++
 .../common/security/KarafSecurityService.java      |  40 ++++
 .../service/AbstractContextAwareService.java       |  85 +++++--
 .../impl/scheduler/SchedulerServiceImpl.java       |  37 +++
 .../impl/scheduler/SchedulerServiceImplTest.java   |  85 ++++++-
 56 files changed, 997 insertions(+), 269 deletions(-)

diff --git a/.gitignore b/.gitignore
index 108c59631..49f40df85 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,5 @@ dependency_tree.txt
 itests/snapshots_repository/
 itests/archives/
 .env.local
+.venv*
+javadoc_*
diff --git a/api/src/main/java/org/apache/unomi/api/ClusterNode.java 
b/api/src/main/java/org/apache/unomi/api/ClusterNode.java
index 66a4ee7c4..a80b5689f 100644
--- a/api/src/main/java/org/apache/unomi/api/ClusterNode.java
+++ b/api/src/main/java/org/apache/unomi/api/ClusterNode.java
@@ -24,6 +24,7 @@ public class ClusterNode extends Item {
 
     private static final long serialVersionUID = 1281422346318230514L;
 
+    // Item type identifier for cluster nodes.
     public static final String ITEM_TYPE = "clusterNode";
 
     private double cpuLoad;
diff --git a/api/src/main/java/org/apache/unomi/api/ConsentStatus.java 
b/api/src/main/java/org/apache/unomi/api/ConsentStatus.java
index d51fab011..1f5b20d27 100644
--- a/api/src/main/java/org/apache/unomi/api/ConsentStatus.java
+++ b/api/src/main/java/org/apache/unomi/api/ConsentStatus.java
@@ -21,7 +21,10 @@ package org.apache.unomi.api;
  * remove a consent for a profile.
  */
 public enum ConsentStatus {
+    // Consent has been granted.
     GRANTED,
+    // Consent has been denied.
     DENIED,
+    // Consent has been revoked.
     REVOKED
 }
diff --git a/api/src/main/java/org/apache/unomi/api/ContextRequest.java 
b/api/src/main/java/org/apache/unomi/api/ContextRequest.java
index f050be672..090fcce18 100644
--- a/api/src/main/java/org/apache/unomi/api/ContextRequest.java
+++ b/api/src/main/java/org/apache/unomi/api/ContextRequest.java
@@ -190,10 +190,20 @@ public class ContextRequest {
         this.filters = filters;
     }
 
+    /**
+     * Returns the list of personalization requests.
+     *
+     * @return the list of personalization requests
+     */
     public List<PersonalizationService.PersonalizationRequest> 
getPersonalizations() {
         return personalizations;
     }
 
+    /**
+     * Sets the list of personalization requests.
+     *
+     * @param personalizations the list of personalization requests
+     */
     public void 
setPersonalizations(List<PersonalizationService.PersonalizationRequest> 
personalizations) {
         this.personalizations = personalizations;
     }
@@ -292,10 +302,20 @@ public class ContextRequest {
         this.profileId = profileId;
     }
 
+    /**
+     * Returns the client ID for this request.
+     *
+     * @return the client ID
+     */
     public String getClientId() {
         return clientId;
     }
 
+    /**
+     * Sets the client ID for this request.
+     *
+     * @param clientId the client ID
+     */
     public void setClientId(String clientId) {
         this.clientId = clientId;
     }
diff --git a/api/src/main/java/org/apache/unomi/api/ContextResponse.java 
b/api/src/main/java/org/apache/unomi/api/ContextResponse.java
index de2335390..aa6a00c18 100644
--- a/api/src/main/java/org/apache/unomi/api/ContextResponse.java
+++ b/api/src/main/java/org/apache/unomi/api/ContextResponse.java
@@ -192,16 +192,27 @@ public class ContextResponse implements Serializable {
     }
 
 
+    /**
+     * Returns the number of events processed in this request.
+     *
+     * @return the number of events processed in this request
+     */
     public int getProcessedEvents() {
         return processedEvents;
     }
 
+    /**
+     * Sets the number of processed events.
+     *
+     * @param processedEvents the count
+     */
     public void setProcessedEvents(int processedEvents) {
         this.processedEvents = processedEvents;
     }
 
     /**
      * @deprecated personalizations results are more complex since 2.1.0 and 
they are now available under: getPersonalizationResults()
+     * @return the personalization results map
      */
     @Deprecated
     public Map<String, List<String>> getPersonalizations() {
@@ -210,6 +221,7 @@ public class ContextResponse implements Serializable {
 
     /**
      * @deprecated personalizations results are more complex since 2.1.0 and 
they are now available under: setPersonalizationResults()
+     * @param personalizations the personalization results
      */
     @Deprecated
     public void setPersonalizations(Map<String, List<String>> 
personalizations) {
@@ -224,6 +236,11 @@ public class ContextResponse implements Serializable {
         return personalizationResults;
     }
 
+    /**
+     * Sets the personalization results.
+     *
+     * @param personalizationResults the results map
+     */
     public void setPersonalizationResults(Map<String, PersonalizationResult> 
personalizationResults) {
         this.personalizationResults = personalizationResults;
     }
@@ -289,10 +306,20 @@ public class ContextResponse implements Serializable {
         this.consents = consents;
     }
 
+    /**
+     * Returns the request tracing data.
+     *
+     * @return the request tracing data
+     */
     public TraceNode getRequestTracing() {
         return requestTracing;
     }
 
+    /**
+     * Sets the request tracing data.
+     *
+     * @param requestTracing the tracing node
+     */
     public void setRequestTracing(TraceNode requestTracing) {
         this.requestTracing = requestTracing;
     }
diff --git a/api/src/main/java/org/apache/unomi/api/CustomItem.java 
b/api/src/main/java/org/apache/unomi/api/CustomItem.java
index a5dceab29..fab0917e4 100644
--- a/api/src/main/java/org/apache/unomi/api/CustomItem.java
+++ b/api/src/main/java/org/apache/unomi/api/CustomItem.java
@@ -68,10 +68,20 @@ public class CustomItem extends Item {
         this.properties = properties;
     }
 
+    /**
+     * Returns the custom item type identifier.
+     *
+     * @return the custom item type identifier
+     */
     public String getCustomItemType() {
         return customItemType;
     }
 
+    /**
+     * Sets the custom item type identifier.
+     *
+     * @param customItemType the custom item type identifier
+     */
     public void setCustomItemType(String customItemType) {
         this.customItemType = customItemType;
     }
diff --git a/api/src/main/java/org/apache/unomi/api/Item.java 
b/api/src/main/java/org/apache/unomi/api/Item.java
index ada8a9f7b..ec796de52 100644
--- a/api/src/main/java/org/apache/unomi/api/Item.java
+++ b/api/src/main/java/org/apache/unomi/api/Item.java
@@ -124,6 +124,11 @@ public abstract class Item implements Serializable, 
YamlConvertible {
         return itemType;
     }
 
+    /**
+     * Sets the Item's type.
+     *
+     * @param itemType the Item's type
+     */
     public void setItemType(String itemType) {
         this.itemType = itemType;
     }
@@ -137,6 +142,11 @@ public abstract class Item implements Serializable, 
YamlConvertible {
         return scope;
     }
 
+    /**
+     * Sets the Item's scope.
+     *
+     * @param scope the Item's scope
+     */
     public void setScope(String scope) {
         this.scope = scope;
     }
@@ -164,10 +174,22 @@ public abstract class Item implements Serializable, 
YamlConvertible {
         this.version = version;
     }
 
+    /**
+     * Returns the system metadata for the given key.
+     *
+     * @param key the key
+     * @return the system metadata for the given key
+     */
     public Object getSystemMetadata(String key) {
         return systemMetadata.get(key);
     }
 
+    /**
+     * Sets the system metadata for the given key.
+     *
+     * @param key the key
+     * @param value the value
+     */
     public void setSystemMetadata(String key, Object value) {
         systemMetadata.put(key, value);
     }
@@ -176,6 +198,11 @@ public abstract class Item implements Serializable, 
YamlConvertible {
         return tenantId;
     }
 
+    /**
+     * Sets the tenant ID.
+     *
+     * @param tenantId the tenant ID
+     */
     public void setTenantId(String tenantId) {
         this.tenantId = tenantId;
     }
@@ -185,6 +212,11 @@ public abstract class Item implements Serializable, 
YamlConvertible {
         return createdBy;
     }
 
+    /**
+     * Sets the created by.
+     *
+     * @param createdBy the created by
+     */
     public void setCreatedBy(String createdBy) {
         this.createdBy = createdBy;
     }
@@ -193,38 +225,83 @@ public abstract class Item implements Serializable, 
YamlConvertible {
         return lastModifiedBy;
     }
 
+    /**
+     * Sets the last modified by.
+     *
+     * @param lastModifiedBy the last modified by
+     */
     public void setLastModifiedBy(String lastModifiedBy) {
         this.lastModifiedBy = lastModifiedBy;
     }
 
+    /**
+     * Returns the date when this item was created.
+     *
+     * @return the date when this item was created
+     */
     public Date getCreationDate() {
         return creationDate;
     }
 
+    /**
+     * Sets the date when this item was created.
+     *
+     * @param creationDate the creation date
+     */
     public void setCreationDate(Date creationDate) {
         this.creationDate = creationDate;
     }
 
+    /**
+     * Returns the date when this item was last modified.
+     *
+     * @return the date when this item was last modified
+     */
     public Date getLastModificationDate() {
         return lastModificationDate;
     }
 
+    /**
+     * Sets the date when this item was last modified.
+     *
+     * @param lastModificationDate the last modification date
+     */
     public void setLastModificationDate(Date lastModificationDate) {
         this.lastModificationDate = lastModificationDate;
     }
 
+    /**
+     * Returns the source instance ID.
+     *
+     * @return the source instance ID
+     */
     public String getSourceInstanceId() {
         return sourceInstanceId;
     }
 
+    /**
+     * Sets the source instance ID.
+     *
+     * @param sourceInstanceId the source instance ID
+     */
     public void setSourceInstanceId(String sourceInstanceId) {
         this.sourceInstanceId = sourceInstanceId;
     }
 
+    /**
+     * Returns the last synchronization date.
+     *
+     * @return the last synchronization date
+     */
     public Date getLastSyncDate() {
         return lastSyncDate;
     }
 
+    /**
+     * Sets the last synchronization date.
+     *
+     * @param lastSyncDate the last synchronization date
+     */
     public void setLastSyncDate(Date lastSyncDate) {
         this.lastSyncDate = lastSyncDate;
     }
diff --git a/api/src/main/java/org/apache/unomi/api/actions/ActionType.java 
b/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
index d61245cb3..bf6fb477a 100644
--- a/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
+++ b/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
@@ -34,6 +34,7 @@ import static 
org.apache.unomi.api.utils.YamlUtils.toYamlValue;
  * A type definition for {@link Action}s.
  */
 public class ActionType extends MetadataItem implements PluginType, 
YamlConvertible {
+    /** Item type identifier for action types. */
     public static final String ITEM_TYPE = "actionType";
 
     private static final long serialVersionUID = -3522958600710010935L;
diff --git 
a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java 
b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
index d62c0a344..e1a00bda1 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
@@ -38,6 +38,7 @@ import static 
org.apache.unomi.api.utils.YamlUtils.toYamlValue;
  * parameters may test whether a given property has a specific value: “User 
property x has value y”.
  */
 public class ConditionType extends MetadataItem implements PluginType, 
YamlConvertible {
+    /** Item type identifier for condition types. */
     public static final String ITEM_TYPE = "conditionType";
 
     private static final long serialVersionUID = -6965481691241954969L;
diff --git 
a/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java 
b/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java
index 37c7acf37..9dfd5feaf 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/ConditionValidation.java
@@ -31,15 +31,25 @@ import static 
org.apache.unomi.api.utils.YamlUtils.setToSortedList;
 public class ConditionValidation implements Serializable, YamlConvertible {
     private static final long serialVersionUID = 1L;
 
+    /** Defines the expected value types for condition parameters. */
     public enum Type {
+        /** String value type. */
         STRING,
+        /** Integer value type. */
         INTEGER,
+        /** Long value type. */
         LONG,
+        /** Float value type. */
         FLOAT,
+        /** Double value type. */
         DOUBLE,
+        /** Boolean value type. */
         BOOLEAN,
+        /** Date value type. */
         DATE,
+        /** Condition value type. */
         CONDITION,
+        /** Object value type. */
         OBJECT
     }
 
@@ -52,69 +62,152 @@ public class ConditionValidation implements Serializable, 
YamlConvertible {
     private boolean recommended;  // Parameter is recommended but not required
     private transient Class<?> customType;
 
+    /**
+     * Instantiates a new ConditionValidation.
+     */
     public ConditionValidation() {
     }
 
+    /**
+     * Returns whether this parameter is required.
+     *
+     * @return true if the parameter is required
+     */
     public boolean isRequired() {
         return required;
     }
 
+    /**
+     * Sets whether this parameter is required.
+     *
+     * @param required true if required
+     */
     public void setRequired(boolean required) {
         this.required = required;
     }
 
+    /**
+     * Returns the set of allowed string values.
+     *
+     * @return the set of allowed string values
+     */
     public Set<String> getAllowedValues() {
         return allowedValues;
     }
 
+    /**
+     * Sets the allowed string values.
+     *
+     * @param allowedValues the allowed values
+     */
     public void setAllowedValues(Set<String> allowedValues) {
         this.allowedValues = allowedValues;
     }
 
+    /**
+     * Returns the allowed condition tags.
+     *
+     * @return the allowed condition tags
+     */
     public Set<String> getAllowedConditionTags() {
         return allowedConditionTags;
     }
 
+    /**
+     * Sets the allowed condition tags.
+     *
+     * @param allowedConditionTags the allowed tags
+     */
     public void setAllowedConditionTags(Set<String> allowedConditionTags) {
         this.allowedConditionTags = allowedConditionTags;
     }
 
+    /**
+     * Returns the disallowed condition type identifiers.
+     *
+     * @return the disallowed condition type identifiers
+     */
     public Set<String> getDisallowedConditionTypes() {
         return disallowedConditionTypes;
     }
 
+    /**
+     * Sets the disallowed condition type identifiers.
+     *
+     * @param disallowedConditionTypes the disallowed types
+     */
     public void setDisallowedConditionTypes(Set<String> 
disallowedConditionTypes) {
         this.disallowedConditionTypes = disallowedConditionTypes;
     }
 
+    /**
+     * Returns whether this parameter is mutually exclusive with others in the 
same group.
+     *
+     * @return true if the parameter is mutually exclusive with others in the 
same group
+     */
     public boolean isExclusive() {
         return exclusive;
     }
 
+    /**
+     * Sets whether this parameter is mutually exclusive.
+     *
+     * @param exclusive true if exclusive
+     */
     public void setExclusive(boolean exclusive) {
         this.exclusive = exclusive;
     }
 
+    /**
+     * Returns the exclusivity group name.
+     *
+     * @return the exclusivity group name
+     */
     public String getExclusiveGroup() {
         return exclusiveGroup;
     }
 
+    /**
+     * Sets the exclusivity group name.
+     *
+     * @param exclusiveGroup the group name
+     */
     public void setExclusiveGroup(String exclusiveGroup) {
         this.exclusiveGroup = exclusiveGroup;
     }
 
+    /**
+     * Returns whether this parameter is recommended.
+     *
+     * @return true if the parameter is recommended
+     */
     public boolean isRecommended() {
         return recommended;
     }
 
+    /**
+     * Sets whether this parameter is recommended.
+     *
+     * @param recommended true if recommended
+     */
     public void setRecommended(boolean recommended) {
         this.recommended = recommended;
     }
 
+    /**
+     * Returns the custom Java type for this parameter.
+     *
+     * @return the custom Java type for this parameter
+     */
     public Class<?> getCustomType() {
         return customType;
     }
 
+    /**
+     * Sets the custom Java type for this parameter.
+     *
+     * @param customType the custom type class
+     */
     public void setCustomType(Class<?> customType) {
         this.customType = customType;
     }
diff --git 
a/api/src/main/java/org/apache/unomi/api/exceptions/BadSegmentConditionException.java
 
b/api/src/main/java/org/apache/unomi/api/exceptions/BadSegmentConditionException.java
index 854d9a7a7..8bd908a2c 100644
--- 
a/api/src/main/java/org/apache/unomi/api/exceptions/BadSegmentConditionException.java
+++ 
b/api/src/main/java/org/apache/unomi/api/exceptions/BadSegmentConditionException.java
@@ -22,14 +22,28 @@ package org.apache.unomi.api.exceptions;
  */
 public class BadSegmentConditionException extends RuntimeException {
 
+    /**
+     * Instantiates a new BadSegmentConditionException.
+     */
     public BadSegmentConditionException() {
         super();
     }
 
+    /**
+     * Constructs with a message.
+     *
+     * @param message the error message
+     */
     public BadSegmentConditionException(String message) {
         super(message);
     }
 
+    /**
+     * Constructs with a message and cause.
+     *
+     * @param message the error message
+     * @param cause the root cause
+     */
     public BadSegmentConditionException(String message, Throwable cause) {
         super(message, cause);
     }
diff --git a/api/src/main/java/org/apache/unomi/api/query/Aggregate.java 
b/api/src/main/java/org/apache/unomi/api/query/Aggregate.java
index d68f43390..9c2f3ffca 100644
--- a/api/src/main/java/org/apache/unomi/api/query/Aggregate.java
+++ b/api/src/main/java/org/apache/unomi/api/query/Aggregate.java
@@ -24,7 +24,13 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * A specification for an aggregate as part of {@link AggregateQuery}s
+ * Specification for an aggregation used by {@link AggregateQuery}.
+ * <p>
+ * The {@link #type} identifies the aggregation strategy: {@code date}, {@code 
dateRange},
+ * {@code numericRange}, {@code ipRange}, or {@code null} (terms aggregation 
on distinct values).
+ * Type-specific configuration is supplied through {@link #parameters} and the 
corresponding range lists.
+ *
+ * @see AggregateQuery
  */
 public class Aggregate implements Serializable {
     private String type;
@@ -35,49 +41,119 @@ public class Aggregate implements Serializable {
     private List<IpRange> ipRanges = new ArrayList<>();
 
 
+    /**
+     * Instantiates a new Aggregate.
+     */
     public Aggregate() {
     }
 
+    /**
+     * Retrieves the aggregation type.
+     * <p>
+     * Supported values are {@code date}, {@code dateRange}, {@code 
numericRange}, and {@code ipRange}.
+     * When {@code null}, a terms aggregation is used on distinct property 
values.
+     *
+     * @return the aggregation type, or {@code null} for a terms aggregation
+     */
     public String getType() {
         return type;
     }
 
+    /**
+     * Sets the aggregation type.
+     * <p>
+     * Supported values are {@code date}, {@code dateRange}, {@code 
numericRange}, and {@code ipRange}.
+     * When {@code null}, a terms aggregation is used on distinct property 
values.
+     *
+     * @param type the aggregation type
+     */
     public void setType(String type) {
         this.type = type;
     }
 
+    /**
+     * Retrieves the aggregation parameters.
+     * <p>
+     * For {@code date} aggregations, expected keys are {@code interval} and 
{@code format}.
+     * For {@code dateRange} aggregations, the {@code format} key defines the 
date format.
+     *
+     * @return the aggregation parameters
+     */
     public Map<String, Object> getParameters() {
         return parameters;
     }
 
+    /**
+     * Sets the aggregation parameters.
+     * <p>
+     * For {@code date} aggregations, expected keys are {@code interval} and 
{@code format}.
+     * For {@code dateRange} aggregations, the {@code format} key defines the 
date format.
+     *
+     * @param parameters the aggregation parameters
+     */
     public void setParameters(Map<String, Object> parameters) {
         this.parameters = parameters;
     }
 
+    /**
+     * Retrieves the property to aggregate on.
+     *
+     * @return the property to aggregate on
+     */
     public String getProperty() {
         return property;
     }
 
+    /**
+     * Sets the property to aggregate on.
+     *
+     * @param property the property name
+     */
     public void setProperty(String property) {
         this.property = property;
     }
 
+    /**
+     * Retrieves the numeric ranges used by {@code numericRange} aggregations.
+     *
+     * @return the numeric ranges
+     */
     public List<NumericRange> getNumericRanges() {
         return numericRanges;
     }
 
+    /**
+     * Retrieves the date ranges used by {@code dateRange} aggregations.
+     *
+     * @return the date ranges
+     */
     public List<DateRange> getDateRanges() {
         return dateRanges;
     }
 
+    /**
+     * Sets the date ranges used by {@code dateRange} aggregations.
+     *
+     * @param dateRanges the date ranges
+     */
     public void setDateRanges(List<DateRange> dateRanges) {
         this.dateRanges = dateRanges;
     }
 
+    /**
+     * Retrieves the IP ranges used by {@code ipRange} aggregations.
+     *
+     * @return the IP ranges
+     */
     public List<IpRange> ipRanges() {
         return ipRanges;
     }
 
+    /**
+     * Sets the IP ranges used by {@code ipRange} aggregations.
+     *
+     * @param ipRanges the IP ranges
+     */
     public void setIpRanges(List<IpRange> ipRanges) {
         this.ipRanges = ipRanges;
     }
diff --git 
a/api/src/main/java/org/apache/unomi/api/services/ConditionValidationService.java
 
b/api/src/main/java/org/apache/unomi/api/services/ConditionValidationService.java
index b9d73ea92..2f77c95a8 100644
--- 
a/api/src/main/java/org/apache/unomi/api/services/ConditionValidationService.java
+++ 
b/api/src/main/java/org/apache/unomi/api/services/ConditionValidationService.java
@@ -23,21 +23,29 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * A service to validate conditions against their type definitions
+ * Service that validates {@link Condition} instances against their {@link 
org.apache.unomi.api.conditions.ConditionType} definitions.
+ * <p>
+ * Used during save operations to ensure condition parameters satisfy type 
constraints. Parameters containing
+ * references ({@code parameter::}) or script expressions ({@code script::}) 
are skipped because their values
+ * are resolved at evaluation time.
+ *
+ * @see DefinitionsService#getConditionValidationService()
  */
 public interface ConditionValidationService {
 
     /**
      * Validates a condition against its type definition.
-     * Skips validation for parameters that contain references (`parameter::`) 
or script expressions (`script::`).
-     * Only validates parameters that are NOT parameter references or script 
expressions.
+     * <p>
+     * Skips validation for parameters that contain references ({@code 
parameter::}) or script expressions
+     * ({@code script::}). Only literal parameter values are validated.
+     *
      * @param condition the condition to validate
-     * @return a list of validation errors, empty if the condition is valid 
(for non-reference/script values)
+     * @return a list of validation errors, empty when the condition is valid
      */
     List<ValidationError> validate(Condition condition);
 
     /**
-     * Represents a validation error with detailed context
+     * A validation error with detailed context about the failing condition, 
parameter, and cause.
      */
     class ValidationError {
         private final String parameterName;
@@ -48,10 +56,28 @@ public interface ConditionValidationService {
         private final Map<String, Object> context;
         private final ValidationError parentError;
 
+        /**
+         * Instantiates a validation error for a single parameter.
+         *
+         * @param parameterName the parameter that caused the error
+         * @param message the error description
+         * @param type the error type
+         */
         public ValidationError(String parameterName, String message, 
ValidationErrorType type) {
             this(parameterName, message, type, null, null, null, null);
         }
 
+        /**
+         * Instantiates a validation error with full condition context and 
optional parent error.
+         *
+         * @param parameterName the parameter that caused the error
+         * @param message the error description
+         * @param type the error type
+         * @param conditionId the identifier of the condition being validated
+         * @param conditionTypeId the identifier of the condition type
+         * @param context additional context key-value pairs
+         * @param parentError the parent error that caused this error, or 
{@code null}
+         */
         public ValidationError(String parameterName, String message, 
ValidationErrorType type,
                              String conditionId, String conditionTypeId, 
Map<String, Object> context,
                              ValidationError parentError) {
@@ -64,43 +90,81 @@ public interface ConditionValidationService {
             this.parentError = parentError;
         }
 
+        /**
+         * Retrieves the name of the parameter that caused the error.
+         *
+         * @return the parameter name, or {@code null} if not applicable
+         */
         public String getParameterName() {
             return parameterName;
         }
 
+        /**
+         * Retrieves the error description message.
+         *
+         * @return the error message
+         */
         public String getMessage() {
             return message;
         }
 
+        /**
+         * Retrieves the type of validation error.
+         *
+         * @return the error type
+         */
         public ValidationErrorType getType() {
             return type;
         }
 
+        /**
+         * Retrieves the identifier of the condition associated with this 
error.
+         *
+         * @return the condition identifier, or {@code null} if not applicable
+         */
         public String getConditionId() {
             return conditionId;
         }
 
+        /**
+         * Retrieves the identifier of the condition type associated with this 
error.
+         *
+         * @return the condition type identifier, or {@code null} if not 
applicable
+         */
         public String getConditionTypeId() {
             return conditionTypeId;
         }
 
-        /** @deprecated Use {@link #getConditionTypeId()} instead. */
+        /**
+         * @deprecated Use {@link #getConditionTypeId()} instead.
+         */
         @Deprecated
         public String getConditionTypeName() {
             return conditionTypeId;
         }
 
+        /**
+         * Retrieves a copy of the additional context map for this error.
+         *
+         * @return the context map
+         */
         public Map<String, Object> getContext() {
             return new HashMap<>(context);
         }
 
+        /**
+         * Retrieves the parent error that caused this error, if any.
+         *
+         * @return the parent error, or {@code null} if none
+         */
         public ValidationError getParentError() {
             return parentError;
         }
 
         /**
-         * Returns a detailed error message including all context information
-         * @return A detailed error message
+         * Retrieves a detailed error message including condition, parameter, 
context, and parent error information.
+         *
+         * @return the detailed error message
          */
         public String getDetailedMessage() {
             StringBuilder sb = new StringBuilder();
@@ -151,13 +215,18 @@ public interface ConditionValidationService {
     }
 
     /**
-     * Types of validation errors
+     * Categories of validation errors returned by {@link 
#validate(Condition)}.
      */
     enum ValidationErrorType {
+        /** A required parameter is absent. */
         MISSING_REQUIRED_PARAMETER("Required parameter is missing"),
+        /** The value provided for a parameter is not valid. */
         INVALID_VALUE("Invalid value provided"),
+        /** The condition type is invalid or not supported. */
         INVALID_CONDITION_TYPE("Invalid or unsupported condition type"),
+        /** Mutually exclusive parameters are both present. */
         EXCLUSIVE_PARAMETER_VIOLATION("Mutually exclusive parameters 
conflict"),
+        /** A recommended parameter is absent. */
         MISSING_RECOMMENDED_PARAMETER("Recommended parameter is missing");
 
         private final String description;
@@ -166,6 +235,11 @@ public interface ConditionValidationService {
             this.description = description;
         }
 
+        /**
+         * Retrieves the human-readable description of this error type.
+         *
+         * @return the error type description
+         */
         public String getDescription() {
             return description;
         }
diff --git 
a/api/src/main/java/org/apache/unomi/api/services/ConfigSharingService.java 
b/api/src/main/java/org/apache/unomi/api/services/ConfigSharingService.java
index c1d91af28..d847905e5 100644
--- a/api/src/main/java/org/apache/unomi/api/services/ConfigSharingService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/ConfigSharingService.java
@@ -19,25 +19,88 @@ package org.apache.unomi.api.services;
 import java.util.Set;
 
 /**
- * A service to share configuration properties with other bundles. It also 
support listeners that will be called whenever
- * a property is added/updated/removed. Simply register a service with the 
@link ConfigSharingServiceConfigChangeListener interface and it will
- * be automatically picked up.
+ * OSGi service for sharing runtime configuration properties across Unomi 
bundles.
+ * <p>
+ * Bundles that own configuration (for example {@code web-servlets} for cookie 
settings or
+ * {@code router-core} for import paths) publish values with {@link 
#setProperty(String, Object)}.
+ * Other bundles read them with {@link #getProperty(String)} without taking a 
direct dependency on
+ * the publishing module. Initial properties may also be seeded at startup 
(for example
+ * {@code internalServerAddress} from cluster configuration).
+ * <p>
+ * Property changes notify registered {@link ConfigChangeListener} OSGi 
services. Register a
+ * component implementing {@link ConfigChangeListener} to react to {@link 
ConfigChangeEvent}s
+ * when properties are added, updated, or removed.
  */
 public interface ConfigSharingService {
 
+    /**
+     * Retrieves the value of the named property.
+     *
+     * @param name the property name
+     * @return the property value, or {@code null} if not set
+     */
     Object getProperty(String name);
+
+    /**
+     * Sets the value of the named property and notifies registered listeners.
+     *
+     * @param name the property name
+     * @param value the new value
+     * @return the previous value, or {@code null} if the property was not 
previously set
+     */
     Object setProperty(String name, Object value);
+
+    /**
+     * Determines whether the named property is currently set.
+     *
+     * @param name the property name
+     * @return {@code true} if the property exists, {@code false} otherwise
+     */
     boolean hasProperty(String name);
+
+    /**
+     * Removes the named property and notifies registered listeners.
+     *
+     * @param name the property name
+     * @return the previous value, or {@code null} if the property was not set
+     */
     Object removeProperty(String name);
+
+    /**
+     * Retrieves the names of all currently known properties.
+     *
+     * @return the property names
+     */
     Set<String> getPropertyNames();
 
+    /**
+     * Event describing a configuration property change.
+     */
     class ConfigChangeEvent {
-        public enum ConfigChangeEventType { ADDED, UPDATED, REMOVED };
+        /**
+         * Types of configuration change events.
+         */
+        public enum ConfigChangeEventType {
+            /** Property was added. */
+            ADDED,
+            /** Property was updated. */
+            UPDATED,
+            /** Property was removed. */
+            REMOVED
+        };
         private ConfigChangeEventType eventType;
         private String name;
         private Object oldValue;
         private Object newValue;
 
+        /**
+         * Instantiates a configuration change event.
+         *
+         * @param eventType the type of change
+         * @param name the property name
+         * @param oldValue the previous value, or {@code null} for {@link 
ConfigChangeEventType#ADDED} events
+         * @param newValue the new value, or {@code null} for {@link 
ConfigChangeEventType#REMOVED} events
+         */
         public ConfigChangeEvent(ConfigChangeEventType eventType, String name, 
Object oldValue, Object newValue) {
             this.eventType = eventType;
             this.name = name;
@@ -45,24 +108,55 @@ public interface ConfigSharingService {
             this.newValue = newValue;
         }
 
+        /**
+         * Retrieves the type of configuration change.
+         *
+         * @return the event type
+         */
         public ConfigChangeEventType getEventType() {
             return eventType;
         }
 
+        /**
+         * Retrieves the name of the changed property.
+         *
+         * @return the property name
+         */
         public String getName() {
             return name;
         }
 
+        /**
+         * Retrieves the previous value of the property.
+         *
+         * @return the old value, or {@code null} for {@link 
ConfigChangeEventType#ADDED} events
+         */
         public Object getOldValue() {
             return oldValue;
         }
 
+        /**
+         * Retrieves the new value of the property.
+         *
+         * @return the new value, or {@code null} for {@link 
ConfigChangeEventType#REMOVED} events
+         */
         public Object getNewValue() {
             return newValue;
         }
     }
 
+    /**
+     * Listener for configuration property changes.
+     * <p>
+     * Implementations are registered as OSGi services and invoked when 
properties are added,
+     * updated, or removed through {@link ConfigSharingService}.
+     */
     interface ConfigChangeListener {
+        /**
+         * Called when a configuration property changes.
+         *
+         * @param configChangeEvent the change event
+         */
         void configChanged(ConfigChangeEvent configChangeEvent);
     }
 
diff --git 
a/api/src/main/java/org/apache/unomi/api/services/ProfileService.java 
b/api/src/main/java/org/apache/unomi/api/services/ProfileService.java
index b99985d76..78ecb1ffb 100644
--- a/api/src/main/java/org/apache/unomi/api/services/ProfileService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/ProfileService.java
@@ -123,6 +123,7 @@ public interface ProfileService {
      * @param profileID the identifier of the profile
      * @param alias     the alias which should be unlinked from the profile
      * @param clientID  the identifier of the client
+     * @return the removed ProfileAlias, or null if not found
      */
     ProfileAlias removeAliasFromProfile(String profileID, String alias, String 
clientID);
 
diff --git 
a/api/src/main/java/org/apache/unomi/api/services/SchedulerService.java 
b/api/src/main/java/org/apache/unomi/api/services/SchedulerService.java
index aff9a843a..6868ebc65 100644
--- a/api/src/main/java/org/apache/unomi/api/services/SchedulerService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/SchedulerService.java
@@ -241,7 +241,7 @@ public interface SchedulerService {
      * @param runnable the code to execute
      * @param persistent whether to store in persistence service (true) or 
only in memory (false)
      * @return the created and scheduled task
-     * @throws IllegalArgumentException if period <= 0 or timeUnit is null
+     * @throws IllegalArgumentException if period &lt;= 0 or timeUnit is null
      */
     ScheduledTask createRecurringTask(String taskType, long period, TimeUnit 
timeUnit, Runnable runnable, boolean persistent);
 
diff --git 
a/api/src/main/java/org/apache/unomi/api/services/cache/CacheableTypeConfig.java
 
b/api/src/main/java/org/apache/unomi/api/services/cache/CacheableTypeConfig.java
index 3194305f7..be2632c42 100644
--- 
a/api/src/main/java/org/apache/unomi/api/services/cache/CacheableTypeConfig.java
+++ 
b/api/src/main/java/org/apache/unomi/api/services/cache/CacheableTypeConfig.java
@@ -615,6 +615,13 @@ public class CacheableTypeConfig<T extends Serializable> {
      */
     @FunctionalInterface
     public interface TriConsumer<T, U, V> {
+        /**
+         * Performs this operation on the given arguments.
+         *
+         * @param t the first input argument
+         * @param u the second input argument
+         * @param v the third input argument
+         */
         void accept(T t, U u, V v);
     }
 } 
\ No newline at end of file
diff --git a/api/src/main/java/org/apache/unomi/api/tasks/ScheduledTask.java 
b/api/src/main/java/org/apache/unomi/api/tasks/ScheduledTask.java
index b971e81f2..e8afce678 100644
--- a/api/src/main/java/org/apache/unomi/api/tasks/ScheduledTask.java
+++ b/api/src/main/java/org/apache/unomi/api/tasks/ScheduledTask.java
@@ -26,21 +26,30 @@ import java.util.concurrent.TimeUnit;
 import java.util.HashSet;
 
 /**
- * Represents a persistent scheduled task that can be executed across a 
cluster.
- * This class provides a comprehensive model for task scheduling and execution 
with features including:
+ * Persistent {@link Item} representing a cluster-aware scheduled task managed 
by {@link org.apache.unomi.api.services.SchedulerService}.
+ * <p>
+ * Tasks are identified by {@link #taskType} and executed by registered {@link 
TaskExecutor} implementations. The model supports:
  * <ul>
- *   <li>Task lifecycle management through states (SCHEDULED, WAITING, 
RUNNING, etc.)</li>
- *   <li>Lock management for cluster coordination</li>
- *   <li>Execution history and checkpoint data for recovery</li>
- *   <li>Support for one-shot and periodic execution</li>
- *   <li>Task dependencies and parallel execution control</li>
- *   <li>Cluster-wide task distribution</li>
+ *   <li>Lifecycle states via {@link TaskStatus} (scheduled, waiting, running, 
completed, failed, cancelled, crashed)</li>
+ *   <li>Cluster coordination through lock ownership and executing node 
tracking</li>
+ *   <li>One-shot and periodic scheduling with fixed-rate or fixed-delay 
semantics</li>
+ *   <li>Retry, checkpoint, and dependency management for long-running or 
multi-step work</li>
+ *   <li>Persistent storage (cluster-visible) or in-memory execution (single 
node)</li>
  * </ul>
+ *
+ * @see org.apache.unomi.api.services.SchedulerService
+ * @see TaskExecutor
  */
 public class ScheduledTask extends Item implements Serializable {
 
+    /**
+     * Java serialization version; Unomi does not rely on Java serialization 
of this type as a cross-version persistence contract.
+     */
     private static final long serialVersionUID = 1L;
 
+    /**
+     * Item type identifier for scheduled tasks, used by {@link 
org.apache.unomi.api.Item#getItemType()}.
+     */
     public static final String ITEM_TYPE = "scheduledTask";
 
     /**
@@ -66,129 +75,39 @@ public class ScheduledTask extends Item implements 
Serializable {
 
     private String taskType;
     private Map<String, Object> parameters;
-    private String executingNodeId;  // The ID of the node currently executing 
this task
-    /**
-     * The initial delay before first execution, in the specified time unit.
-     */
+    private String executingNodeId;
     private long initialDelay;
     private long period;
     private TimeUnit timeUnit;
     private boolean fixedRate;
-    /**
-     * Gets the date of the last execution attempt.
-     * 
-     * @return the last execution date or null if never executed
-     */
     private Date lastExecutionDate;
-    /**
-     * Gets the node ID that last executed this task.
-     * 
-     * @return the ID of the last executing node
-     */
     private String lastExecutedBy;
-    /**
-     * Gets the error message from the last failed execution.
-     * 
-     * @return the last error message or null if no error
-     */
     private String lastError;
     private boolean enabled;
     private String lockOwner;
-    /**
-     * Gets the date when the current lock was acquired.
-     * 
-     * @return the lock acquisition date or null if unlocked
-     */
     private Date lockDate;
     private boolean oneShot;
     private boolean allowParallelExecution;
-    /**
-     * Gets the current task status.
-     * 
-     * @return the current status
-     */
     private TaskStatus status;
     private Map<String, Object> statusDetails;
-    /**
-     * Gets the next scheduled execution date for periodic tasks.
-     * 
-     * @return the next scheduled execution date or null if not scheduled
-     */
     private Date nextScheduledExecution;
-    /**
-     * Gets the number of consecutive execution failures.
-     * 
-     * @return the failure count
-     */
     private int failureCount;
-    /**
-     * Gets the number of successful executions.
-     * 
-     * @return the success count
-     */
     private int successCount;
-    /**
-     * Gets the maximum number of retry attempts after failures.
-     * For one-shot tasks:
-     * - When a task fails, it will be automatically retried up to this many 
times
-     * - Each retry attempt occurs after waiting for retryDelay
-     * - After reaching this limit, the task remains in FAILED state until 
manually retried
-     * 
-     * For periodic tasks:
-     * - Retries only apply within a single scheduled execution
-     * - If retries are exhausted, the task will still attempt its next 
scheduled execution
-     * - The next scheduled execution resets the failure count
-     * 
-     * A value of 0 means no automatic retries in either case.
-     * 
-     * @return the maximum retry count
-     */
     private int maxRetries;
-    /**
-     * Gets the delay between retry attempts.
-     * For one-shot tasks:
-     * - This delay is applied between each retry attempt after a failure
-     * - Helps prevent rapid-fire retries that could overload the system
-     * 
-     * For periodic tasks:
-     * - This delay is used between retry attempts within a single scheduled 
execution
-     * - Does not affect the task's configured period/scheduling
-     * 
-     * @return the retry delay in milliseconds
-     */
     private long retryDelay;
-    /**
-     * Gets the name of the current execution step.
-     * This is used to track progress through multi-step tasks.
-     * 
-     * @return the current step name or null if not set
-     */
     private String currentStep;
-    /**
-     * Gets the checkpoint data for task resumption.
-     * This data allows a task to resume from where it left off after a crash.
-     * 
-     * @return map of checkpoint data or null if no checkpoint
-     */
     private Map<String, Object> checkpointData;
-    private boolean persistent = true;  // By default tasks are persistent
-    private boolean runOnAllNodes = false;  // By default tasks run on a 
single node
-    /**
-     * Indicates if this is a system task that should not be recreated on 
startup.
-     * System tasks are created by the system during initialization and should 
be
-     * preserved across restarts.
-     */
-    private boolean systemTask = false;  // By default tasks are not system 
tasks
-    /**
-     * Gets the task type that this task is waiting for a lock on.
-     * This is used when tasks of the same type cannot run in parallel.
-     * 
-     * @return the task type being waited on or null if not waiting
-     */
+    private boolean persistent = true;
+    private boolean runOnAllNodes = false;
+    private boolean systemTask = false;
     private String waitingForTaskType;
-    private Set<String> dependsOn = new HashSet<>();  // Set of task IDs this 
task depends on
-    private Set<String> waitingOnTasks = new HashSet<>();  // Set of task IDs 
this task is currently waiting on
+    private Set<String> dependsOn = new HashSet<>();
+    private Set<String> waitingOnTasks = new HashSet<>();
 
+    /**
+     * Instantiates a new scheduled task with default status {@link 
TaskStatus#SCHEDULED},
+     * {@code maxRetries} of 3, and a default {@code retryDelay} of 60 seconds.
+     */
     public ScheduledTask() {
         super();
         this.status = TaskStatus.SCHEDULED;
@@ -198,7 +117,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the task type identifier.
+     * Retrieves the task type identifier.
      * The task type determines which executor will handle this task.
      * 
      * @return the task type identifier
@@ -217,7 +136,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the task parameters.
+     * Retrieves the task parameters.
      * These parameters are passed to the task executor during execution.
      * 
      * @return map of task parameters
@@ -236,25 +155,25 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the initial delay before first execution.
-     * 
-     * @return the initial delay in the specified time unit
+     * Retrieves the initial delay before the first execution, expressed in 
{@link #getTimeUnit()}.
+     *
+     * @return the initial delay in the configured time unit
      */
     public long getInitialDelay() {
         return initialDelay;
     }
 
     /**
-     * Sets the initial delay before first execution.
-     * 
-     * @param initialDelay the initial delay in the specified time unit
+     * Sets the initial delay before the first execution, expressed in {@link 
#getTimeUnit()}.
+     *
+     * @param initialDelay the initial delay in the configured time unit
      */
     public void setInitialDelay(long initialDelay) {
         this.initialDelay = initialDelay;
     }
 
     /**
-     * Gets the period between successive task executions.
+     * Retrieves the period between successive task executions.
      * A period of 0 indicates a one-time task and will automatically set 
oneShot=true.
      * 
      * @return the period between executions in the specified time unit
@@ -285,7 +204,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the time unit for delay and period values.
+     * Retrieves the time unit for delay and period values.
      * 
      * @return the time unit used for scheduling
      */
@@ -303,11 +222,11 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets whether this task uses fixed-rate scheduling.
+     * Determines whether this task uses fixed-rate scheduling.
      * If true, executions are scheduled at fixed intervals from the start 
time.
      * If false, executions are scheduled at fixed delays from completion.
      * 
-     * @return true if using fixed-rate scheduling
+     * @return {@code true} if using fixed-rate scheduling
      */
     public boolean isFixedRate() {
         return fixedRate;
@@ -323,9 +242,9 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the date of the last execution attempt.
+     * Retrieves the date of the last execution attempt.
      * 
-     * @return the last execution date or null if never executed
+     * @return the last execution date or {@code null} if never executed
      */
     public Date getLastExecutionDate() {
         return lastExecutionDate;
@@ -341,7 +260,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the node ID that last executed this task.
+     * Retrieves the node ID that last executed this task.
      * 
      * @return the ID of the last executing node
      */
@@ -359,9 +278,9 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the error message from the last failed execution.
+     * Retrieves the error message from the last failed execution.
      * 
-     * @return the last error message or null if no error
+     * @return the last error message or {@code null} if no error
      */
     public String getLastError() {
         return lastError;
@@ -377,10 +296,10 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets whether this task is enabled.
+     * Determines whether this task is enabled.
      * Disabled tasks will not be executed.
      * 
-     * @return true if the task is enabled
+     * @return {@code true} if the task is enabled
      */
     public boolean isEnabled() {
         return enabled;
@@ -396,9 +315,9 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the ID of the node that currently holds the execution lock.
+     * Retrieves the ID of the node that currently holds the execution lock.
      * 
-     * @return the current lock owner's node ID or null if unlocked
+     * @return the current lock owner's node ID or {@code null} if unlocked
      */
     public String getLockOwner() {
         return lockOwner;
@@ -414,9 +333,9 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the date when the current lock was acquired.
+     * Retrieves the date when the current lock was acquired.
      * 
-     * @return the lock acquisition date or null if unlocked
+     * @return the lock acquisition date or {@code null} if unlocked
      */
     public Date getLockDate() {
         return lockDate;
@@ -432,10 +351,10 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Returns whether this task should execute only once.
+     * Determines whether this task should execute only once.
      * Tasks with period=0 are automatically marked as one-shot tasks.
      * 
-     * @return true if the task should execute only once
+     * @return {@code true} if the task should execute only once
      */
     public boolean isOneShot() {
         return oneShot;
@@ -456,11 +375,11 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets whether parallel execution is allowed for this task.
+     * Determines whether parallel execution is allowed for this task.
      * If true, multiple instances of this task can run simultaneously.
      * If false, the task uses locking to ensure only one instance runs at a 
time.
      * 
-     * @return true if parallel execution is allowed
+     * @return {@code true} if parallel execution is allowed
      */
     public boolean isAllowParallelExecution() {
         return allowParallelExecution;
@@ -476,7 +395,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the current task status.
+     * Retrieves the current task status.
      * 
      * @return the current status
      */
@@ -495,7 +414,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets additional details about the task's current status.
+     * Retrieves additional details about the task's current status.
      * This may include execution progress, history, or other metadata.
      * 
      * @return map of status details
@@ -514,9 +433,9 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the next scheduled execution date for periodic tasks.
+     * Retrieves the next scheduled execution date for periodic tasks.
      * 
-     * @return the next scheduled execution date or null if not scheduled
+     * @return the next scheduled execution date or {@code null} if not 
scheduled
      */
     public Date getNextScheduledExecution() {
         return nextScheduledExecution;
@@ -532,7 +451,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the number of consecutive execution failures.
+     * Retrieves the number of consecutive execution failures.
      * 
      * @return the failure count
      */
@@ -550,7 +469,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the number of successful executions.
+     * Retrieves the number of successful executions.
      * 
      * @return the success count
      */
@@ -568,7 +487,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the maximum number of retry attempts after failures.
+     * Retrieves the maximum number of retry attempts after failures.
      * For one-shot tasks:
      * - When a task fails, it will be automatically retried up to this many 
times
      * - Each retry attempt occurs after waiting for retryDelay
@@ -597,7 +516,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the delay between retry attempts.
+     * Retrieves the delay between retry attempts.
      * For one-shot tasks:
      * - This delay is applied between each retry attempt after a failure
      * - Helps prevent rapid-fire retries that could overload the system
@@ -622,10 +541,10 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the name of the current execution step.
+     * Retrieves the name of the current execution step.
      * This is used to track progress through multi-step tasks.
      * 
-     * @return the current step name or null if not set
+     * @return the current step name or {@code null} if not set
      */
     public String getCurrentStep() {
         return currentStep;
@@ -641,10 +560,10 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the checkpoint data for task resumption.
+     * Retrieves the checkpoint data for task resumption.
      * This data allows a task to resume from where it left off after a crash.
      * 
-     * @return map of checkpoint data or null if no checkpoint
+     * @return map of checkpoint data or {@code null} if no checkpoint
      */
     public Map<String, Object> getCheckpointData() {
         return checkpointData;
@@ -660,25 +579,32 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets whether this task is stored persistently.
+     * Determines whether this task is stored persistently.
      * Persistent tasks survive system restarts and are visible across the 
cluster.
      * Non-persistent tasks exist only in memory on a single node.
      * 
-     * @return true if the task is persistent
+     * @return {@code true} if the task is persistent
      */
     public boolean isPersistent() {
         return persistent;
     }
 
+    /**
+     * Sets whether this task is stored persistently.
+     * Persistent tasks survive system restarts and are visible across the 
cluster.
+     * Non-persistent tasks exist only in memory on a single node.
+     *
+     * @param persistent {@code true} to persist the task, {@code false} for 
in-memory execution
+     */
     public void setPersistent(boolean persistent) {
         this.persistent = persistent;
     }
 
     /**
-     * Gets whether this task should run on all cluster nodes.
+     * Determines whether this task should run on all cluster nodes.
      * If false, the task runs only on executor nodes.
      * 
-     * @return true if the task should run on all nodes
+     * @return {@code true} if the task should run on all nodes
      */
     public boolean isRunOnAllNodes() {
         return runOnAllNodes;
@@ -694,11 +620,11 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets whether this task is a system task.
+     * Determines whether this task is a system task.
      * System tasks are created by the system during initialization and should 
be 
      * preserved across restarts rather than being recreated.
      * 
-     * @return true if the task is a system task
+     * @return {@code true} if the task is a system task
      */
     public boolean isSystemTask() {
         return systemTask;
@@ -706,18 +632,20 @@ public class ScheduledTask extends Item implements 
Serializable {
 
     /**
      * Sets whether this task is a system task.
-     * 
-     * @param systemTask true to mark the task as a system task
+     * System tasks are created during initialization and should be preserved 
across
+     * restarts rather than being recreated.
+     *
+     * @param systemTask {@code true} to mark the task as a system task
      */
     public void setSystemTask(boolean systemTask) {
         this.systemTask = systemTask;
     }
 
     /**
-     * Gets the task type that this task is waiting for a lock on.
+     * Retrieves the task type that this task is waiting for a lock on.
      * This is used when tasks of the same type cannot run in parallel.
      * 
-     * @return the task type being waited on or null if not waiting
+     * @return the task type being waited on or {@code null} if not waiting
      */
     public String getWaitingForTaskType() {
         return waitingForTaskType;
@@ -733,7 +661,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the set of task IDs that this task depends on.
+     * Retrieves the set of task IDs that this task depends on.
      * The task will not execute until all dependencies have completed.
      * 
      * @return set of dependency task IDs
@@ -752,7 +680,7 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the set of task IDs that this task is currently waiting on.
+     * Retrieves the set of task IDs that this task is currently waiting on.
      * This represents the subset of dependencies that have not yet completed.
      * 
      * @return set of task IDs being waited on
@@ -818,11 +746,11 @@ public class ScheduledTask extends Item implements 
Serializable {
     }
 
     /**
-     * Gets the ID of the node currently executing this task.
+     * Retrieves the ID of the node currently executing this task.
      * This is different from lockOwner as it specifically indicates which node
      * is actively executing the task, not just holding the lock.
      * 
-     * @return the ID of the executing node or null if not being executed
+     * @return the ID of the executing node or {@code null} if not being 
executed
      */
     public String getExecutingNodeId() {
         return executingNodeId;
@@ -837,6 +765,11 @@ public class ScheduledTask extends Item implements 
Serializable {
         this.executingNodeId = executingNodeId;
     }
 
+    /**
+     * Returns a diagnostic string representation of this task for logging and 
debugging.
+     *
+     * @return a string containing the main task fields
+     */
     @Override
     public String toString() {
         return "ScheduledTask{" +
diff --git 
a/api/src/main/java/org/apache/unomi/api/tenants/security/TenantSecurityService.java
 
b/api/src/main/java/org/apache/unomi/api/tenants/security/TenantSecurityService.java
index 0e5a80397..e9116d3a0 100644
--- 
a/api/src/main/java/org/apache/unomi/api/tenants/security/TenantSecurityService.java
+++ 
b/api/src/main/java/org/apache/unomi/api/tenants/security/TenantSecurityService.java
@@ -40,7 +40,6 @@ public interface TenantSecurityService {
      *
      * @param tenantId the ID of the tenants to configure
      * @param settings the security settings to apply
-     * @throws ConfigurationException if the settings are invalid or cannot be 
applied
      */
     void configureSecuritySettings(String tenantId, SecuritySettings settings);
 
diff --git a/api/src/main/java/org/apache/unomi/api/utils/ConditionBuilder.java 
b/api/src/main/java/org/apache/unomi/api/utils/ConditionBuilder.java
index 9871d6a19..c2ce9b02e 100644
--- a/api/src/main/java/org/apache/unomi/api/utils/ConditionBuilder.java
+++ b/api/src/main/java/org/apache/unomi/api/utils/ConditionBuilder.java
@@ -216,6 +216,7 @@ public class ConditionBuilder {
         return new ConditionItem(conditionTypeId, definitionsService);
     }
 
+    /** Base class for comparison-based condition items. */
     public abstract class ComparisonCondition extends ConditionItem {
 
         /**
@@ -857,7 +858,9 @@ public class ConditionBuilder {
      */
     public class ConditionItem {
 
+        /** The underlying condition. */
         protected Condition condition;
+        /** Service for resolving condition definitions. */
         protected DefinitionsService definitionsService;
 
         /**
diff --git a/build.sh b/build.sh
index 7ffa3426c..a88ce8a0f 100755
--- a/build.sh
+++ b/build.sh
@@ -272,6 +272,9 @@ IT_DEBUG_PORT=5006
 IT_DEBUG_SUSPEND=false
 SKIP_MIGRATION_TESTS=false
 KEEP_CONTAINER=false
+JAVADOC=false
+LOG_FILE=""
+LOG_FILE_ONLY=false
 
 # Enhanced usage function with color support
 usage() {
@@ -312,7 +315,10 @@ EOF
         echo -e "  ${CYAN}--it-debug-suspend${NC}         Suspend integration 
test until debugger connects"
         echo -e "  ${CYAN}--skip-migration-tests${NC}     Skip 
migration-related tests"
         echo -e "  ${CYAN}--keep-container${NC}           Keep search engine 
container running after tests (for post-failure inspection)"
-        echo -e "  ${CYAN}--ci${NC}                       CI mode: no Karaf, 
no Maven build cache, non-interactive"
+        echo -e "  ${CYAN}--javadoc${NC}                  Build and validate 
Javadoc after install (fails on doclint errors)"
+        echo -e "  ${CYAN}--ci${NC}                       CI mode: no Karaf, 
no Maven build cache, non-interactive, includes Javadoc"
+        echo -e "  ${CYAN}--log-file PATH${NC}            Tee all output to 
PATH (console + file)"
+        echo -e "  ${CYAN}--log-file-only${NC}            With --log-file: 
write to file only, suppress console"
     else
         cat << "EOF"
      _    _ _____ _      ____
@@ -348,7 +354,10 @@ EOF
         echo "  --it-debug-suspend        Suspend integration test until 
debugger connects"
         echo "  --skip-migration-tests    Skip migration-related tests"
         echo "  --keep-container          Keep search engine container running 
after tests (for post-failure inspection)"
-        echo "  --ci                      CI mode: no Karaf, no Maven build 
cache, non-interactive"
+        echo "  --javadoc                 Build and validate Javadoc after 
install (fails on doclint errors)"
+        echo "  --ci                      CI mode: no Karaf, no Maven build 
cache, non-interactive, includes Javadoc"
+        echo "  --log-file PATH           Tee all output to PATH (console + 
file)"
+        echo "  --log-file-only           With --log-file: write to file only, 
suppress console"
     fi
 
     echo
@@ -496,11 +505,22 @@ while [ "$1" != "" ]; do
         --keep-container)
             KEEP_CONTAINER=true
             ;;
+        --javadoc)
+            JAVADOC=true
+            ;;
+        --log-file)
+            shift
+            LOG_FILE="$1"
+            ;;
+        --log-file-only)
+            LOG_FILE_ONLY=true
+            ;;
         --ci)
             NO_KARAF=true
             USE_MAVEN_CACHE=false
             BUILD_NON_INTERACTIVE=true
             MAVEN_QUIET=true
+            JAVADOC=true
             ;;
         *)
             echo "Unknown option: $1"
@@ -510,6 +530,20 @@ while [ "$1" != "" ]; do
     shift
 done
 
+# Wire up log file output if requested
+if [ "$LOG_FILE_ONLY" = true ] && [ -z "$LOG_FILE" ]; then
+    echo "Error: --log-file-only requires --log-file PATH"
+    exit 1
+fi
+
+if [ -n "$LOG_FILE" ]; then
+    if [ "$LOG_FILE_ONLY" = true ]; then
+        exec > "$LOG_FILE" 2>&1
+    else
+        exec > >(tee -a "$LOG_FILE") 2>&1
+    fi
+fi
+
 # Set environment
 DIRNAME=`dirname "$0"`
 PROGNAME=`basename "$0"`
@@ -522,6 +556,10 @@ if [ "$PURGE_MAVEN_CACHE" = true ]; then
     echo "Purging Maven cache..."
     rm -rf ~/.m2/build-cache ~/.m2/dependency-cache ~/.m2/dependency-cache_v2
     echo "Maven cache purged."
+    # Disable the build cache for this run: a cold cache with the extension 
still active
+    # causes the workspace resolver to return target/classes instead of built 
JARs,
+    # breaking karaf-maven-plugin:verify. Disabling matches CI behaviour.
+    USE_MAVEN_CACHE=false
 fi
 
 # Function to check if command exists
@@ -974,7 +1012,7 @@ echo "Estimated time: 3-5 minutes for build, 50-60 minutes 
with integration test
 start_timer
 
 # Build phases with enhanced output
-total_steps=2
+[ "$JAVADOC" = true ] && total_steps=3 || total_steps=2
 current_step=0
 
 write_it_run_trace_start() {
@@ -1056,6 +1094,21 @@ fi
 
 print_status "success" "Build completed in $(get_elapsed_time)"
 
+if [ "$JAVADOC" = true ]; then
+    print_section "Javadoc Validation"
+    print_progress $((++current_step)) $total_steps "Generating and validating 
Javadoc..."
+    if [ "$HAS_COLORS" -eq 1 ]; then
+        echo -e "${GRAY}Running: $MVN_CMD javadoc:javadoc -DskipTests 
$MVN_OPTS${NC}"
+    else
+        echo "Running: $MVN_CMD javadoc:javadoc -DskipTests $MVN_OPTS"
+    fi
+    $MVN_CMD javadoc:javadoc -DskipTests $MVN_OPTS || {
+        print_status "error" "Javadoc validation failed — fix doclint errors 
above before pushing"
+        exit 1
+    }
+    print_status "success" "Javadoc validated successfully"
+fi
+
 # Deployment section with enhanced output
 if [ "$DEPLOY" = true ]; then
     # Validate Karaf home directory
diff --git 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/IRouterCamelContext.java
 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/IRouterCamelContext.java
index 476b36e82..c6835f143 100644
--- 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/IRouterCamelContext.java
+++ 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/IRouterCamelContext.java
@@ -29,14 +29,12 @@ package org.apache.unomi.router.api;
  *   <li>Rebuilding export reader routes after an {@link 
org.apache.unomi.router.api.ExportConfiguration} update</li>
  *   <li>Optional Camel tracing for troubleshooting route execution</li>
  * </ul>
- * </p>
  *
  * <p>Typical usage:
  * <ul>
  *   <li>Management services call update methods when import/export 
configuration documents change</li>
  *   <li>Cleanup paths call {@link #killExistingRoute(String, boolean)} to 
drop routes whose configs were removed</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
diff --git 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportExportConfiguration.java
 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportExportConfiguration.java
index 7c9b5e238..782c7903e 100644
--- 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportExportConfiguration.java
+++ 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportExportConfiguration.java
@@ -35,7 +35,6 @@ import java.util.Map;
  *   <li>Tracks execution status and history</li>
  *   <li>Handles configuration activation/deactivation</li>
  * </ul>
- * </p>
  *
  * <p>Usage in Unomi:
  * <ul>
@@ -43,7 +42,6 @@ import java.util.Map;
  *   <li>Consumed by Camel routes to determine how to process data</li>
  *   <li>Referenced by import/export processors to format data correctly</li>
  * </ul>
- * </p>
  *
  * <p>Configuration properties include:
  * <ul>
@@ -58,7 +56,6 @@ import java.util.Map;
  *   <li>status - current status of the configuration</li>
  *   <li>executions - history of execution attempts</li>
  * </ul>
- * </p>
  *
  * @see org.apache.unomi.router.api.services.ImportExportConfigurationService
  * @since 1.0
diff --git 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ProfileToImport.java
 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ProfileToImport.java
index 957ac88e9..0ea3a94de 100644
--- 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ProfileToImport.java
+++ 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ProfileToImport.java
@@ -33,7 +33,6 @@ import java.util.List;
  *   <li>Handles profile deletion flags</li>
  *   <li>Controls merge vs full-replace behavior for existing profiles (see 
{@link #isOverwriteExistingProfiles()})</li>
  * </ul>
- * </p>
  *
  * <p>Usage in Unomi:
  * <ul>
@@ -41,7 +40,6 @@ import java.util.List;
  *   <li>Consumed by ProfileImportService for import operations</li>
  *   <li>Supports different import strategies (merge/overwrite/delete)</li>
  * </ul>
- * </p>
  *
  * @see Profile
  * @see org.apache.unomi.router.api.services.ProfileImportService
diff --git 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/exceptions/BadProfileDataFormatException.java
 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/exceptions/BadProfileDataFormatException.java
index c4156e929..a348d605d 100644
--- 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/exceptions/BadProfileDataFormatException.java
+++ 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/exceptions/BadProfileDataFormatException.java
@@ -28,14 +28,12 @@ package org.apache.unomi.router.api.exceptions;
  *   <li>Malformed multi-value fields</li>
  *   <li>Empty lines in import files</li>
  * </ul>
- * </p>
  *
  * <p>Usage in Unomi:
  * <ul>
  *   <li>Thrown by import line processors (e.g. {@code 
LineSplitProcessor})</li>
  *   <li>Handled by import route error handlers</li>
  * </ul>
- * </p>
  *
  * @see org.apache.unomi.router.api.ProfileToImport
  * @since 1.0
diff --git 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ImportExportConfigurationService.java
 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ImportExportConfigurationService.java
index ba6d321b9..705a258f0 100644
--- 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ImportExportConfigurationService.java
+++ 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ImportExportConfigurationService.java
@@ -35,7 +35,6 @@ import java.util.Map;
  *   <li>Coordinating with Camel routes for configuration updates</li>
  *   <li>Tracking configuration changes that need route updates</li>
  * </ul>
- * </p>
  *
  * <p>Usage in Unomi:
  * <ul>
@@ -43,7 +42,6 @@ import java.util.Map;
  *   <li>Consumed by Camel routes to get configuration updates</li>
  *   <li>Utilized by admin interfaces for configuration management</li>
  * </ul>
- * </p>
  *
  * <p>Implementation considerations:
  * <ul>
@@ -51,7 +49,6 @@ import java.util.Map;
  *   <li>Thread safety should be considered for concurrent operations</li>
  *   <li>Configuration changes should be properly propagated to running 
routes</li>
  * </ul>
- * </p>
  *
  * @param <T> The type of configuration (ImportConfiguration or 
ExportConfiguration)
  * @see ImportConfiguration
diff --git 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileExportService.java
 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileExportService.java
index ccdc3711b..506a9a1f1 100644
--- 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileExportService.java
+++ 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileExportService.java
@@ -34,7 +34,6 @@ import java.util.Collection;
  *   <li>Handling data formatting and transformation</li>
  *   <li>Managing export file generation</li>
  * </ul>
- * </p>
  *
  * <p>Usage in Unomi:
  * <ul>
@@ -42,7 +41,6 @@ import java.util.Collection;
  *   <li>Used during scheduled export operations</li>
  *   <li>Integrated with Unomi's segmentation system</li>
  * </ul>
- * </p>
  *
  * <p>Implementation considerations:
  * <ul>
@@ -51,7 +49,6 @@ import java.util.Collection;
  *   <li>Must respect profile property formatting</li>
  *   <li>Should handle multi-valued properties</li>
  * </ul>
- * </p>
  *
  * @see Profile
  * @see ExportConfiguration
diff --git 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileImportService.java
 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileImportService.java
index 0d008bbee..aafffba13 100644
--- 
a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileImportService.java
+++ 
b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/services/ProfileImportService.java
@@ -32,7 +32,6 @@ import java.lang.reflect.InvocationTargetException;
  *   <li>Handling profile creation for new imports</li>
  *   <li>Managing profile deletion when specified</li>
  * </ul>
- * </p>
  *
  * <p>Usage in Unomi:
  * <ul>
@@ -40,7 +39,6 @@ import java.lang.reflect.InvocationTargetException;
  *   <li>Used during batch import operations</li>
  *   <li>Integrated with Unomi's profile management system</li>
  * </ul>
- * </p>
  *
  * <p>Implementation considerations:
  * <ul>
@@ -49,7 +47,6 @@ import java.lang.reflect.InvocationTargetException;
  *   <li>Must maintain data consistency</li>
  *   <li>Expects property values already parsed (type conversion is done 
upstream, e.g. by import processors)</li>
  * </ul>
- * </p>
  *
  * @see ProfileToImport
  * @see org.apache.unomi.api.Profile
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/bean/CollectProfileBean.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/bean/CollectProfileBean.java
index a62b19ece..d0454e760 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/bean/CollectProfileBean.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/bean/CollectProfileBean.java
@@ -34,7 +34,6 @@ import java.util.List;
  *   <li>Segment-based profile extraction via persistence queries</li>
  *   <li>Integration with Unomi's persistence service</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/context/RouterCamelContext.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/context/RouterCamelContext.java
index 090d6927e..711b88915 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/context/RouterCamelContext.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/context/RouterCamelContext.java
@@ -66,7 +66,6 @@ import java.util.concurrent.TimeUnit;
  *   <li>Supports Kafka ({@link RouterConstants#CONFIG_TYPE_KAFKA}) and 
in-process
  *       {@code direct:} endpoints when configured as {@link 
RouterConstants#CONFIG_TYPE_NOBROKER}</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ExportRouteCompletionProcessor.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ExportRouteCompletionProcessor.java
index 7f7a7e776..a45a524e0 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ExportRouteCompletionProcessor.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ExportRouteCompletionProcessor.java
@@ -41,7 +41,6 @@ import java.util.Map;
  *   <li>Maintains execution history within configured size limits</li>
  *   <li>Persists updated configuration information</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -66,7 +65,6 @@ public class ExportRouteCompletionProcessor implements 
Processor {
      *   <li>Maintains the execution history size limit</li>
      *   <li>Updates the export status to complete</li>
      * </ul>
-     * </p>
      *
      * @param exchange the Camel exchange containing export execution details
      * @throws Exception if an error occurs during processing
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportConfigByFileNameProcessor.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportConfigByFileNameProcessor.java
index 2673898ea..0ac7d4499 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportConfigByFileNameProcessor.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportConfigByFileNameProcessor.java
@@ -38,7 +38,7 @@ import java.nio.file.Paths;
  *
  * <p>The processor expects filenames in the format:
  * <pre>configurationId.extension</pre>
- * where the configurationId matches an existing import configuration.</p>
+ * where the configurationId matches an existing import configuration.
  *
  * <p>Features:
  * <ul>
@@ -47,7 +47,6 @@ import java.nio.file.Paths;
  *   <li>Sets configuration in exchange header for processing</li>
  *   <li>Handles missing configurations gracefully</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -73,7 +72,6 @@ public class ImportConfigByFileNameProcessor implements 
Processor {
      *   <li>Sets the configuration in the exchange header if found</li>
      *   <li>Stops route processing if no configuration is found</li>
      * </ul>
-     * </p>
      *
      * @param exchange the Camel exchange containing the file to process
      * @throws Exception if an error occurs during processing
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportRouteCompletionProcessor.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportRouteCompletionProcessor.java
index e34d965b6..25784d6c5 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportRouteCompletionProcessor.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/ImportRouteCompletionProcessor.java
@@ -38,7 +38,6 @@ import java.util.*;
  *   <li>Maintains execution history</li>
  *   <li>Handles both one-shot and recurring imports</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -66,7 +65,6 @@ public class ImportRouteCompletionProcessor implements 
Processor {
      *   <li>Updates the import configuration with execution results</li>
      *   <li>Sets the final status based on success/failure counts</li>
      * </ul>
-     * </p>
      *
      * @param exchange the Camel exchange containing import results
      * @throws Exception if an error occurs during processing
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineBuildProcessor.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineBuildProcessor.java
index f1748cc0a..2170efc3f 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineBuildProcessor.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineBuildProcessor.java
@@ -55,7 +55,6 @@ public class LineBuildProcessor implements Processor {
      *   <li>Converts the profile to a CSV line using the 
ProfileExportService</li>
      *   <li>Sets the resulting string as the new exchange body</li>
      * </ul>
-     * </p>
      *
      * @param exchange the Camel exchange containing the Profile to convert 
and export configuration
      * @throws Exception if an error occurs during processing
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitFailureHandler.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitFailureHandler.java
index 2f4a5950d..4b68397b2 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitFailureHandler.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitFailureHandler.java
@@ -34,7 +34,6 @@ import org.slf4j.LoggerFactory;
  *   <li>BadProfileDataFormatException - for data format related errors</li>
  *   <li>General exceptions - capturing the root cause message</li>
  * </ul>
- * </p>
  *
  * <p>For each failure, it creates an ImportLineError object containing:
  * <ul>
@@ -42,7 +41,6 @@ import org.slf4j.LoggerFactory;
  *   <li>The content of the failed line</li>
  *   <li>The line number in the source file</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -60,7 +58,6 @@ public class LineSplitFailureHandler implements Processor {
      *   <li>Extracts the appropriate error message based on the exception 
type</li>
      *   <li>Sets the failure information in the exchange for further 
processing</li>
      * </ul>
-     * </p>
      *
      * @param exchange the Camel exchange containing the failed message and 
exception details
      * @throws Exception if an error occurs during failure handling
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitProcessor.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitProcessor.java
index e93d55637..90d9d4fb8 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitProcessor.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/LineSplitProcessor.java
@@ -49,7 +49,6 @@ import java.util.*;
  *   <li>Profile merging configuration</li>
  *   <li>Delete operation support</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -101,7 +100,6 @@ public class LineSplitProcessor implements Processor {
      *   <li>Sets up profile merging configuration</li>
      *   <li>Processes delete operations if configured</li>
      * </ul>
-     * </p>
      *
      * @param exchange the Camel exchange containing the CSV line to process
      * @throws Exception if an error occurs during processing, including 
BadProfileDataFormatException
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/UnomiStorageProcessor.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/UnomiStorageProcessor.java
index 99dbe0775..d7335e8ed 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/UnomiStorageProcessor.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/processor/UnomiStorageProcessor.java
@@ -43,7 +43,6 @@ import java.util.Set;
  *   <li>Updates profile information with calculated segments</li>
  *   <li>Persists profiles in the Unomi storage system</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -70,7 +69,6 @@ public class UnomiStorageProcessor implements Processor {
      *   <li>For non-delete operations, calculates and updates segments and 
scores</li>
      *   <li>Persists the profile using the ProfileImportService</li>
      * </ul>
-     * </p>
      *
      * @param exchange the Camel exchange containing the profile to process
      * @throws Exception if an error occurs during processing
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportCollectRouteBuilder.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportCollectRouteBuilder.java
index 27a618c4b..48fb967f0 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportCollectRouteBuilder.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportCollectRouteBuilder.java
@@ -45,7 +45,6 @@ import java.util.Map;
  *   <li>Security through endpoint allowlist</li>
  *   <li>Support for Kafka and in-process {@code direct:} endpoints ({@link 
RouterConstants#CONFIG_TYPE_KAFKA} / {@link 
RouterConstants#CONFIG_TYPE_NOBROKER})</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -82,7 +81,6 @@ public class ProfileExportCollectRouteBuilder extends 
RouterAbstractRouteBuilder
      *   <li>Processes profiles for export</li>
      *   <li>Routes data to appropriate endpoints</li>
      * </ul>
-     * </p>
      *
      * @throws Exception if an error occurs during route configuration
      */
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportProducerRouteBuilder.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportProducerRouteBuilder.java
index 89619b7da..e9f52a2a3 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportProducerRouteBuilder.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileExportProducerRouteBuilder.java
@@ -41,7 +41,6 @@ import java.util.Map;
  *   <li>Completion handling and status updates</li>
  *   <li>Support for Kafka and in-process {@code direct:} endpoints ({@link 
RouterConstants#CONFIG_TYPE_KAFKA} / {@link 
RouterConstants#CONFIG_TYPE_NOBROKER})</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -86,7 +85,6 @@ public class ProfileExportProducerRouteBuilder extends 
RouterAbstractRouteBuilde
      *   <li>Handles export completion</li>
      *   <li>Routes data to configured destinations</li>
      * </ul>
-     * </p>
      *
      * @throws Exception if an error occurs during route configuration
      */
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java
index 845f023c1..f7906f3f5 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java
@@ -52,7 +52,6 @@ import java.util.Map;
  *   <li>Support for Kafka and in-process {@code direct:} endpoints ({@link 
RouterConstants#CONFIG_TYPE_KAFKA} / {@link 
RouterConstants#CONFIG_TYPE_NOBROKER})</li>
  *   <li>Graceful shutdown handling</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -92,7 +91,6 @@ public class ProfileImportFromSourceRouteBuilder extends 
RouterAbstractRouteBuil
      *   <li>Route processed data to appropriate endpoints</li>
      *   <li>Manage graceful completion of imports</li>
      * </ul>
-     * </p>
      *
      * @throws Exception if an error occurs during route configuration
      */
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportOneShotRouteBuilder.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportOneShotRouteBuilder.java
index 3f0d8ca1a..3c9575cac 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportOneShotRouteBuilder.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportOneShotRouteBuilder.java
@@ -43,7 +43,6 @@ import java.util.Map;
  *   <li>Automatic file movement after processing</li>
  *   <li>Error reporting and failed file handling</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -79,7 +78,6 @@ public class ProfileImportOneShotRouteBuilder extends 
RouterAbstractRouteBuilder
      *   <li>Handles validation and format errors</li>
      *   <li>Routes processed data to appropriate endpoints</li>
      * </ul>
-     * </p>
      *
      * @throws Exception if an error occurs during route configuration
      */
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportToUnomiRouteBuilder.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportToUnomiRouteBuilder.java
index 7f31e57bb..0e463018e 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportToUnomiRouteBuilder.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportToUnomiRouteBuilder.java
@@ -40,7 +40,6 @@ import java.util.Map;
  *   <li>Import completion handling</li>
  *   <li>Error handling and reporting</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -75,7 +74,6 @@ public class ProfileImportToUnomiRouteBuilder extends 
RouterAbstractRouteBuilder
      *   <li>Handles import completion</li>
      *   <li>Manages error reporting</li>
      * </ul>
-     * </p>
      *
      * @throws Exception if an error occurs during route configuration
      */
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java
index 69586990f..720274ec0 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java
@@ -41,7 +41,6 @@ import java.util.Map;
  *   <li>Profile service integration</li>
  *   <li>Endpoint security through allowlist</li>
  * </ul>
- * </p>
  *
  * @since 1.0
  */
@@ -110,7 +109,6 @@ public abstract class RouterAbstractRouteBuilder extends 
RouteBuilder {
      *   <li>Returns direct endpoint URIs when not using Kafka</li>
      *   <li>Configures consumer properties for incoming endpoints</li>
      * </ul>
-     * </p>
      *
      * @param direction the direction of the endpoint (to/from)
      * @param operationDepositBuffer the operation buffer identifier
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/ArrayListAggregationStrategy.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/ArrayListAggregationStrategy.java
index c113e3aeb..93f3cb8d6 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/ArrayListAggregationStrategy.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/ArrayListAggregationStrategy.java
@@ -31,7 +31,6 @@ import java.util.ArrayList;
  *   <li>For the first message (when oldExchange is null), it creates a new 
ArrayList and adds the message body to it</li>
  *   <li>For subsequent messages, it adds the new message body to the existing 
ArrayList</li>
  * </ul>
- * </p>
  *
  * <p>The ArrayList is maintained in the exchange body, allowing for easy 
access to all aggregated items
  * once the aggregation is complete.</p>
@@ -50,7 +49,6 @@ public class ArrayListAggregationStrategy implements 
AggregationStrategy {
      *   <li>The new body is added to the ArrayList</li>
      *   <li>The ArrayList is maintained in the exchange body for subsequent 
aggregations</li>
      * </ul>
-     * </p>
      *
      * @param oldExchange the previous exchange being aggregated (may be null 
on first invocation)
      * @param newExchange the current exchange being aggregated (contains the 
new item to add to the list)
diff --git 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/StringLinesAggregationStrategy.java
 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/StringLinesAggregationStrategy.java
index 8ccabe687..82f0ad2cc 100644
--- 
a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/StringLinesAggregationStrategy.java
+++ 
b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/strategy/StringLinesAggregationStrategy.java
@@ -31,7 +31,6 @@ import org.apache.unomi.router.api.RouterUtils;
  *   <li>For the first message (when oldExchange is null), it simply returns 
the new exchange</li>
  *   <li>For subsequent messages, it appends the new content to the existing 
content using the configured line separator</li>
  * </ul>
- * </p>
  *
  * <p>The line separator used for aggregation is obtained from the 
ExportConfiguration object
  * stored in the exchange header under the key "exportConfig".</p>
@@ -50,7 +49,6 @@ public class StringLinesAggregationStrategy implements 
AggregationStrategy {
      *   <li>If there's an old exchange, the new content is appended to it 
with the line separator</li>
      *   <li>If there's no old exchange, the new exchange is returned as 
is</li>
      * </ul>
-     * </p>
      *
      * @param oldExchange the previous exchange being aggregated (may be null 
on first invocation)
      * @param newExchange the current exchange being aggregated (contains the 
new line to append)
diff --git 
a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java 
b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java
index 540c3ebff..b75a83ecb 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java
@@ -102,7 +102,8 @@ public class GraphQLListIT extends BaseGraphQLIT {
                         Object profileId = 
context.getValue("data.cdp.findLists.edges[0].node.active.edges[0].node.cdp_profileIDs[0].id");
                         return profile.getItemId().equals(profileId);
                     },
-                    DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+                    // async rule-engine processing needs more retries on 
loaded CI
+                    DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES * 3);
 
             Assert.assertEquals("testListId", 
findListsContext.getValue("data.cdp.findLists.edges[0].node.id"));
 
diff --git 
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/ConditionContextHelper.java
 
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/ConditionContextHelper.java
index 5e96d2d6d..e338fafea 100644
--- 
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/ConditionContextHelper.java
+++ 
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/ConditionContextHelper.java
@@ -127,10 +127,8 @@ public class ConditionContextHelper {
      * @param context context map for parameter resolution
      * @param scriptExecutor executor for script expressions
      * @param definitionsService optional service for parameter type 
information
-     * @param tracerService optional tracer service for validation warnings
      * @return resolved condition with all parameter references resolved
      */
-
     public static Condition getContextualCondition(
         Condition condition,
         Map<String, Object> context,
@@ -139,6 +137,17 @@ public class ConditionContextHelper {
         return getContextualCondition(condition, context, scriptExecutor, 
definitionsService, null);
     }
 
+    /**
+     * Resolves parameter references and script expressions in a condition,
+     * with optional type validation and execution tracing.
+     *
+     * @param condition the condition to resolve
+     * @param context context map for parameter resolution
+     * @param scriptExecutor executor for script expressions
+     * @param definitionsService optional service for parameter type 
information
+     * @param tracerService optional tracer service for validation warnings
+     * @return resolved condition with all parameter references resolved
+     */
     public static Condition getContextualCondition(
         Condition condition,
         Map<String, Object> context,
diff --git 
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
 
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
index 3adffc589..ba4edbda6 100644
--- 
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
+++ 
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
@@ -51,7 +51,6 @@ import java.util.Map;
  *   the result means "events occurred within bounds" or "no events 
occurred".</li>
  * </ul>
  *
- * @see 
org.apache.unomi.plugins.advancedconditions.conditions.PastEventConditionEvaluator
  */
 public interface PastEventConditionPersistenceQueryBuilder {
 
diff --git 
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
 
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
index 8259311cb..791f6ce94 100644
--- 
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
+++ 
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
@@ -42,7 +42,6 @@ import java.util.Map;
  * {@code PastEventConditionEvaluator} for a typical evaluator.
  *
  * @see 
org.apache.unomi.persistence.spi.conditions.evaluator.impl.ConditionEvaluatorDispatcherImpl
- * @see 
org.apache.unomi.plugins.advancedconditions.conditions.PastEventConditionEvaluator
  */
 public interface ConditionEvaluatorDispatcher {
 
diff --git 
a/services-common/src/main/java/org/apache/unomi/services/common/cache/AbstractMultiTypeCachingService.java
 
b/services-common/src/main/java/org/apache/unomi/services/common/cache/AbstractMultiTypeCachingService.java
index a5d29f809..499c7aa7d 100644
--- 
a/services-common/src/main/java/org/apache/unomi/services/common/cache/AbstractMultiTypeCachingService.java
+++ 
b/services-common/src/main/java/org/apache/unomi/services/common/cache/AbstractMultiTypeCachingService.java
@@ -49,7 +49,15 @@ import java.util.stream.Collectors;
 import static org.apache.unomi.api.tenants.TenantService.SYSTEM_TENANT;
 
 /**
- * Base service supporting multiple cacheable types
+ * Abstract base for services that cache multiple {@link CacheableTypeConfig} 
types per tenant.
+ * <p>
+ * Handles OSGi bundle lifecycle (loading predefined JSON from {@code 
META-INF/cxs/}), periodic
+ * cache refresh via {@link SchedulerService}, tenant-aware persistence 
queries, and system-tenant
+ * inheritance. Concrete implementations include {@code 
DefinitionsServiceImpl},
+ * {@code SegmentServiceImpl}, and {@code RulesServiceImpl}.
+ *
+ * @see MultiTypeCacheService
+ * @see CacheableTypeConfig
  */
 public abstract class AbstractMultiTypeCachingService extends 
AbstractContextAwareService implements SynchronousBundleListener {
 
@@ -81,26 +89,55 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
     // Each service defines its supported types
     protected abstract Set<CacheableTypeConfig<?>> getTypeConfigs();
 
+    /**
+     * Sets the OSGi bundle context used for bundle lifecycle listening and 
predefined item loading.
+     *
+     * @param bundleContext the bundle context
+     */
     public void setBundleContext(BundleContext bundleContext) {
         this.bundleContext = bundleContext;
     }
 
+    /**
+     * Sets the scheduler service used for periodic cache refresh tasks.
+     *
+     * @param schedulerService the scheduler service
+     */
     public void setSchedulerService(SchedulerService schedulerService) {
         this.schedulerService = schedulerService;
     }
 
+    /**
+     * Sets the multi-type cache service backing this service.
+     *
+     * @param cacheService the cache service
+     */
     public void setCacheService(MultiTypeCacheService cacheService) {
         this.cacheService = cacheService;
     }
 
+    /**
+     * Sets the tenant service used to enumerate tenants during cache refresh.
+     *
+     * @param tenantService the tenant service
+     */
     public void setTenantService(TenantService tenantService) {
         this.tenantService = tenantService;
     }
 
+    /**
+     * Sets the audit service used when saving items to persistence.
+     *
+     * @param auditService the audit service
+     */
     public void setAuditService(AuditService auditService) {
         this.auditService = auditService;
     }
 
+    /**
+     * Initializes caches, loads predefined items from bundles, registers a 
bundle listener,
+     * loads initial persistence data, and starts refresh timers.
+     */
     public void postConstruct() {
         logger.debug("postConstruct {{}}", bundleContext.getBundle());
 
@@ -148,6 +185,9 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
         }
     }
 
+    /**
+     * Shuts down the service by removing the bundle listener and cancelling 
refresh timers.
+     */
     public void preDestroy() {
         bundleContext.removeBundleListener(this);
         shutdownTimers();
@@ -441,7 +481,7 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
     }
 
     /**
-     * Get all items contributed by a specific bundle.
+     * Retrieves all items contributed by a specific bundle.
      *
      * @param bundleId the ID of the bundle
      * @return a list of items contributed by that bundle, or an empty list if 
none
@@ -575,6 +615,11 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
         }
     }
 
+    /**
+     * Handles OSGi bundle start and stop events to load or remove predefined 
items.
+     *
+     * @param event the bundle event
+     */
     @Override
     public void bundleChanged(BundleEvent event) {
         contextManager.executeAsSystem(() -> {
@@ -672,16 +717,16 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
     }
 
     /**
-     * Get a map of all plugin types indexed by plugin ID (bundle ID).
+     * Retrieves a map of plugin types indexed by contributing bundle 
identifier.
      *
-     * @return Map where key is the bundle ID, value is the list of plugin 
types from that bundle
+     * @return map of bundle ID to plugin types contributed by that bundle
      */
     public Map<Long, List<PluginType>> getTypesByPlugin() {
         return pluginTypes;
     }
 
     /**
-     * Get all items of a specific type for the current tenant.
+     * Retrieves all items of a specific type for the current tenant.
      *
      * @param <T> the type of items to retrieve
      * @param itemClass the class of the items to retrieve
@@ -696,7 +741,7 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
     }
 
     /**
-     * Get items of a specific type filtered by tag.
+     * Retrieves items of a specific type filtered by tag.
      *
      * @param <T> the type of items to retrieve
      * @param itemClass the class of the items to retrieve
@@ -713,7 +758,7 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
     }
 
     /**
-     * Get items of a specific type filtered by system tag.
+     * Retrieves items of a specific type filtered by system tag.
      *
      * @param <T> the type of items to retrieve
      * @param itemClass the class of the items to retrieve
@@ -730,12 +775,12 @@ public abstract class AbstractMultiTypeCachingService 
extends AbstractContextAwa
     }
 
     /**
-     * Get a specific item by ID.
+     * Retrieves a specific item by identifier.
      *
      * @param <T> the type of item to retrieve
      * @param id the ID of the item
      * @param itemClass the class of the item
-     * @return the item with the specified ID, or null if not found
+     * @return the item with the specified identifier, or {@code null} if not 
found
      */
     protected <T extends Serializable> T getItem(String id, Class<T> 
itemClass) {
         String tenantId = contextManager.getCurrentContext().getTenantId();
diff --git 
a/services-common/src/main/java/org/apache/unomi/services/common/security/AuditServiceImpl.java
 
b/services-common/src/main/java/org/apache/unomi/services/common/security/AuditServiceImpl.java
index 1952e0cfa..e6c2e6bc3 100644
--- 
a/services-common/src/main/java/org/apache/unomi/services/common/security/AuditServiceImpl.java
+++ 
b/services-common/src/main/java/org/apache/unomi/services/common/security/AuditServiceImpl.java
@@ -25,15 +25,31 @@ import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
+/**
+ * Default implementation of {@link AuditService} backed by {@link 
PersistenceService}.
+ * <p>
+ * Records create, update, and delete metadata on {@link Item} instances and 
supports
+ * tenant synchronization queries (modified items and last-sync tracking).
+ */
 public class AuditServiceImpl implements AuditService {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(AuditServiceImpl.class);
 
     private PersistenceService persistenceService;
 
+    /**
+     * Binds the persistence service used for sync queries and updates.
+     *
+     * @param persistenceService the persistence service
+     */
     public void bindPersistenceService(PersistenceService persistenceService) {
         this.persistenceService = persistenceService;
     }
 
+    /**
+     * Unbinds the persistence service.
+     *
+     * @param persistenceService the persistence service being unbound
+     */
     public void unbindPersistenceService(PersistenceService 
persistenceService) {
         this.persistenceService = null;
     }
@@ -132,6 +148,12 @@ public class AuditServiceImpl implements AuditService {
         LOGGER.info("Tenant operation: {} performed on tenant {}", operation, 
tenantId);
     }
 
+    /**
+     * Updates the last-modified metadata on an item.
+     *
+     * @param item the item to update
+     * @param userId the user performing the modification
+     */
     public void updateModificationMetadata(Item item, String userId) {
         item.setLastModifiedBy(userId);
         item.setLastModificationDate(new Date());
diff --git 
a/services-common/src/main/java/org/apache/unomi/services/common/security/ExecutionContextManagerImpl.java
 
b/services-common/src/main/java/org/apache/unomi/services/common/security/ExecutionContextManagerImpl.java
index 555338015..6cf39713a 100644
--- 
a/services-common/src/main/java/org/apache/unomi/services/common/security/ExecutionContextManagerImpl.java
+++ 
b/services-common/src/main/java/org/apache/unomi/services/common/security/ExecutionContextManagerImpl.java
@@ -30,6 +30,16 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Supplier;
 
+/**
+ * Thread-local implementation of {@link ExecutionContextManager}.
+ * <p>
+ * Maintains the current {@link ExecutionContext} per thread, derived from the 
active
+ * {@link SecurityService} subject. {@link #executeAsSystem(Supplier)} 
switches both the
+ * security subject and execution context to the system tenant for the 
duration of the
+ * operation, then restores the previous state.
+ *
+ * @see KarafSecurityService
+ */
 public class ExecutionContextManagerImpl implements ExecutionContextManager {
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(ExecutionContextManagerImpl.class);
@@ -37,6 +47,11 @@ public class ExecutionContextManagerImpl implements 
ExecutionContextManager {
     private final ThreadLocal<ExecutionContext> currentContext = new 
ThreadLocal<>();
     private SecurityService securityService;
 
+    /**
+     * Sets the security service used to resolve subjects, roles, and 
permissions.
+     *
+     * @param securityService the security service
+     */
     public void setSecurityService(SecurityService securityService) {
         this.securityService = securityService;
     }
diff --git 
a/services-common/src/main/java/org/apache/unomi/services/common/security/KarafSecurityService.java
 
b/services-common/src/main/java/org/apache/unomi/services/common/security/KarafSecurityService.java
index 8f5ac2686..b084dff8a 100644
--- 
a/services-common/src/main/java/org/apache/unomi/services/common/security/KarafSecurityService.java
+++ 
b/services-common/src/main/java/org/apache/unomi/services/common/security/KarafSecurityService.java
@@ -32,9 +32,20 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+/**
+ * Karaf JAAS-based implementation of {@link SecurityService}.
+ * <p>
+ * Resolves the active {@link Subject} from the JAAS context, a temporary 
privileged subject,
+ * or the current request subject. Role and permission checks consult all 
active subjects in that
+ * order. Provides the system subject used by {@link 
ExecutionContextManagerImpl} for elevated
+ * operations.
+ *
+ * @see ExecutionContextManagerImpl
+ */
 public class KarafSecurityService implements SecurityService {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(KarafSecurityService.class);
 
+    /** The system tenant identifier used for system-wide operations. */
     public static final String SYSTEM_TENANT = "system";
     private final Subject SYSTEM_SUBJECT;
 
@@ -45,6 +56,9 @@ public class KarafSecurityService implements SecurityService {
     private final ThreadLocal<Subject> currentSubject = new ThreadLocal<>();
     private final ThreadLocal<Subject> privilegedSubject = new ThreadLocal<>();
 
+    /**
+     * Creates the security service and initializes the system subject.
+     */
     public KarafSecurityService() {
         SYSTEM_SUBJECT = createSystemSubject();
     }
@@ -58,6 +72,9 @@ public class KarafSecurityService implements SecurityService {
         return subject;
     }
 
+    /**
+     * Initializes the service with default configuration if none was injected.
+     */
     public void init() {
         if (configuration == null) {
             configuration = new SecurityServiceConfiguration();
@@ -65,6 +82,9 @@ public class KarafSecurityService implements SecurityService {
         updateSystemSubject();
     }
 
+    /**
+     * Shuts down the security service.
+     */
     public void destroy() {
         // Cleanup
     }
@@ -78,18 +98,38 @@ public class KarafSecurityService implements 
SecurityService {
         }
     }
 
+    /**
+     * Sets the audit service used for tenant operation logging.
+     *
+     * @param tenantAuditService the tenant audit service
+     */
     public void setTenantAuditService(AuditService tenantAuditService) {
         this.tenantAuditService = tenantAuditService;
     }
 
+    /**
+     * Sets the security configuration (role-to-permission mappings and system 
roles).
+     *
+     * @param configuration the security configuration
+     */
     public void setConfiguration(SecurityServiceConfiguration configuration) {
         this.configuration = configuration;
     }
 
+    /**
+     * Binds the encryption service for tenant key retrieval.
+     *
+     * @param encryptionService the encryption service
+     */
     public void bindEncryptionService(EncryptionService encryptionService) {
         this.encryptionService = encryptionService;
     }
 
+    /**
+     * Unbinds the encryption service.
+     *
+     * @param encryptionService the encryption service being unbound
+     */
     public void unbindEncryptionService(EncryptionService encryptionService) {
         this.encryptionService = null;
     }
diff --git 
a/services-common/src/main/java/org/apache/unomi/services/common/service/AbstractContextAwareService.java
 
b/services-common/src/main/java/org/apache/unomi/services/common/service/AbstractContextAwareService.java
index 64940cbc8..59bb5e6ac 100644
--- 
a/services-common/src/main/java/org/apache/unomi/services/common/service/AbstractContextAwareService.java
+++ 
b/services-common/src/main/java/org/apache/unomi/services/common/service/AbstractContextAwareService.java
@@ -35,7 +35,15 @@ import java.util.function.Supplier;
 import static org.apache.unomi.api.tenants.TenantService.SYSTEM_TENANT;
 
 /**
- * Base class for services that need to be context-aware and handle 
inheritance from the system tenant.
+ * Base class for services that operate within a tenant {@link 
org.apache.unomi.api.ExecutionContext} and support
+ * inheritance from the system tenant.
+ * <p>
+ * Subclasses use {@link #loadWithInheritance(String, Class)} and {@link 
#getMetadatas(Query, Class)}
+ * to resolve tenant-scoped data with fallback to the system tenant. 
System-tenant operations are
+ * delegated to {@link ExecutionContextManager#executeAsSystem(Runnable)}.
+ *
+ * @see org.apache.unomi.api.services.ExecutionContextManager
+ * @see org.apache.unomi.services.common.cache.AbstractMultiTypeCachingService
  */
 public abstract class AbstractContextAwareService {
 
@@ -44,25 +52,42 @@ public abstract class AbstractContextAwareService {
     protected PersistenceService persistenceService;
     protected volatile ExecutionContextManager contextManager = null;
 
+    /**
+     * Sets the persistence service used for loading and saving items.
+     *
+     * @param persistenceService the persistence service
+     */
     public void setPersistenceService(PersistenceService persistenceService) {
         this.persistenceService = persistenceService;
     }
 
+    /**
+     * Sets the execution context manager for tenant-scoped operations.
+     *
+     * @param contextManager the execution context manager
+     */
     public void setContextManager(ExecutionContextManager contextManager) {
         this.contextManager = contextManager;
     }
 
+    /**
+     * Retrieves the persistence service.
+     *
+     * @return the persistence service
+     */
     public PersistenceService getPersistenceService() {
         return persistenceService;
     }
 
     /**
-     * Load an item with tenant inheritance support.
-     * First tries to load from the current tenant, then falls back to the 
system tenant if not found.
+     * Loads an item with tenant inheritance support.
+     * <p>
+     * First loads from the current tenant; if not found, falls back to the 
system tenant.
      *
-     * @param itemId The ID of the item to load
-     * @param itemClass The class of the item
-     * @return The loaded item or null if not found in either tenant
+     * @param <T> the item type
+     * @param itemId the identifier of the item to load
+     * @param itemClass the item class
+     * @return the loaded item, or {@code null} if not found in either tenant
      */
     protected <T extends Item> T loadWithInheritance(String itemId, Class<T> 
itemClass) {
         T item = persistenceService.load(itemId, itemClass);
@@ -75,10 +100,11 @@ public abstract class AbstractContextAwareService {
     }
 
     /**
-     * Save an item with tenant awareness.
-     * Ensures the item is saved to the current tenant and handles any 
inheritance implications.
+     * Saves an item to the current tenant.
+     * <p>
+     * Sets the item's tenant identifier from the current execution context 
before persisting.
      *
-     * @param item The item to save
+     * @param item the item to save
      */
     protected void saveWithTenant(Item item) {
         String currentTenant = 
contextManager.getCurrentContext().getTenantId();
@@ -89,11 +115,12 @@ public abstract class AbstractContextAwareService {
     }
 
     /**
-     * Get metadata items with tenant awareness and inheritance.
+     * Retrieves metadata for items matching a query in the current tenant.
      *
-     * @param query The query to execute
-     * @param clazz The class of items to retrieve
-     * @return A partial list of metadata items
+     * @param <T> the metadata item type
+     * @param query the query to execute
+     * @param clazz the item class
+     * @return a partial list of metadata, or empty if no tenant context is set
      */
     protected <T extends MetadataItem> PartialList<Metadata> 
getMetadatas(Query query, Class<T> clazz) {
         String currentTenantId =  
contextManager.getCurrentContext().getTenantId();
@@ -109,7 +136,10 @@ public abstract class AbstractContextAwareService {
     }
 
     /**
-     * Create a condition to filter by tenant
+     * Creates a condition that filters items by tenant identifier.
+     *
+     * @param tenantId the tenant identifier
+     * @return a condition matching the given tenant
      */
     protected Condition createTenantCondition(String tenantId) {
         Condition tenantCondition = new Condition();
@@ -121,7 +151,11 @@ public abstract class AbstractContextAwareService {
     }
 
     /**
-     * Combine a query condition with a tenant condition
+     * Combines a query condition with a tenant filter using a logical AND.
+     *
+     * @param queryCondition the user query condition
+     * @param tenantCondition the tenant filter condition
+     * @return the combined condition
      */
     protected Condition combineTenantCondition(Condition queryCondition, 
Condition tenantCondition) {
         Condition finalCondition = new Condition();
@@ -132,7 +166,11 @@ public abstract class AbstractContextAwareService {
     }
 
     /**
-     * Convert a list of items to a list of metadata
+     * Converts a partial list of metadata items to a partial list of {@link 
Metadata}.
+     *
+     * @param <T> the metadata item type
+     * @param items the source items
+     * @return the converted metadata list with the same paging metadata
      */
     protected <T extends MetadataItem> PartialList<Metadata> 
convertToMetadataList(PartialList<T> items) {
         List<Metadata> metadatas = new LinkedList<>();
@@ -143,9 +181,9 @@ public abstract class AbstractContextAwareService {
     }
 
     /**
-     * Check if the current tenant is the system tenant
+     * Determines whether the current execution context is the system tenant.
      *
-     * @return true if the current tenant is the system tenant
+     * @return {@code true} if the current tenant is the system tenant
      */
     protected boolean isSystemTenant() {
         String currentTenant =  
contextManager.getCurrentContext().getTenantId();
@@ -153,19 +191,20 @@ public abstract class AbstractContextAwareService {
     }
 
     /**
-     * Execute code in the context of the system tenant
+     * Executes an operation in the system tenant context.
      *
-     * @param runnable The code to execute
+     * @param operation the operation to execute
      */
     protected void executeAsSystem(Runnable operation) {
         contextManager.executeAsSystem(operation);
     }
 
     /**
-     * Execute code in the context of the system tenant and return a value
+     * Executes an operation in the system tenant context and returns its 
result.
      *
-     * @param supplier The code to execute that returns a value
-     * @return The value returned by the supplier
+     * @param <T> the result type
+     * @param operation the operation to execute
+     * @return the value returned by the operation
      */
     protected <T> T executeAsSystem(Supplier<T> operation) {
         return contextManager.executeAsSystem(operation);
diff --git 
a/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImpl.java
 
b/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImpl.java
index 6f6c57789..74f3cbe71 100644
--- 
a/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImpl.java
+++ 
b/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImpl.java
@@ -790,6 +790,43 @@ public class SchedulerServiceImpl implements 
SchedulerService {
             LOGGER.debug("Error shutting down execution manager: {}", 
e.getMessage());
         }
 
+        // Mark tasks still in RUNNING state as CRASHED — they were 
interrupted mid-execution.
+        // This allows the next scheduler instance to reschedule them via 
CRASHED→SCHEDULED,
+        // and prevents invalid RUNNING→SCHEDULED transitions in shared 
persistence environments.
+        // We go directly to persistenceProvider/nonPersistentTasks here 
(instead of
+        // getAllTasks()/saveTask()) because shutdownNow is already true at 
this point,
+        // and those wrapper methods short-circuit to no-ops once that flag is 
set.
+        if (stateManager != null) {
+            try {
+                List<ScheduledTask> tasksToCheck = new 
ArrayList<>(nonPersistentTasks.values());
+                if (persistenceProvider != null) {
+                    // Only crash RUNNING tasks owned by this node, or stale 
orphans with no lock owner.
+                    // Do not touch RUNNING tasks locked by other nodes in a 
cluster.
+                    for (ScheduledTask task : 
persistenceProvider.findTasksByStatus(ScheduledTask.TaskStatus.RUNNING)) {
+                        String lockOwner = task.getLockOwner();
+                        if (lockOwner == null || nodeId.equals(lockOwner)) {
+                            tasksToCheck.add(task);
+                        }
+                    }
+                }
+                for (ScheduledTask task : tasksToCheck) {
+                    if 
(ScheduledTask.TaskStatus.RUNNING.equals(task.getStatus())) {
+                        try {
+                            stateManager.updateTaskState(task, 
ScheduledTask.TaskStatus.CRASHED,
+                                    "Interrupted by scheduler shutdown", 
nodeId);
+                            if (task.isPersistent() && persistenceProvider != 
null) {
+                                persistenceProvider.saveTask(task);
+                            }
+                        } catch (Exception e) {
+                            LOGGER.warn("Error marking task {} as crashed 
during shutdown: {}", task.getItemId(), e.getMessage());
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                LOGGER.debug("Error marking running tasks as crashed during 
shutdown: {}", e.getMessage());
+            }
+        }
+
         // Release all manager references
         this.recoveryManager = null;
         this.executionManager = null;
diff --git 
a/services/src/test/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImplTest.java
 
b/services/src/test/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImplTest.java
index 3a1b04b92..7a31d62ab 100644
--- 
a/services/src/test/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImplTest.java
+++ 
b/services/src/test/java/org/apache/unomi/services/impl/scheduler/SchedulerServiceImplTest.java
@@ -86,7 +86,7 @@ public class SchedulerServiceImplTest {
     /** Maximum number of retries for storage operations */
     private static final int MAX_RETRIES = 10;
     /** Default timeout for test assertions */
-    private static final long TEST_TIMEOUT = 5000; // 5 seconds
+    private static final long TEST_TIMEOUT = 15000; // 15 seconds — extra 
margin for loaded CI runners
     /** Time unit for test timeouts */
     private static final TimeUnit TEST_TIME_UNIT = TimeUnit.MILLISECONDS;
     /** Lock timeout for testing lock expiration */
@@ -2474,4 +2474,87 @@ public class SchedulerServiceImplTest {
         // Clean up
         newSchedulerService.preDestroy();
     }
+
+    /**
+     * Regression test for the preDestroy() shutdown sequence: a persistent 
task still in
+     * RUNNING state when the node goes down must be marked CRASHED so the 
next scheduler
+     * instance can reschedule it via the CRASHED-&gt;SCHEDULED transition. 
This exercises the
+     * direct persistenceProvider/nonPersistentTasks path used during 
shutdown, bypassing the
+     * getAllTasks()/saveTask() wrappers which become no-ops once shutdownNow 
is set.
+     */
+    @Test
+    @Tag("RecoveryTests")
+    public void testPreDestroyMarksStaleRunningPersistentTaskAsCrashed() 
throws Exception {
+        ScheduledTask runningTask = createTestTask("predestroy-crash-test", 
ScheduledTask.TaskStatus.RUNNING);
+        persistenceService.refresh();
+
+        schedulerService.preDestroy();
+
+        ScheduledTask reloadedTask = 
persistenceService.load(runningTask.getItemId(), ScheduledTask.class);
+        assertNotNull(reloadedTask, "Task should still exist after shutdown");
+        assertEquals(
+            ScheduledTask.TaskStatus.CRASHED,
+            reloadedTask.getStatus(),
+            "Task still RUNNING at shutdown should be marked CRASHED");
+        assertEquals(
+            "Interrupted by scheduler shutdown",
+            reloadedTask.getLastError(),
+            "Crashed task should record the shutdown as the cause");
+    }
+
+    /**
+     * End-to-end version of the above: a task actually executing (and 
ignoring the
+     * interrupt sent by executionManager.shutdown()) must still be observed 
as RUNNING
+     * and marked CRASHED by the time preDestroy() returns.
+     */
+    @Test
+    @Tag("RecoveryTests")
+    public void testPreDestroyMarksActivelyExecutingTaskAsCrashed() throws 
Exception {
+        String taskType = "predestroy-active-crash-test";
+        CountDownLatch startLatch = new CountDownLatch(1);
+        AtomicBoolean shouldStop = new AtomicBoolean(false);
+
+        TaskExecutor executor = new TaskExecutor() {
+            @Override
+            public String getTaskType() {
+                return taskType;
+            }
+
+            @Override
+            public void execute(ScheduledTask task, TaskStatusCallback 
callback) {
+                startLatch.countDown();
+                // Block past the shutdown's interrupt so the task is still 
RUNNING
+                // when preDestroy() scans for crashed tasks.
+                while (!shouldStop.get()) {
+                    try {
+                        Thread.sleep(TEST_SLEEP);
+                    } catch (InterruptedException ignored) {
+                        Thread.interrupted(); // clear the flag so the loop 
keeps running
+                    }
+                }
+            }
+        };
+
+        schedulerService.registerTaskExecutor(executor);
+
+        ScheduledTask task = 
schedulerService.newTask(taskType).disallowParallelExecution().schedule();
+
+        assertTrue(startLatch.await(TEST_TIMEOUT, TEST_TIME_UNIT), "Task 
should start executing");
+        TestHelper.retryUntil(
+            () -> schedulerService.getTask(task.getItemId()).getStatus(),
+            status -> status == ScheduledTask.TaskStatus.RUNNING);
+
+        try {
+            schedulerService.preDestroy();
+
+            ScheduledTask reloadedTask = 
persistenceService.load(task.getItemId(), ScheduledTask.class);
+            assertNotNull(reloadedTask, "Task should still exist after 
shutdown");
+            assertEquals(
+                ScheduledTask.TaskStatus.CRASHED,
+                reloadedTask.getStatus(),
+                "Task actively executing at shutdown should be marked 
CRASHED");
+        } finally {
+            shouldStop.set(true);
+        }
+    }
 }

Reply via email to