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

sergehuber pushed a commit to branch UNOMI-955-fix-ci-failures-javadoc
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit 891715aea95ffa8bb714855bc899ea6e753d7155
Author: Serge Huber <[email protected]>
AuthorDate: Wed Jun 24 18:01:36 2026 +0200

    UNOMI-955: Fix CI Javadoc failures and a dead shutdown crash-recovery path
    
    Rewrites Javadoc across api, services-common, persistence-spi, and the
    router extensions so the build's doclint pass goes clean, and wires a
    --javadoc flag into build.sh (enabled by default under --ci) to catch
    regressions going forward.
    
    While reviewing the diff, also fixed a real bug that had snuck into
    SchedulerServiceImpl: the new shutdown logic meant to mark RUNNING tasks
    as CRASHED was reading and saving through wrapper methods that silently
    no-op once the shutdown flag is set, so it never actually persisted
    anything. It now goes through the persistence provider directly. Added
    two unit tests that reproduce the bug against the old code and pass
    against the fix. Also replaced a hardcoded retry count in GraphQLListIT
    with a multiple of the shared DEFAULT_TRYING_TRIES constant.
---
 .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                                           |  54 ++++-
 .../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       |  30 +++
 .../impl/scheduler/SchedulerServiceImplTest.java   |  85 ++++++-
 56 files changed, 985 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..909217cf0 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,15 @@ while [ "$1" != "" ]; do
     shift
 done
 
+# Wire up log file output if requested
+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 +551,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 +1007,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 +1089,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 bb20ba2d0..73b8d37e3 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 d8c59857a..6404482ab 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
@@ -65,7 +65,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 9a68e3797..8199d44e5 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..51bf83e5d 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,36 @@ 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) {
+                    tasksToCheck.addAll(persistenceProvider.getAllTasks());
+                }
+                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