From c119f91b1c807a5454eb215a5723ed9c83fc7ebb Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 06:29:50 +0200 Subject: [PATCH 01/19] UNOMI-960: Improve api Javadoc and add pragmatic validation tooling Expand and fix public/protected Javadoc in the api module, configure doclint to fail only on broken links/HTML/syntax, and add a Checkstyle profile plus build.sh --javadoc two-pass checks for tag gaps on the published API surface. --- .../org/apache/unomi/api/BatchUpdate.java | 5 +- .../org/apache/unomi/api/ClusterNode.java | 30 +-- .../org/apache/unomi/api/ConsentStatus.java | 11 +- .../java/org/apache/unomi/api/CustomItem.java | 4 +- .../java/org/apache/unomi/api/EventInfo.java | 23 +- .../org/apache/unomi/api/EventProperty.java | 6 +- .../org/apache/unomi/api/EventSource.java | 41 ++++ .../org/apache/unomi/api/EventTarget.java | 37 +++ .../unomi/api/EventsCollectorRequest.java | 14 +- .../apache/unomi/api/ExecutionContext.java | 79 +++++- .../java/org/apache/unomi/api/GeoPoint.java | 4 +- .../main/java/org/apache/unomi/api/Item.java | 89 +++++-- .../java/org/apache/unomi/api/Metadata.java | 35 +-- .../org/apache/unomi/api/MetadataItem.java | 28 ++- .../java/org/apache/unomi/api/Parameter.java | 56 ++++- .../org/apache/unomi/api/PartialList.java | 40 ++- .../main/java/org/apache/unomi/api/Patch.java | 40 +++ .../java/org/apache/unomi/api/Persona.java | 13 + .../org/apache/unomi/api/PersonaSession.java | 23 +- .../apache/unomi/api/PersonaWithSessions.java | 35 ++- .../unomi/api/PersonalizationResult.java | 48 +++- .../java/org/apache/unomi/api/PluginType.java | 4 +- .../java/org/apache/unomi/api/Profile.java | 2 + .../org/apache/unomi/api/ProfileAlias.java | 44 ++++ .../api/PropertyMergeStrategyExecutor.java | 4 +- .../unomi/api/PropertyMergeStrategyType.java | 26 +- .../org/apache/unomi/api/PropertyType.java | 44 +--- .../main/java/org/apache/unomi/api/Scope.java | 4 +- .../java/org/apache/unomi/api/ServerInfo.java | 85 ++++++- .../java/org/apache/unomi/api/Session.java | 10 + .../unomi/api/SystemPropertiesItem.java | 4 +- .../org/apache/unomi/api/TimestampedItem.java | 4 +- .../main/java/org/apache/unomi/api/Topic.java | 22 ++ .../java/org/apache/unomi/api/ValueType.java | 12 +- .../unomi/api/actions/ActionExecutor.java | 4 +- .../unomi/api/actions/ActionPostExecutor.java | 4 +- .../apache/unomi/api/actions/ActionType.java | 4 +- .../apache/unomi/api/campaigns/Campaign.java | 4 +- .../unomi/api/campaigns/CampaignDetail.java | 6 +- .../unomi/api/conditions/Condition.java | 4 +- .../api/conditions/ConditionValidation.java | 4 +- .../BadSegmentConditionException.java | 4 +- .../java/org/apache/unomi/api/goals/Goal.java | 38 ++- .../apache/unomi/api/goals/GoalReport.java | 75 +++++- .../org/apache/unomi/api/lists/UserList.java | 4 +- .../org/apache/unomi/api/query/DateRange.java | 31 ++- .../org/apache/unomi/api/query/IpRange.java | 31 ++- .../apache/unomi/api/query/NumericRange.java | 32 ++- .../org/apache/unomi/api/query/Query.java | 50 ++-- .../unomi/api/rules/RuleStatistics.java | 12 +- .../unomi/api/security/EncryptionService.java | 3 +- .../SecurityServiceConfiguration.java | 129 +++++++++- .../unomi/api/segments/DependentMetadata.java | 27 +++ .../unomi/api/segments/ScoringElement.java | 4 +- .../unomi/api/segments/SegmentsAndScores.java | 4 +- .../unomi/api/services/ClusterService.java | 5 +- .../services/ConditionValidationService.java | 21 +- .../api/services/DefinitionsService.java | 4 +- .../api/services/EventListenerService.java | 4 +- .../unomi/api/services/EventService.java | 4 +- .../api/services/ExecutionContextManager.java | 3 +- .../unomi/api/services/GoalsService.java | 4 +- .../unomi/api/services/InvalidObjectInfo.java | 74 +++++- .../api/services/PersonalizationService.java | 75 ++++-- .../unomi/api/services/PrivacyService.java | 3 +- .../unomi/api/services/ProfileService.java | 58 +---- .../unomi/api/services/QueryService.java | 3 +- .../api/services/RuleListenerService.java | 7 +- .../unomi/api/services/RulesService.java | 4 +- .../unomi/api/services/SchedulerService.java | 21 ++ .../unomi/api/services/ScopeService.java | 5 +- .../unomi/api/services/SegmentService.java | 4 +- .../api/services/TenantLifecycleListener.java | 3 +- .../unomi/api/services/TopicService.java | 9 +- .../unomi/api/services/UserListService.java | 3 +- .../api/services/ValueTypeValidator.java | 3 +- .../api/tenants/ApiKeyCreationResult.java | 13 + .../unomi/api/tenants/AuditService.java | 9 +- .../unomi/api/tenants/ItemAuditService.java | 3 +- .../unomi/api/tenants/TenantAuditService.java | 3 +- .../api/tenants/TenantBackupMetadata.java | 26 ++ .../api/tenants/TenantEventPurgeResult.java | 56 ++++- .../unomi/api/tenants/TenantScopeUsage.java | 28 ++- .../unomi/api/tenants/TenantStatus.java | 4 +- .../apache/unomi/api/tenants/TenantUsage.java | 134 ++++++++++- .../unomi/api/tenants/TenantUsageService.java | 1 + .../tenants/security/SecurityAuditReport.java | 49 ++++ .../tenants/security/SecuritySettings.java | 66 +++++ .../apache/unomi/api/utils/ParserHelper.java | 227 +++++++++++++++++- .../org/apache/unomi/api/utils/YamlUtils.java | 3 + build.sh | 22 +- config/checkstyle-javadoc.xml | 31 +++ pom.xml | 42 ++++ 93 files changed, 2124 insertions(+), 310 deletions(-) create mode 100644 config/checkstyle-javadoc.xml diff --git a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java index 8f55a5195..240b0c8ad 100644 --- a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java +++ b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java @@ -20,7 +20,10 @@ import org.apache.unomi.api.conditions.Condition; /** - * A representation of an operation to update the value of a property on items matching a specific condition. + * Describes a bulk update operation that changes a property on every item + * matching a {@link org.apache.unomi.api.conditions.Condition}. + * Used when administrators or imports need to apply the same property change + * to many profiles, sessions, or other items at once. */ public class BatchUpdate { private String propertyName; 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 a80b5689f..09bed0dd8 100644 --- a/api/src/main/java/org/apache/unomi/api/ClusterNode.java +++ b/api/src/main/java/org/apache/unomi/api/ClusterNode.java @@ -18,13 +18,17 @@ package org.apache.unomi.api; /** - * Information about a cluster node. + * Runtime snapshot of one Apache Unomi node in a cluster. + * Stores health and topology flags such as load averages, addresses, uptime, + * and whether the node is a master (coordination only) or data node (stores + * context data). Cluster services publish these objects so operators can + * inspect cluster state. */ public class ClusterNode extends Item { private static final long serialVersionUID = 1281422346318230514L; - // Item type identifier for cluster nodes. + /** Item type identifier for cluster nodes. */ public static final String ITEM_TYPE = "clusterNode"; private double cpuLoad; @@ -50,7 +54,6 @@ public ClusterNode() { /** * Retrieves the cpu load. - * * @return the cpu load */ public double getCpuLoad() { @@ -59,7 +62,6 @@ public double getCpuLoad() { /** * Sets the cpu load. - * * @param cpuLoad the cpu load */ public void setCpuLoad(double cpuLoad) { @@ -68,7 +70,6 @@ public void setCpuLoad(double cpuLoad) { /** * Retrieves the public host address. - * * @return the public host address */ public String getPublicHostAddress() { @@ -77,7 +78,6 @@ public String getPublicHostAddress() { /** * Sets the public host address. - * * @param publicHostAddress the public host address */ public void setPublicHostAddress(String publicHostAddress) { @@ -86,7 +86,6 @@ public void setPublicHostAddress(String publicHostAddress) { /** * Retrieves the internal host address which uses the HTTP/HTTPS protocol for communications between clients and the context server. - * * @return the internal host address */ public String getInternalHostAddress() { @@ -95,7 +94,6 @@ public String getInternalHostAddress() { /** * Sets the internal host address which uses the HTTP/HTTPS protocol for communications between clients and the context server. - * * @param internalHostAddress the internal host address */ public void setInternalHostAddress(String internalHostAddress) { @@ -104,7 +102,6 @@ public void setInternalHostAddress(String internalHostAddress) { /** * Retrieves the load average for the last minute, five minutes and fifteen minutes. - * * @return an array of {@code double} containing, in order and starting from index {@code 0}, the load average for the last minute, last five minutes and last fifteen minutes */ public double[] getLoadAverage() { @@ -113,7 +110,6 @@ public double[] getLoadAverage() { /** * Sets the load average for the last minute, five minutes and fifteen minutes. - * * @param loadAverage an array of {@code double} containing, in order and starting from index {@code 0}, the load average for the last minute, last five minutes and last fifteen minutes */ public void setLoadAverage(double[] loadAverage) { @@ -122,7 +118,6 @@ public void setLoadAverage(double[] loadAverage) { /** * Retrieves the uptime. - * * @return the uptime */ public long getUptime() { @@ -131,7 +126,6 @@ public long getUptime() { /** * Sets the uptime. - * * @param uptime the uptime */ public void setUptime(long uptime) { @@ -140,7 +134,6 @@ public void setUptime(long uptime) { /** * Determines whether this ClusterNode is a master node, i.e. this node doesn't store any data but is only focused on cluster management operations. - * * @return {@code true} if this node is a master node, {@code false} otherwise */ public boolean isMaster() { @@ -148,8 +141,7 @@ public boolean isMaster() { } /** - * Specifies whether this ClusterNode is a master node, i.e. this node doesn't store any data but is only focused on cluster management operations.. - * + * Specifies whether this ClusterNode is a master node, i.e. this node doesn't store any data but is only focused on cluster management operations. * @param master {@code true} if this node is a master node, {@code false} otherwise */ public void setMaster(boolean master) { @@ -158,7 +150,6 @@ public void setMaster(boolean master) { /** * Determines whether this ClusterNode locally stores data. - * * @return {@code true} if this node locally stores data, {@code false} otherwise */ public boolean isData() { @@ -167,7 +158,6 @@ public boolean isData() { /** * Specifies whether this ClusterNode locally stores data. - * * @param data {@code true} if this node locally stores data, {@code false} otherwise */ public void setData(boolean data) { @@ -176,7 +166,6 @@ public void setData(boolean data) { /** * Retrieves the node start time in milliseconds. - * * @return the start time */ public long getStartTime() { @@ -185,7 +174,6 @@ public long getStartTime() { /** * Sets the node start time in milliseconds. - * * @param startTime the start time */ public void setStartTime(long startTime) { @@ -194,7 +182,6 @@ public void setStartTime(long startTime) { /** * Retrieves the last heartbeat time in milliseconds. - * * @return the last heartbeat time */ public long getLastHeartbeat() { @@ -203,7 +190,6 @@ public long getLastHeartbeat() { /** * Sets the last heartbeat time in milliseconds. - * * @param lastHeartbeat the last heartbeat time */ public void setLastHeartbeat(long lastHeartbeat) { @@ -212,7 +198,6 @@ public void setLastHeartbeat(long lastHeartbeat) { /** * Gets the server information. - * * @return the server information */ public ServerInfo getServerInfo() { @@ -221,7 +206,6 @@ public ServerInfo getServerInfo() { /** * Sets the server information. - * * @param serverInfo the server information */ public void setServerInfo(ServerInfo serverInfo) { 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 1f5b20d27..3ece0c5c8 100644 --- a/api/src/main/java/org/apache/unomi/api/ConsentStatus.java +++ b/api/src/main/java/org/apache/unomi/api/ConsentStatus.java @@ -17,14 +17,15 @@ package org.apache.unomi.api; /** - * This enum class represents the type of grant a @Consent might have. The revoke grant type is a special one used to - * remove a consent for a profile. + * Allowed grant states for a profile {@link Consent}. + * Each constant tells whether consent was granted, denied, or later revoked. + * Revoked consents are removed from the profile rather than kept as denied. */ public enum ConsentStatus { - // Consent has been granted. + /** Consent was explicitly granted by the profile. */ GRANTED, - // Consent has been denied. + /** Consent was explicitly denied. */ DENIED, - // Consent has been revoked. + /** Consent was revoked and should be removed from the profile. */ REVOKED } 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 fab0917e4..7c471ada8 100644 --- a/api/src/main/java/org/apache/unomi/api/CustomItem.java +++ b/api/src/main/java/org/apache/unomi/api/CustomItem.java @@ -21,7 +21,9 @@ import java.util.Map; /** - * A generic extension of Item for context server extensions, properties are stored in a Map. + * Generic {@link Item} extension for plugin-defined entity types. + * Properties live in a flexible map instead of typed fields, which lets + * extensions store custom payloads without adding new Java classes. */ public class CustomItem extends Item { /** diff --git a/api/src/main/java/org/apache/unomi/api/EventInfo.java b/api/src/main/java/org/apache/unomi/api/EventInfo.java index 543d6d940..df2936115 100644 --- a/api/src/main/java/org/apache/unomi/api/EventInfo.java +++ b/api/src/main/java/org/apache/unomi/api/EventInfo.java @@ -18,28 +18,49 @@ package org.apache.unomi.api; /** - * Basic event information + * Summary of one event type known to the server. + * Pairs an event type name with an occurrence count, for example when + * {@link ServerInfo} lists which events the running instance supports. */ public class EventInfo { private String name; private Long occurences; + /** + * Constructs a new {@code EventInfo} object. + */ public EventInfo() { } + /** + * Returns the name of the event. + * @return The event name as a {@link String}. + */ public String getName() { return name; } + /** + * Sets the name of the event. + * @param name The name to set for the event. + */ public void setName(String name) { this.name = name; } + /** + * Gets the number of occurrences for this event type. + * @return The count of occurrences as a {@link Long}. + */ public Long getOccurences() { return occurences; } + /** + * Sets the number of occurrences for this event type. + * @param occurences The count of occurrences to set. + */ public void setOccurences(Long occurences) { this.occurences = occurences; } diff --git a/api/src/main/java/org/apache/unomi/api/EventProperty.java b/api/src/main/java/org/apache/unomi/api/EventProperty.java index d727e1498..b7c588f03 100644 --- a/api/src/main/java/org/apache/unomi/api/EventProperty.java +++ b/api/src/main/java/org/apache/unomi/api/EventProperty.java @@ -19,9 +19,9 @@ import java.io.Serializable; /** - * An event property. - * - * @author Sergiy Shyrkov + * Named property attached to an {@link Event}. + * Events carry many properties; this type represents one key/value pair + * stored with the event for segmentation, personalization, and reporting. */ public class EventProperty implements Serializable { diff --git a/api/src/main/java/org/apache/unomi/api/EventSource.java b/api/src/main/java/org/apache/unomi/api/EventSource.java index 90128cf6c..d43ee3143 100644 --- a/api/src/main/java/org/apache/unomi/api/EventSource.java +++ b/api/src/main/java/org/apache/unomi/api/EventSource.java @@ -19,6 +19,10 @@ /** * TODO: REMOVE + * Describes where an event originated in the digital experience. + * Identifies the scope, item id, path, and type of the source page or item + * that triggered the event. Clients attach this information when they send + * events to Unomi. */ public class EventSource { private String scope; @@ -26,37 +30,74 @@ public class EventSource { private String path; private String type; + /** + * Constructs a new EventSource instance. + */ public EventSource() { } + /** + * Returns the scope associated with this event source. + * @return The scope string. + */ public String getScope() { return scope; } + /** + * Sets the scope for this event source. + * @param scope the new scope to set. + */ public void setScope(String scope) { this.scope = scope; } + /** + * Returns the unique identifier of this event source. + * @return The ID string. + */ public String getId() { return id; } + /** + * Sets the unique identifier for this event source. + * @param id the new ID to set. + */ public void setId(String id) { this.id = id; } + /** + * Returns the path associated with this event source. + * @return The path string. + */ public String getPath() { return path; } + /** + * Sets the path for this event source. + * @param path the new path to set. + */ public void setPath(String path) { this.path = path; } + /** + * Returns the type identifier of this event source. + * @return The type string. + */ public String getType() { return type; } + /** + * Sets the type of this event source. + * This value can be used to classify the data structure or content + * represented by the event source, such as "string", "array", or "boolean". + * @param type The string representation of the desired type. + */ public void setType(String type) { this.type = type; } diff --git a/api/src/main/java/org/apache/unomi/api/EventTarget.java b/api/src/main/java/org/apache/unomi/api/EventTarget.java index f1bc1addc..8a172aa50 100644 --- a/api/src/main/java/org/apache/unomi/api/EventTarget.java +++ b/api/src/main/java/org/apache/unomi/api/EventTarget.java @@ -22,6 +22,10 @@ /** * TODO: REMOVE + * Describes what an event acted upon. + * Holds the target item id and type plus optional properties, for example + * the product or content element a user clicked. Used together with + * {@link Event} to give rules and analytics full context. */ public class EventTarget implements Serializable { private static final long serialVersionUID = 6370790894348364803L; @@ -29,34 +33,67 @@ public class EventTarget implements Serializable { private String type; private Map properties; + /** + * Constructs a default {@code EventTarget}. + */ public EventTarget() { } + /** + * Constructs an {@code EventTarget} with the specified identifier and type. + * @param id The unique identifier of the event target. + * @param type The type associated with this event target. + */ public EventTarget(String id, String type) { this.id = id; this.type = type; } + /** + * Returns the unique identifier of this event target. + * @return The {@code String} ID. + */ public String getId() { return id; } + /** + * Sets the unique identifier of this event target. + * @param id The new {@code String} ID. + */ public void setId(String id) { this.id = id; } + /** + * Returns the type associated with this event target. + * @return The {@code String} type. + */ public String getType() { return type; } + /** + * Sets the type associated with this event target. + * @param type The new {@code String} type. + */ public void setType(String type) { this.type = type; } + /** + * Returns a map containing arbitrary properties associated + * with this event target. + * @return The {@link java.util.Map} of properties. + */ public Map getProperties() { return properties; } + /** + * Sets the arbitrary properties associated with this event target. + * @param properties The {@link java.util.Map} of properties to set. + */ public void setProperties(Map properties) { this.properties = properties; } diff --git a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java index 759a71ca8..96e69ffc9 100644 --- a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java +++ b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java @@ -20,7 +20,9 @@ import java.util.List; /** - * A request for events to be processed. + * Payload sent to the events collector endpoint. + * Wraps one or more {@link Event} instances that a client wants Unomi to + * process, evaluate against rules, and persist. */ public class EventsCollectorRequest { @@ -37,13 +39,17 @@ public class EventsCollectorRequest { /** * Retrieves the events to be processed. - * * @return the events to be processed */ public List getEvents() { return events; } + /** + * Sets the list of events to be processed by this request. + * @param events a list containing all events that should be + * collected and processed. + */ public void setEvents(List events) { this.events = events; } @@ -51,7 +57,6 @@ public void setEvents(List events) { /** * Retrieve the sessionId passed along with the request. All events will be processed with this sessionId as a * default - * * @return the identifier for the session */ public String getSessionId() { @@ -61,7 +66,6 @@ public String getSessionId() { /** * Sets the sessionId in the request. This is the preferred method of passing along a session identifier with the * request, as passing it along in the URL can lead to potential security vulnerabilities. - * * @param sessionId an unique identifier for the session */ public void setSessionId(String sessionId) { @@ -71,7 +75,6 @@ public void setSessionId(String sessionId) { /** * Retrieve the profileId passed along with the request. All events will be processed with this profileId as a * default - * * @return the identifier for the profile */ public String getProfileId() { @@ -80,7 +83,6 @@ public String getProfileId() { /** * Sets the profileId in the request. - * * @param profileId an unique identifier for the profile */ public void setProfileId(String profileId) { diff --git a/api/src/main/java/org/apache/unomi/api/ExecutionContext.java b/api/src/main/java/org/apache/unomi/api/ExecutionContext.java index 39b13f383..7506a31e5 100644 --- a/api/src/main/java/org/apache/unomi/api/ExecutionContext.java +++ b/api/src/main/java/org/apache/unomi/api/ExecutionContext.java @@ -21,9 +21,13 @@ import java.util.Stack; /** - * Represents the execution context for operations in Unomi, including security and tenant information. + * Security and tenant context for an in-flight operation. + * Carries the active tenant id, roles, permissions, and helpers to check + * access or temporarily switch tenant scope. Services read this object to + * enforce multi-tenant isolation and authorization. */ public class ExecutionContext { + /** The constant string used to identify the system tenant. */ public static final String SYSTEM_TENANT = "system"; private String tenantId; @@ -32,6 +36,15 @@ public class ExecutionContext { private Stack tenantStack = new Stack<>(); private boolean isSystem = false; + /** + * Constructs a new execution context with specified + * security and tenant details. + * @param tenantId The unique identifier of the tenant for this context. + * @param roles A set of roles assigned to the user in this + * context. Can be null. + * @param permissions A set of explicit permissions granted to the user in + * this context. Can be null. + */ public ExecutionContext(String tenantId, Set roles, Set permissions) { this.tenantId = tenantId; if (tenantId != null && tenantId.equals(SYSTEM_TENANT)) { @@ -45,34 +58,70 @@ public ExecutionContext(String tenantId, Set roles, Set permissi } } + /** + * Creates and returns a dedicated execution context + * representing the system tenant. + * @return An {@link ExecutionContext} instance configured + * for the system tenant. + */ public static ExecutionContext systemContext() { ExecutionContext context = new ExecutionContext(SYSTEM_TENANT, null, null); context.isSystem = true; return context; } + /** + * Retrieves the unique identifier of the current tenant + * associated with this context. + * @return The tenant ID string. + */ public String getTenantId() { return tenantId; } + /** + * Returns a copy of the set of roles assigned to this execution context. + * @return A new {@link Set} containing a copy of all assigned roles. + */ public Set getRoles() { return new HashSet<>(roles); } + /** + * Returns a copy of the set of permissions granted to + * this execution context. + * @return A new {@link Set} containing a copy of all explicit permissions. + */ public Set getPermissions() { return new HashSet<>(permissions); } + /** + * Checks if this execution context is designated as the system context. + * @return true if the context represents the system + * tenant, false otherwise. + */ public boolean isSystem() { return isSystem; } + /** + * Sets a new tenant ID for the current execution context. This operation + * saves the previous tenant ID onto an internal stack and updates the + * system status accordingly. + * @param tenantId The new tenant identifier to set. + */ public void setTenant(String tenantId) { tenantStack.push(this.tenantId); this.tenantId = tenantId; this.isSystem = SYSTEM_TENANT.equals(tenantId); } + /** + * Restores the execution context to the previously active tenant. + * If a previous tenant was set, this method pops that tenant ID from the + * internal stack and updates the current context state accordingly. + */ public void restorePreviousTenant() { if (!tenantStack.isEmpty()) { this.tenantId = tenantStack.pop(); @@ -80,6 +129,17 @@ public void restorePreviousTenant() { } } + /** + * Validates whether the current execution context has sufficient + * permissions to perform a given operation. + * If the context is system-level, validation passes immediately. Otherwise, + * it checks if the required permission exists in the context's + * granted permissions. + * @param operation The specific operation name required + * for access validation. + * @throws SecurityException if the current context lacks the necessary + * permission for the given operation. + */ public void validateAccess(String operation) { if (isSystem) { return; @@ -90,10 +150,27 @@ public void validateAccess(String operation) { } } + /** + * Checks if the current execution context possesses a specific permission. + * The context is considered to have the permission if it is system-level or + * if the permission name is explicitly listed in the context's + * granted permissions. + * @param permission The name of the permission to check for. + * @return true if the context is system-level or has been granted the + * permission, false otherwise. + */ public boolean hasPermission(String permission) { return isSystem || permissions.contains(permission); } + /** + * Checks if the current execution context possesses a specific role. + * The context is considered to have the role if it is system-level or if + * the role name is explicitly listed in the context's granted roles. + * @param role The name of the role to check for. + * @return true if the context is system-level or has been granted the + * role, false otherwise. + */ public boolean hasRole(String role) { return isSystem || roles.contains(role); } diff --git a/api/src/main/java/org/apache/unomi/api/GeoPoint.java b/api/src/main/java/org/apache/unomi/api/GeoPoint.java index b3d0d02a6..e7481d2db 100644 --- a/api/src/main/java/org/apache/unomi/api/GeoPoint.java +++ b/api/src/main/java/org/apache/unomi/api/GeoPoint.java @@ -20,7 +20,9 @@ import java.util.Map; /** - * GeoPoint represents a point in geographical coordinate system using latitude and longitude. + * A geographic location expressed as latitude and longitude. + * Used on profiles, events, and queries when rules or segments need to + * match users or actions by location. */ public class GeoPoint { 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 ec796de52..44ff53b0f 100644 --- a/api/src/main/java/org/apache/unomi/api/Item.java +++ b/api/src/main/java/org/apache/unomi/api/Item.java @@ -50,6 +50,15 @@ public abstract class Item implements Serializable, YamlConvertible { private static final Map itemTypeCache = new ConcurrentHashMap<>(); + /** + * Retrieves the type string for a given class by attempting to read a + * public static String constant named {@code ITEM_TYPE} from that class. + * This method caches the result based on the provided class to improve + * performance during repeated calls. + * @param clazz The Class object whose item type is required. + * @return A string representation of the Item's type, or null if the + * {@code ITEM_TYPE} field cannot be found or accessed. + */ public static String getItemType(Class clazz) { String itemType = itemTypeCache.get(clazz); if (itemType != null) { @@ -67,10 +76,30 @@ public static String getItemType(Class clazz) { return null; } + /** + * Protected field representing the unique identifier used to identify this + * Item when persisted or referenced. + */ protected String itemId; + /** + * Protected field storing the type of this Item. This value is typically + * derived from a constant defined within the implementing class. + */ protected String itemType; + /** + * Protected field defining the scope of this Item, allowing related items + * to be grouped together during processing or retrieval. + */ protected String scope; + /** + * Protected field storing the current version number of this Item. This is + * used for tracking changes over time. + */ protected Long version; + /** + * A protected map used to store additional, non-core metadata about this + * Item using string keys and arbitrary object values. + */ protected Map systemMetadata = new HashMap<>(); private String tenantId; @@ -82,6 +111,12 @@ public static String getItemType(Class clazz) { private String sourceInstanceId; private Date lastSyncDate; + /** + * Default constructor that initializes the Item's type by calling + * {@link #getItemType(Class)} and sets up initial audit metadata fields. + * If the implementing class does not provide a public static String + * constant named {@code ITEM_TYPE}, an error is logged. + */ public Item() { this.itemType = getItemType(this.getClass()); if (itemType == null) { @@ -90,6 +125,11 @@ public Item() { initializeAuditMetadata(); } + /** + * Constructor that initializes the Item using the default mechanism and + * explicitly sets the item's unique identifier. + * @param itemId The unique ID to assign to this Item upon creation. + */ public Item(String itemId) { this(); this.itemId = itemId; @@ -103,13 +143,17 @@ private void initializeAuditMetadata() { /** * Retrieves the Item's identifier used to uniquely identify this Item when persisted or when referred to. An Item's identifier must be unique among Items with the same type. - * * @return a String representation of the identifier, no particular format is prescribed as long as it is guaranteed unique for this particular Item. */ public String getItemId() { return itemId; } + /** + * Sets the unique identifier for this Item. This ID is used when persisting + * or referencing the item in various contexts (e.g., profiles, sessions). + * @param itemId the unique identifier for the Item + */ public void setItemId(String itemId) { this.itemId = itemId; } @@ -117,7 +161,6 @@ public void setItemId(String itemId) { /** * Retrieves the Item's type used to assert metadata and structure common to Items of this type, notably for persistence purposes. The Item's type must * match the value defined by the implementation's {@code ITEM_TYPE} public constant. - * * @return a String representation of this Item's type, must equal the {@code ITEM_TYPE} value */ public String getItemType() { @@ -126,7 +169,6 @@ public String getItemType() { /** * Sets the Item's type. - * * @param itemType the Item's type */ public void setItemType(String itemType) { @@ -135,7 +177,6 @@ public void setItemType(String itemType) { /** * Retrieves the Item's scope. - * * @return the Item's scope name */ public String getScope() { @@ -144,7 +185,6 @@ public String getScope() { /** * Sets the Item's scope. - * * @param scope the Item's scope */ public void setScope(String scope) { @@ -166,17 +206,25 @@ public int hashCode() { return itemId != null ? itemId.hashCode() : 0; } + /** + * Retrieves the current version number of the Item. + * @return a {@link Long} representing the version of this Item. + */ public Long getVersion() { return version; } + /** + * Sets the version number of the Item. This is typically used when updating + * an existing item's state. + * @param version the new version to set + */ public void setVersion(Long version) { this.version = version; } /** * Returns the system metadata for the given key. - * * @param key the key * @return the system metadata for the given key */ @@ -186,7 +234,6 @@ public Object getSystemMetadata(String key) { /** * Sets the system metadata for the given key. - * * @param key the key * @param value the value */ @@ -194,40 +241,51 @@ public void setSystemMetadata(String key, Object value) { systemMetadata.put(key, value); } + /** + * Retrieves the unique identifier of the tenant associated with this Item, + * useful for context management and logging. + * @return the tenant ID as a {@link String}. + */ public String getTenantId() { return tenantId; } /** * Sets the tenant ID. - * * @param tenantId the tenant ID */ public void setTenantId(String tenantId) { this.tenantId = tenantId; } - // Audit metadata getters and setters + /** + * Retrieves the identifier of the user or system that originally created + * this Item. This is part of the item's audit metadata. + * @return the creator's ID as a {@link String}. + */ public String getCreatedBy() { return createdBy; } /** * Sets the created by. - * * @param createdBy the created by */ public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } + /** + * Retrieves the identifier of the user or system that last modified this + * Item. This is part of the item's audit metadata. + * @return the last modifier's ID as a {@link String}. + */ public String getLastModifiedBy() { return lastModifiedBy; } /** * Sets the last modified by. - * * @param lastModifiedBy the last modified by */ public void setLastModifiedBy(String lastModifiedBy) { @@ -236,7 +294,6 @@ public void setLastModifiedBy(String lastModifiedBy) { /** * Returns the date when this item was created. - * * @return the date when this item was created */ public Date getCreationDate() { @@ -245,7 +302,6 @@ public Date getCreationDate() { /** * Sets the date when this item was created. - * * @param creationDate the creation date */ public void setCreationDate(Date creationDate) { @@ -254,7 +310,6 @@ public void setCreationDate(Date creationDate) { /** * Returns the date when this item was last modified. - * * @return the date when this item was last modified */ public Date getLastModificationDate() { @@ -263,7 +318,6 @@ public Date getLastModificationDate() { /** * Sets the date when this item was last modified. - * * @param lastModificationDate the last modification date */ public void setLastModificationDate(Date lastModificationDate) { @@ -272,7 +326,6 @@ public void setLastModificationDate(Date lastModificationDate) { /** * Returns the source instance ID. - * * @return the source instance ID */ public String getSourceInstanceId() { @@ -281,7 +334,6 @@ public String getSourceInstanceId() { /** * Sets the source instance ID. - * * @param sourceInstanceId the source instance ID */ public void setSourceInstanceId(String sourceInstanceId) { @@ -290,7 +342,6 @@ public void setSourceInstanceId(String sourceInstanceId) { /** * Returns the last synchronization date. - * * @return the last synchronization date */ public Date getLastSyncDate() { @@ -299,7 +350,6 @@ public Date getLastSyncDate() { /** * Sets the last synchronization date. - * * @param lastSyncDate the last synchronization date */ public void setLastSyncDate(Date lastSyncDate) { @@ -309,7 +359,6 @@ public void setLastSyncDate(Date lastSyncDate) { /** * Converts this item to a Map structure for YAML output. * Implements YamlConvertible interface with circular reference detection. - * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this item */ diff --git a/api/src/main/java/org/apache/unomi/api/Metadata.java b/api/src/main/java/org/apache/unomi/api/Metadata.java index 4f650589f..b33647771 100644 --- a/api/src/main/java/org/apache/unomi/api/Metadata.java +++ b/api/src/main/java/org/apache/unomi/api/Metadata.java @@ -29,7 +29,9 @@ import static org.apache.unomi.api.utils.YamlUtils.circularRef; /** - * A class providing information about context server entities. + * Common descriptive fields shared by Unomi configuration items. + * Includes id, name, description, scope, tags, and related attributes that + * appear on segments, rules, property types, and other managed entities. * * @see MetadataItem */ @@ -60,7 +62,6 @@ public Metadata() { /** * Instantiates a new Metadata with the specified identifier. - * * @param id the identifier for this Metadata */ public Metadata(String id) { @@ -69,7 +70,6 @@ public Metadata(String id) { /** * Instantiates a new Metadata with the provided information. - * * @param scope the scope for this Metadata * @param id the identifier of the associated {@link Item} * @param name the name @@ -84,7 +84,6 @@ public Metadata(String scope, String id, String name, String description) { /** * Retrieves the identifier for the entity associated with this Metadata - * * @return the identifier */ public String getId() { @@ -93,7 +92,6 @@ public String getId() { /** * Sets the id. - * * @param id the id */ public void setId(String id) { @@ -102,7 +100,6 @@ public void setId(String id) { /** * Retrieves the name. - * * @return the name */ public String getName() { @@ -111,7 +108,6 @@ public String getName() { /** * Sets the name. - * * @param name the name */ public void setName(String name) { @@ -120,7 +116,6 @@ public void setName(String name) { /** * Retrieves the description. - * * @return the description */ public String getDescription() { @@ -129,7 +124,6 @@ public String getDescription() { /** * Sets the description. - * * @param description the description */ public void setDescription(String description) { @@ -138,7 +132,6 @@ public void setDescription(String description) { /** * Retrieves the scope for the entity associated with this Metadata - * * @return the scope for the entity associated with this Metadata * @see Item Item for a deeper discussion of scopes */ @@ -148,7 +141,6 @@ public String getScope() { /** * Sets the scope. - * * @param scope the scope */ public void setScope(String scope) { @@ -157,7 +149,6 @@ public void setScope(String scope) { /** * Retrieves a set of {@link String} tag names associated with this Metadata - * * @return a set of {@link String} tag names associated with this Metadata */ public Set getTags() { @@ -166,7 +157,6 @@ public Set getTags() { /** * Sets the tags. - * * @param tags the tag ids */ public void setTags(Set tags) { @@ -175,7 +165,6 @@ public void setTags(Set tags) { /** * Retrieves a set of {@link String} system tag names associated with this Metadata - * * @return a set of {@link String} system tag names associated with this Metadata */ public Set getSystemTags() { @@ -184,7 +173,6 @@ public Set getSystemTags() { /** * Sets the system tags. - * * @param systemTags the system tag ids */ public void setSystemTags(Set systemTags) { @@ -193,7 +181,6 @@ public void setSystemTags(Set systemTags) { /** * Whether the associated entity is considered active by the context server, in particular to check if rules need to be created / triggered - * * @return {@code true} if the associated entity is enabled, {@code false} otherwise */ public boolean isEnabled() { @@ -202,7 +189,6 @@ public boolean isEnabled() { /** * Specifies whether the associated entity should be active or not. - * * @param enabled {@code true} if the associated entity is enabled, {@code false} otherwise */ public void setEnabled(boolean enabled) { @@ -211,7 +197,6 @@ public void setEnabled(boolean enabled) { /** * Whether the associated entity is waiting on additional plugins to become available to be able to properly perform its function. - * * @return {@code true} if plugins are missing, {@code false} otherwise */ public boolean isMissingPlugins() { @@ -220,7 +205,6 @@ public boolean isMissingPlugins() { /** * Specifies whether the associated entity is waiting on additional plugins to become available. - * * @param missingPlugins {@code true} if plugins are missing, {@code false} otherwise */ public void setMissingPlugins(boolean missingPlugins) { @@ -229,7 +213,6 @@ public void setMissingPlugins(boolean missingPlugins) { /** * Whether the associated entity is considered for internal purposes only and should therefore be hidden to accessing UIs. - * * @return {@code true} if the associated entity needs to be hidden, {@code false} otherwise */ public boolean isHidden() { @@ -238,7 +221,6 @@ public boolean isHidden() { /** * Specifies whether the associated entity is hidden. - * * @param hidden {@code true} if the associated entity needs to be hidden, {@code false} otherwise */ public void setHidden(boolean hidden) { @@ -247,7 +229,6 @@ public void setHidden(boolean hidden) { /** * Whether the associated entity can be accessed but not modified. - * * @return {@code true} if the associated entity can be accessed but not modified, {@code false} otherwise */ public boolean isReadOnly() { @@ -256,13 +237,20 @@ public boolean isReadOnly() { /** * Specifies whether the associated entity should be only accessed and not modified. - * * @param readOnly {@code true} if the associated entity can be accessed but not modified, {@code false} otherwise */ public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } + /** + * Compares this Metadata instance to another based on their identifiers. + * @param o The other {@link Metadata} object to compare against. + * @return An integer representing the comparison result of the IDs: a + * negative value if this ID is less than the specified ID, zero + * if the IDs are equal, or a positive value if this ID is + * greater than the specified ID. + */ public int compareTo(Metadata o) { return getId().compareTo(o.getId()); } @@ -289,7 +277,6 @@ public int hashCode() { /** * Converts this metadata to a Map structure for YAML output. * Implements YamlConvertible interface with circular reference detection. - * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this metadata */ diff --git a/api/src/main/java/org/apache/unomi/api/MetadataItem.java b/api/src/main/java/org/apache/unomi/api/MetadataItem.java index 68cf3b324..9ff4ff0d3 100644 --- a/api/src/main/java/org/apache/unomi/api/MetadataItem.java +++ b/api/src/main/java/org/apache/unomi/api/MetadataItem.java @@ -28,15 +28,27 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue; /** - * A superclass for all {@link Item}s that bear {@link Metadata}. + * Base {@link Item} for entities that carry {@link Metadata}. + * Most user-facing definitions (segments, rules, property types, etc.) + * extend this class so they share the same identification and tagging model. */ public abstract class MetadataItem extends Item { private static final long serialVersionUID = -2459510107927663510L; + /** The associated {@link Metadata} object for this item. */ protected Metadata metadata; + /** + * Constructs a default {@code MetadataItem}. + */ public MetadataItem() { } + /** + * Constructs a {@code MetadataItem} initialized with the given metadata. + * The parent class ID is set using the ID from the provided metadata, and + * the internal metadata field is populated. + * @param metadata the initial metadata for this item + */ public MetadataItem(Metadata metadata) { super(metadata != null ? metadata.getId() : null); this.metadata = metadata; @@ -44,7 +56,6 @@ public MetadataItem(Metadata metadata) { /** * Retrieves the associated Metadata. - * * @return the associated Metadata */ @XmlElement(name = "metadata") @@ -52,6 +63,12 @@ public Metadata getMetadata() { return metadata; } + /** + * Sets the associated {@link Metadata}. + * If the provided metadata is not null, the parent class's ID will be + * updated to match the ID of the new metadata object. + * @param metadata the metadata to set + */ public void setMetadata(Metadata metadata) { if (metadata != null) { this.itemId = metadata.getId(); @@ -59,6 +76,12 @@ public void setMetadata(Metadata metadata) { this.metadata = metadata; } + /** + * Returns the scope for this item, taken from {@link Metadata} when present. + * Falls back to the item-level scope field otherwise. + * + * @return the scope id, or {@code null} if none is set + */ @XmlTransient public String getScope() { if (metadata != null) { @@ -71,7 +94,6 @@ public String getScope() { * Converts this metadata item to a Map structure for YAML output. * Merges fields from Item parent class and adds metadata field. * Subclasses should override this method, call super.toYaml(visited), and add their specific fields. - * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this metadata item */ diff --git a/api/src/main/java/org/apache/unomi/api/Parameter.java b/api/src/main/java/org/apache/unomi/api/Parameter.java index 10cd6083d..c43949eb0 100644 --- a/api/src/main/java/org/apache/unomi/api/Parameter.java +++ b/api/src/main/java/org/apache/unomi/api/Parameter.java @@ -44,35 +44,71 @@ public class Parameter implements Serializable, YamlConvertible { private Object defaultValue; private ConditionValidation validation; + /** + * Constructs a default {@link Parameter} instance. + */ public Parameter() { } + /** + * Constructs a {@link Parameter} with specified + * identification and type details. + * @param id The unique identifier for this parameter. + * @param type The data type of the parameter (e.g., "string", "integer"). + * @param multivalued Indicates if the parameter can hold multiple values. + */ public Parameter(String id, String type, boolean multivalued) { this.id = id; this.type = type; this.multivalued = multivalued; } + /** + * Returns the unique identifier assigned to this parameter. + * @return The ID string of the parameter. + */ public String getId() { return id; } + /** + * Sets the unique identifier for this parameter. + * @param id The new ID to assign to the parameter. + */ public void setId(String id) { this.id = id; } + /** + * Retrieves the data type associated with this parameter. + * @return The string representation of the parameter's type. + */ public String getType() { return type; } + /** + * Sets the data type for this parameter. + * @param type The new data type string (e.g., "array", "boolean"). + */ public void setType(String type) { this.type = type; } + /** + * Checks if this parameter is configured to handle multiple values. + * @return {@code true} if the parameter is multivalued, + * {@code false} otherwise. + */ public boolean isMultivalued() { return multivalued; } + /** + * Sets whether this parameter can hold multiple values. + * @param multivalued If {@code true}, the parameter accepts multiple + * values; otherwise, it accepts only one. + */ public void setMultivalued(boolean multivalued) { this.multivalued = multivalued; } @@ -86,18 +122,37 @@ public void setChoiceListInitializerFilter(String choiceListInitializerFilter) { // Avoid errors when deploying old definitions } + /** + * Retrieves the default value configured for this parameter. + * @return The stored default value, which may be null if none is set. + */ public Object getDefaultValue() { return defaultValue; } + /** + * Sets the default value that should be used when evaluating conditions + * involving this parameter. This value can be of any type. + * @param defaultValue the object to be set as the default value + */ public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; } + /** + * Gets the condition validation rules associated with this parameter. + * @return The {@link ConditionValidation} object defining constraints, or + * null if none is configured. + */ public ConditionValidation getValidation() { return validation; } + /** + * Sets the specific condition validation rules for this parameter. This + * allows controlling how the parameter behaves within a condition context. + * @param validation the new {@link ConditionValidation} object to apply + */ public void setValidation(ConditionValidation validation) { this.validation = validation; } @@ -105,7 +160,6 @@ public void setValidation(ConditionValidation validation) { /** * Converts this parameter to a Map structure for YAML output. * Implements YamlConvertible interface. - * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this parameter */ diff --git a/api/src/main/java/org/apache/unomi/api/PartialList.java b/api/src/main/java/org/apache/unomi/api/PartialList.java index 568cf334e..afe1c9c2a 100644 --- a/api/src/main/java/org/apache/unomi/api/PartialList.java +++ b/api/src/main/java/org/apache/unomi/api/PartialList.java @@ -25,7 +25,6 @@ /** * A list of elements representing a limited view of a larger list, starting from a given element (offset from the first) and showing only a given number of elements, instead of * showing all of them. This is useful to retrieve "pages" of large element collections. - * * @param the generic type of contained elements */ public class PartialList implements Serializable { @@ -45,7 +44,9 @@ public class PartialList implements Serializable { * relation we can understand if we are in the case of an accurate hit or not. */ public enum Relation { + /** Total size equals the reported value exactly. */ EQUAL, + /** Total size is at least the reported value (approximate lower bound). */ GREATER_THAN_OR_EQUAL_TO } @@ -62,7 +63,6 @@ public PartialList() { /** * Instantiates a new PartialList. - * * @param list the limited view into the bigger List this PartialList is representing * @param offset the offset of the first element in the view * @param pageSize the number of elements this PartialList contains @@ -79,7 +79,6 @@ public PartialList(List list, long offset, long pageSize, long totalSize, Rel /** * Retrieves the limited list view. - * * @return a List of the {@code size} elements starting from the {@code offset}-th one from the original, larger list */ public List getList() { @@ -88,7 +87,6 @@ public List getList() { /** * Sets the view list. - * * @param list the view list into the bigger List this PartialList is representing */ public void setList(List list) { @@ -97,46 +95,56 @@ public void setList(List list) { /** * Retrieves the offset of the first element of the view. - * * @return the offset of the first element of the view */ public long getOffset() { return offset; } + /** + * Sets the offset of the first element in this partial list view. + * @param offset the starting index (offset) for the elements in the view. + */ public void setOffset(long offset) { this.offset = offset; } /** * Retrieves the number of elements this PartialList contains. - * * @return the number of elements this PartialList contains */ public long getPageSize() { return pageSize; } + /** + * Sets the maximum number of elements to be returned in + * this partial list view. + * @param pageSize the size limit for the page view. + */ public void setPageSize(long pageSize) { this.pageSize = pageSize; } /** * Retrieves the total size of elements in the original List. - * * @return the total size of elements in the original List */ public long getTotalSize() { return totalSize; } + /** + * Sets the total count of elements available in the original, full list. + * @param totalSize the overall size of the collection represented + * by this PartialList. + */ public void setTotalSize(long totalSize) { this.totalSize = totalSize; } /** * Retrieves the size of this PartialList. Should equal {@link #getPageSize()}. - * * @return the size of this PartialList */ @XmlTransient @@ -146,7 +154,6 @@ public int size() { /** * Retrieves the element at the specified index - * * @param index the index of the element to retrieve * @return the element at the specified index */ @@ -163,6 +170,11 @@ public String getScrollIdentifier() { return scrollIdentifier; } + /** + * Sets the scroll identifier used to continue a scrolling list query. + * @param scrollIdentifier a string containing the unique identifier needed + * for subsequent requests in a scroll operation. + */ public void setScrollIdentifier(String scrollIdentifier) { this.scrollIdentifier = scrollIdentifier; } @@ -175,6 +187,11 @@ public String getScrollTimeValidity() { return scrollTimeValidity; } + /** + * Sets the time validity period for the scroll query. + * @param scrollTimeValidity a string representing how long the scroll + * context remains valid (e.g., "10m"). + */ public void setScrollTimeValidity(String scrollTimeValidity) { this.scrollTimeValidity = scrollTimeValidity; } @@ -188,6 +205,11 @@ public Relation getTotalSizeRelation() { return totalSizeRelation; } + /** + * Sets the relation describing the total size count. + * @param totalSizeRelation the relationship type between the total size and + * the actual data set. + */ public void setTotalSizeRelation(Relation totalSizeRelation) { this.totalSizeRelation = totalSizeRelation; } diff --git a/api/src/main/java/org/apache/unomi/api/Patch.java b/api/src/main/java/org/apache/unomi/api/Patch.java index b7fb457b4..c703ef2a8 100644 --- a/api/src/main/java/org/apache/unomi/api/Patch.java +++ b/api/src/main/java/org/apache/unomi/api/Patch.java @@ -29,9 +29,21 @@ import java.util.Map; import java.util.TreeMap; +/** + * Represents a patch operation applied to an existing {@link Item}. + * {@link #PATCHABLE_TYPES} lists the item types that support patching. + * The {@link #operation} is one of {@code override}, {@code patch}, or + * {@code remove}: {@code override} replaces the full item, {@code patch} + * applies a JSON Patch payload, and {@code remove} needs no data. + */ public class Patch extends Item { private static final long serialVersionUID = 4171966405850833985L; + /** + * A map containing the types of items that are capable of being patched. + * The keys represent item type names, and the values are {@link Class} + * objects corresponding to those patchable item types. + */ public final static Map> PATCHABLE_TYPES; static { @@ -47,6 +59,9 @@ public class Patch extends Item { PATCHABLE_TYPES.put("scoring", Scoring.class); } + /** + * The constant string used to identify this class as a "patch" item type. + */ public static final String ITEM_TYPE = "patch"; private String patchedItemId; @@ -67,6 +82,10 @@ public String getPatchedItemId() { return patchedItemId; } + /** + * Sets the ID of the item that will be concerned by this patch. + * @param patchedItemId the id of the item + */ public void setPatchedItemId(String patchedItemId) { this.patchedItemId = patchedItemId; } @@ -79,6 +98,10 @@ public String getPatchedItemType() { return patchedItemType; } + /** + * Sets the item type of the item that will be concerned by this patch. + * @param patchedItemType the item type + */ public void setPatchedItemType(String patchedItemType) { this.patchedItemType = patchedItemType; } @@ -91,6 +114,11 @@ public String getOperation() { return operation; } + /** + * Sets the type of patch operation to perform. This can typically be + * 'override', 'patch', or 'remove'. + * @param operation the patch operation type + */ public void setOperation(String operation) { this.operation = operation; } @@ -106,6 +134,14 @@ public Object getData() { return data; } + /** + * Sets the data payload for the patch. + * For override operations, this should contain the full + * item representation. + * For patch operations, this should contain a JsonPatch object. + * For remove operations, no data is required. + * @param data the patch data + */ public void setData(Object data) { this.data = data; } @@ -118,6 +154,10 @@ public Date getLastApplication() { return lastApplication; } + /** + * Sets the date when the patch was last applied. + * @param lastApplication the date of the last application + */ public void setLastApplication(Date lastApplication) { this.lastApplication = lastApplication; } diff --git a/api/src/main/java/org/apache/unomi/api/Persona.java b/api/src/main/java/org/apache/unomi/api/Persona.java index 284ecb250..3dfa2e52c 100644 --- a/api/src/main/java/org/apache/unomi/api/Persona.java +++ b/api/src/main/java/org/apache/unomi/api/Persona.java @@ -23,13 +23,26 @@ */ public class Persona extends Profile { + /** + * The fixed item type identifier used when representing a persona object. + */ public static final String ITEM_TYPE = "persona"; private static final long serialVersionUID = -1239061113528609426L; + /** + * Constructs a new, default {@link Persona} instance. + * This constructor initializes the persona without an explicit ID, + * allowing the item ID to potentially be set subsequently. + */ public Persona() { super(); } + /** + * Constructs a new {@link Persona} instance with the + * specified unique identifier. + * @param personaId The unique ID to assign to this persona. + */ public Persona(String personaId) { super(personaId); } diff --git a/api/src/main/java/org/apache/unomi/api/PersonaSession.java b/api/src/main/java/org/apache/unomi/api/PersonaSession.java index 0c9373dc8..47b36c081 100644 --- a/api/src/main/java/org/apache/unomi/api/PersonaSession.java +++ b/api/src/main/java/org/apache/unomi/api/PersonaSession.java @@ -20,15 +20,36 @@ import java.util.Date; /** - * A Persona session. + * A browsing session attached to a {@link Persona}. + * Personas are test or synthetic profiles; their sessions let marketers + * preview personalized content as if they were a visitor in that category. */ public class PersonaSession extends Session { + /** + * The constant string identifier used as the item type for + * {@link PersonaSession} instances. + */ public static final String ITEM_TYPE = "personaSession"; private static final long serialVersionUID = -1499107289607498852L; + /** + * Constructs an empty {@link PersonaSession} instance. + * This session must be fully configured and saved using persistence + * services to represent a valid persona session record. + */ public PersonaSession() { } + /** + * Constructs a {@link PersonaSession} associated with a given profile, + * item ID, and timestamp. + * The resulting session is initialized with a system scope metadata. + * @param itemId the unique identifier for the persona session item. + * @param profile the {@link Profile} object representing the + * context of the session. + * @param timeStamp the date and time when this session was + * created or recorded. + */ public PersonaSession(String itemId, Profile profile, Date timeStamp) { super(itemId, profile, timeStamp, Metadata.SYSTEM_SCOPE); } diff --git a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java index 0a507e9b1..cf4a82cdb 100644 --- a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java +++ b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java @@ -22,37 +22,70 @@ import java.util.List; /** - * A convenience object gathering a {@link Persona} and its associated {@link PersonaSession}s. + * Bundles a {@link Persona} with its related {@link PersonaSession} list. + * REST and tooling use this wrapper when returning a persona together with + * the sessions needed to simulate or inspect that persona. */ public class PersonaWithSessions implements Serializable { private Persona persona; private List sessions; + /** + * Constructs a new, empty {@link PersonaWithSessions} instance. + */ public PersonaWithSessions() { } + /** + * Constructs a {@link PersonaWithSessions} object with the specified + * persona and list of sessions. + * @param persona the associated {@link Persona} + * @param sessions the list of associated {@link PersonaSession}s + */ public PersonaWithSessions(Persona persona, List sessions) { this.persona = persona; this.sessions = sessions; } + /** + * Returns the {@link Persona} associated with this object. + * @return the contained {@link Persona} + */ public Persona getPersona() { return persona; } + /** + * Sets the {@link Persona} associated with this object. + * @param persona the new {@link Persona} + */ public void setPersona(Persona persona) { this.persona = persona; } + /** + * Returns all {@link PersonaSession}s associated with this object. + * @return a list of {@link PersonaSession}s + */ public List getSessions() { return sessions; } + /** + * Sets the list of {@link PersonaSession}s associated with this object. + * @param sessions the new list of {@link PersonaSession}s + */ public void setSessions(List sessions) { this.sessions = sessions; } + /** + * Retrieves the last session from the stored list. This is assumed to be + * the first element (index 0). + * If no sessions are present, returns null. + * @return the most recent {@link PersonaSession}, or null if none exist + */ @XmlTransient public PersonaSession getLastSession() { return sessions.size()>0?sessions.get(0):null; diff --git a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java index 15c6ab5a5..0da3e2d53 100644 --- a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java +++ b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java @@ -25,22 +25,39 @@ import java.util.Map; /** - * A class to contain the result of a personalization, containing the list of content IDs as well as a changeType to - * indicate if a profile and/or a session was modified. + * Outcome of a personalization request. + * Returns matching content ids, optional extra metadata (such as control + * group flags), and internal change codes when the resolved experience + * updated the profile or session. */ public class PersonalizationResult implements Serializable { + /** + * Key used in {@link #getAdditionalResultInfos()} to indicate if the + * personalization result was generated while running in a control group. + */ public final static String ADDITIONAL_RESULT_INFO_IN_CONTROL_GROUP = "inControlGroup"; + /** Matching content identifiers for the resolved personalization. */ List contentIds; + /** Extra key/value metadata returned to the client (for example control group flags). */ Map additionalResultInfos = new HashMap<>(); + /** Internal change flags when resolution updated the profile or session. */ int changeType = EventService.NO_CHANGE; + /** + * Constructs an empty PersonalizationResult with default values. + */ public PersonalizationResult() { } + /** + * Constructs a PersonalizationResult initialized with a + * list of content IDs. + * @param contentIds the list of matching ids for current personalization + */ public PersonalizationResult(List contentIds) { this.contentIds = contentIds; } @@ -53,6 +70,11 @@ public List getContentIds() { return contentIds; } + /** + * Sets the list of content IDs associated with this result. + * This overwrites any previously set content IDs. + * @param contentIds the new list of content IDs + */ public void setContentIds(List contentIds) { this.contentIds = contentIds; } @@ -65,6 +87,12 @@ public Map getAdditionalResultInfos() { return additionalResultInfos; } + /** + * Sets the map containing additional result information. This map is useful + * for returning extra data to the client. + * @param additionalResultInfos a map of key/value pair for additional + * information, like: inControlGroup + */ public void setAdditionalResultInfos(Map additionalResultInfos) { this.additionalResultInfos = additionalResultInfos; } @@ -74,7 +102,6 @@ public void setAdditionalResultInfos(Map additionalResultInfos) * Control group are used to identify a profile or a session that should not get personalized results, * instead the current profile/session should get a specific result (usually the same for all peoples falling in control group) * Note: it's for now the responsibility of the client to decide what to do when the current personalization is under control group. - * * @return true in case current profile or session is in control group for the personalization. */ @XmlTransient @@ -83,6 +110,13 @@ public boolean isInControlGroup() { (Boolean) additionalResultInfos.get(ADDITIONAL_RESULT_INFO_IN_CONTROL_GROUP); } + /** + * Sets whether this personalization result belongs to a control group by + * storing the boolean value in the internal additional result info map. + * @param inControlGroup true if the current profile or session is in + * control group for the + * personalization, false otherwise + */ public void setInControlGroup(boolean inControlGroup) { this.additionalResultInfos.put(ADDITIONAL_RESULT_INFO_IN_CONTROL_GROUP, inControlGroup); } @@ -90,7 +124,6 @@ public void setInControlGroup(boolean inControlGroup) { /** * Change code in case the personalization resolution modified the profile or the session * Only used internally, and will not be serialized either for storage or response payload. - * * @return change code */ @XmlTransient @@ -98,6 +131,13 @@ public int getChangeType() { return changeType; } + /** + * Adds specified change flags to the current accumulated change type. + * This method uses bitwise OR operation to ensure that multiple changes + * are recorded without overwriting previous ones. + * @param changes The change code or flag(s) to add to the + * result's change type. + */ public void addChanges(int changes) { this.changeType |= changes; } diff --git a/api/src/main/java/org/apache/unomi/api/PluginType.java b/api/src/main/java/org/apache/unomi/api/PluginType.java index 1b75bff09..491cd9588 100644 --- a/api/src/main/java/org/apache/unomi/api/PluginType.java +++ b/api/src/main/java/org/apache/unomi/api/PluginType.java @@ -18,7 +18,9 @@ package org.apache.unomi.api; /** - * The interface for unomi plugins. + * Marker interface for Unomi plugin definition types. + * Implementations such as {@link PropertyMergeStrategyType} expose an id + * and OSGi filter so the runtime can locate the plugin implementation. */ public interface PluginType { diff --git a/api/src/main/java/org/apache/unomi/api/Profile.java b/api/src/main/java/org/apache/unomi/api/Profile.java index 76d9d63c4..ff83d068b 100644 --- a/api/src/main/java/org/apache/unomi/api/Profile.java +++ b/api/src/main/java/org/apache/unomi/api/Profile.java @@ -206,6 +206,7 @@ public void setSegments(Set segments) { /** * @deprecated since 2.0.0 merge mechanism is now based on profile aliases, and this property is not used anymore + * @return the profile id this profile was merged with, or {@code null} */ @Deprecated public String getMergedWith() { @@ -214,6 +215,7 @@ public String getMergedWith() { /** * @deprecated since 2.0.0 merge mechanism is now based on profile aliases, and this property is not used anymore + * @param mergedWith the profile id this profile was merged with */ @Deprecated public void setMergedWith(String mergedWith) { diff --git a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java index 1434b029d..c7569df2f 100644 --- a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java +++ b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java @@ -19,8 +19,16 @@ import java.util.Date; +/** + * Maps a client-specific profile identifier to a canonical Unomi profile. + * Lets multiple applications reference the same visitor under different ids + * while sharing one merged profile record. + */ public class ProfileAlias extends Item { + /** + * The constant string used to identify this item type as a profile alias. + */ public static final String ITEM_TYPE = "profileAlias"; private String profileID; @@ -31,37 +39,73 @@ public class ProfileAlias extends Item { private Date modifiedTime; + /** + * Constructs an empty ProfileAlias object. + */ public ProfileAlias() { } + /** + * Retrieves the unique identifier of the associated profile. + * @return The profile ID as a {@link String}. + */ public String getProfileID() { return profileID; } + /** + * Sets the unique identifier of the associated profile. + * @param profileID The profile ID to set. + */ public void setProfileID(String profileID) { this.profileID = profileID; } + /** + * Retrieves the client identifier associated with this alias. + * @return The client ID as a {@link String}, or null if not set. + */ public String getClientID() { return clientID; } + /** + * Sets the client identifier associated with this alias. + * @param clientID The client ID to set. + */ public void setClientID(String clientID) { this.clientID = clientID; } + /** + * Retrieves the date and time when this profile alias was created. + * @return The creation timestamp as a {@link java.util.Date}. + */ public Date getCreationTime() { return creationTime; } + /** + * Sets the date and time of creation for this profile alias. + * @param creationTime The creation timestamp to set. + */ public void setCreationTime(Date creationTime) { this.creationTime = creationTime; } + /** + * Retrieves the date and time when this profile alias was last modified. + * or null if no modification time has been recorded. + * @return The {@link java.util.Date} representing the modification time, + */ public Date getModifiedTime() { return modifiedTime; } + /** + * Sets the date and time when this profile alias was last modified. + * @param modifiedTime The new timestamp indicating the modification time. + */ public void setModifiedTime(Date modifiedTime) { this.modifiedTime = modifiedTime; } diff --git a/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyExecutor.java b/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyExecutor.java index c7dcd0171..88400a945 100644 --- a/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyExecutor.java +++ b/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyExecutor.java @@ -20,7 +20,9 @@ import java.util.List; /** - * A strategy algorithm to merge profile properties such as "adding integers", "using oldest value", "using most recent value", "merging lists", etc... + * Algorithm that merges two values for the same profile property. + * Implementations define strategies such as keep latest, sum numbers, or + * union lists when events update the same field. */ public interface PropertyMergeStrategyExecutor { /** diff --git a/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java b/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java index 8a2249ec1..9596d8e63 100644 --- a/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java +++ b/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java @@ -21,7 +21,9 @@ import java.io.Serializable; /** - * A unomi plugin that defines a new property merge strategy. + * Plugin declaration for a profile property merge strategy. + * Merge strategies decide how conflicting property values are combined when + * several updates apply to the same profile field (sum, latest value, etc.). */ public class PropertyMergeStrategyType implements PluginType, Serializable { @@ -32,13 +34,16 @@ public class PropertyMergeStrategyType implements PluginType, Serializable { /** * Retrieves the identifier for this PropertyMergeStrategyType. - * * @return the identifier for this PropertyMergeStrategyType */ public String getId() { return id; } + /** + * Sets the identifier for this PropertyMergeStrategyType. + * @param id the unique identifier to be set. + */ public void setId(String id) { this.id = id; } @@ -47,22 +52,37 @@ public void setId(String id) { * Retrieves the OSGi filter used to identify the implementation associated with this PropertyMergeStrategyType. Filters take the following form: * {@code (propertyMergeStrategyExecutorId=<id>)} where {@code id} corresponds to the value of the {@code propertyMergeStrategyExecutorId} service property in the * Blueprint service definition for this PropertyMergeStrategyType. - * * @return the filter string used to identify the implementation associated with this PropertyMergeStrategyType */ public String getFilter() { return filter; } + /** + * Sets the OSGi filter used to identify the implementation associated with + * this PropertyMergeStrategyType. + * @param filter the filter string value. + */ public void setFilter(String filter) { this.filter = filter; } + /** + * Returns the OSGi bundle id of the plugin that registered this strategy. + * Used internally when resolving merge strategy implementations. + * + * @return the plugin bundle id + */ @XmlTransient public long getPluginId() { return pluginId; } + /** + * Sets the OSGi bundle id of the plugin that registered this strategy. + * + * @param pluginId the plugin bundle id + */ public void setPluginId(long pluginId) { this.pluginId = pluginId; } diff --git a/api/src/main/java/org/apache/unomi/api/PropertyType.java b/api/src/main/java/org/apache/unomi/api/PropertyType.java index 090d7eec0..93ee1b226 100644 --- a/api/src/main/java/org/apache/unomi/api/PropertyType.java +++ b/api/src/main/java/org/apache/unomi/api/PropertyType.java @@ -26,12 +26,13 @@ import java.util.*; /** - * A user-defined profile or session property, specifying how possible values are constrained, if the value is multi-valued (a vector of values as opposed to a scalar value). + * Schema definition for a custom profile or session property. + * Declares value type, defaults, allowed ranges, merge strategy, and whether + * the property accepts multiple values. */ public class PropertyType extends MetadataItem { /** * The PropertyType ITEM_TYPE. - * * @see Item for a discussion of ITEM_TYPE */ public static final String ITEM_TYPE = "propertyType"; @@ -58,7 +59,6 @@ public PropertyType() { /** * Instantiates a new Property type with the specified Metadata. - * * @param metadata the metadata associated with the specified metadata */ public PropertyType(Metadata metadata) { @@ -68,9 +68,7 @@ public PropertyType(Metadata metadata) { /** * Retrieves the target for this property type, indicating the type of elements this property type is defined for. For example, for property types attached to profiles, {@code * target} would be {@code "profiles"}. - * * TODO: deprecated? - * * @return the target for this property type */ public String getTarget() { @@ -79,9 +77,7 @@ public String getTarget() { /** * Sets the target for this property type. - * * TODO: deprecated? - * * @param target the target for this property type, indicating the type of elements this property type is defined for */ public void setTarget(String target) { @@ -90,7 +86,6 @@ public void setTarget(String target) { /** * Retrieves the identifier of the value type constraining values for properties using this PropertyType. - * * @return the value type identifier associated with values defined by this PropertyType * @see ValueType */ @@ -101,7 +96,6 @@ public String getValueTypeId() { /** * Sets the value type identifier. - * * @param valueTypeId the value type identifier */ public void setValueTypeId(String valueTypeId) { @@ -110,7 +104,6 @@ public void setValueTypeId(String valueTypeId) { /** * Retrieves the value type associated with values defined for properties using this PropertyType. - * * @return the value type associated with values defined for properties using this PropertyType */ @XmlTransient @@ -120,7 +113,6 @@ public ValueType getValueType() { /** * Sets the value type. - * * @param valueType the value type associated with values defined for properties using this PropertyType */ public void setValueType(ValueType valueType) { @@ -129,7 +121,6 @@ public void setValueType(ValueType valueType) { /** * Retrieves the default value defined for property using this PropertyType. - * * @return the default value defined for property using this PropertyType */ public String getDefaultValue() { @@ -138,7 +129,6 @@ public String getDefaultValue() { /** * Sets the default value that properties using this PropertyType will use if no value is specified explicitly. - * * @param defaultValue the default value that properties using this PropertyType will use if no value is specified explicitly */ public void setDefaultValue(String defaultValue) { @@ -147,9 +137,7 @@ public void setDefaultValue(String defaultValue) { /** * Retrieves the set of JCR properties from which properties of this type would be automatically initialized from. - * * TODO: remove from API? - * * @return the name of JCR properties properties of this type would be automatically initialized from */ public Set getAutomaticMappingsFrom() { @@ -159,7 +147,6 @@ public Set getAutomaticMappingsFrom() { /** * Specifies the set of JCR properties from which properties of this type would be automatically initialized from. * TODO: remove from API? - * * @param automaticMappingsFrom the set of JCR properties from which properties of this type would be automatically initialized from */ public void setAutomaticMappingsFrom(Set automaticMappingsFrom) { @@ -168,7 +155,6 @@ public void setAutomaticMappingsFrom(Set automaticMappingsFrom) { /** * Retrieves the rank of this PropertyType for ordering purpose. - * * @return the rank of this PropertyType for ordering purpose */ public Double getRank() { @@ -177,7 +163,6 @@ public Double getRank() { /** * Specifies the rank of this PropertyType for ordering purpose. - * * @param rank the rank of this PropertyType for ordering purpose */ public void setRank(Double rank) { @@ -186,7 +171,6 @@ public void setRank(Double rank) { /** * Retrieves the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged. - * * @return the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged */ public String getMergeStrategy() { @@ -195,7 +179,6 @@ public String getMergeStrategy() { /** * Sets the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged - * * @param mergeStrategy the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged */ public void setMergeStrategy(String mergeStrategy) { @@ -204,7 +187,6 @@ public void setMergeStrategy(String mergeStrategy) { /** * Retrieves the date ranges. - * * @return the date ranges */ public List getDateRanges() { @@ -213,7 +195,6 @@ public List getDateRanges() { /** * Sets the date ranges. - * * @param dateRanges the date ranges */ public void setDateRanges(List dateRanges) { @@ -222,7 +203,6 @@ public void setDateRanges(List dateRanges) { /** * Retrieves the numeric ranges. - * * @return the numeric ranges */ public List getNumericRanges() { @@ -231,7 +211,6 @@ public List getNumericRanges() { /** * Sets the numeric ranges. - * * @param numericRanges the numeric ranges */ public void setNumericRanges(List numericRanges) { @@ -240,7 +219,6 @@ public void setNumericRanges(List numericRanges) { /** * Retrieves the ip ranges. - * * @return the ip ranges */ public List getIpRanges() { @@ -249,7 +227,6 @@ public List getIpRanges() { /** * Sets the ip ranges. - * * @param ipRanges the ip ranges */ public void setIpRanges(List ipRanges) { @@ -258,7 +235,6 @@ public void setIpRanges(List ipRanges) { /** * Whether properties using this property type are multi-valued. - * * @return {@code true} if properties of this type should be multi-valued, {@code false} otherwise */ public Boolean isMultivalued() { @@ -267,7 +243,6 @@ public Boolean isMultivalued() { /** * Specifies whether properties using this property type are multi-valued. - * * @param multivalued {@code true} if properties of this type should be multi-valued, {@code false} otherwise */ public void setMultivalued(Boolean multivalued) { @@ -276,9 +251,7 @@ public void setMultivalued(Boolean multivalued) { /** * Whether properties with this type are marked as protected. Protected properties can be displayed but their value cannot be changed. - * * TODO: rename to readOnly? - * * @return {@code true} if properties of this type are protected, {@code false} otherwise */ public Boolean isProtected() { @@ -287,17 +260,26 @@ public Boolean isProtected() { /** * Specifies whether properties with this type are marked as protected. - * * @param protekted {@code true} if properties of this type are protected, {@code false} otherwise */ public void setProtected(boolean protekted) { this.protekted = protekted; } + /** + * Retrieves the set of {@link PropertyType} objects that are considered + * children or subtypes of this property type. + * @return a set of child property types associated with this instance + */ public Set getChildPropertyTypes() { return childPropertyTypes; } + /** + * Sets the collection of child property types for this property type. + * @param childPropertyTypes the set of {@link PropertyType} objects that + * are children or subtypes + */ public void setChildPropertyTypes(Set childPropertyTypes) { this.childPropertyTypes = childPropertyTypes; } diff --git a/api/src/main/java/org/apache/unomi/api/Scope.java b/api/src/main/java/org/apache/unomi/api/Scope.java index a8fa35137..1618f6f24 100644 --- a/api/src/main/java/org/apache/unomi/api/Scope.java +++ b/api/src/main/java/org/apache/unomi/api/Scope.java @@ -18,7 +18,9 @@ package org.apache.unomi.api; /** - * Object representing a scope. + * Named isolation boundary for context data in Unomi. + * Profiles, sessions, events, and definitions belong to a scope (often a + * site or application) so data from one digital property does not mix with another. */ public class Scope extends MetadataItem { diff --git a/api/src/main/java/org/apache/unomi/api/ServerInfo.java b/api/src/main/java/org/apache/unomi/api/ServerInfo.java index 7b80f9487..4f973cf24 100644 --- a/api/src/main/java/org/apache/unomi/api/ServerInfo.java +++ b/api/src/main/java/org/apache/unomi/api/ServerInfo.java @@ -23,7 +23,9 @@ import java.util.Map; /** - * Basic information about a Unomi server + * High-level description of a running Unomi instance. + * Exposes version, build, SCM branch, timestamp, and the list of event types + * the server can handle. Clients call this to discover server capabilities. */ public class ServerInfo { @@ -39,77 +41,158 @@ public class ServerInfo { private List logoLines = new ArrayList<>(); + /** + * Constructs a new ServerInfo instance. + */ public ServerInfo() { } + /** + * Returns the unique identifier of the server. + * @return The server's identifier string. + */ public String getServerIdentifier() { return serverIdentifier; } + /** + * Sets the unique identifier for this server information. + * @param serverIdentifier The unique identifier to set. + */ public void setServerIdentifier(String serverIdentifier) { this.serverIdentifier = serverIdentifier; } + /** + * Returns the version string of the server. + * @return The server's version number. + */ public String getServerVersion() { return serverVersion; } + /** + * Sets the version string for this server information. + * @param serverVersion The version string to set. + */ public void setServerVersion(String serverVersion) { this.serverVersion = serverVersion; } + /** + * Returns the build number of the server. + * @return The server's build number as a string. + */ public String getServerBuildNumber() { return serverBuildNumber; } + /** + * Sets the build number for this server information. + * @param serverBuildNumber The build number to set. + */ public void setServerBuildNumber(String serverBuildNumber) { this.serverBuildNumber = serverBuildNumber; } + /** + * Returns the date associated with the server's build. + * @return The {@link java.util.Date} representing the build date. + */ public Date getServerBuildDate() { return serverBuildDate; } + /** + * Sets the build date of the server. + * @param serverBuildDate the build date of the server + */ public void setServerBuildDate(Date serverBuildDate) { this.serverBuildDate = serverBuildDate; } + /** + * Retrieves the timestamp associated with the server information. + * @return the server's timestamp string + */ public String getServerTimestamp() { return serverTimestamp; } + /** + * Sets the timestamp for the server information. + * @param serverTimestamp the server's timestamp (e.g., YYYYMMDDHHmmss) + */ public void setServerTimestamp(String serverTimestamp) { this.serverTimestamp = serverTimestamp; } + /** + * Retrieves the source control management branch name of the server. + * @return the SCM branch name + */ public String getServerScmBranch() { return serverScmBranch; } + /** + * Sets the source control management branch name for + * the server information. + * @param serverScmBranch the SCM branch name + */ public void setServerScmBranch(String serverScmBranch) { this.serverScmBranch = serverScmBranch; } + /** + * Retrieves the list of event types supported by this server instance. + * @return a list of {@link EventInfo} objects representing + * available event types + */ public List getEventTypes() { return eventTypes; } + /** + * Sets the collection of event types associated with + * the server information. + * @param eventTypes the list of event types to set + */ public void setEventTypes(List eventTypes) { this.eventTypes = eventTypes; } + /** + * Retrieves a map containing various capabilities reported by the server. + * @return a map where keys are capability names and values are descriptions + */ public Map getCapabilities() { return capabilities; } + /** + * Sets the map containing various capabilities reported + * by the Unomi server. + * @param capabilities The map of capability names and descriptions. + */ public void setCapabilities(Map capabilities) { this.capabilities = capabilities; } + /** + * Retrieves the list of strings used to display the server's logo lines. + * @return The {@link java.util.List} + * containing the logo lines. + */ public List getLogoLines() { return logoLines; } + /** + * Sets the list of strings that represent the server's logo lines + * for display purposes. + * @param logoLines The list of strings defining the logo lines. + */ public void setLogoLines(List logoLines) { this.logoLines = logoLines; } diff --git a/api/src/main/java/org/apache/unomi/api/Session.java b/api/src/main/java/org/apache/unomi/api/Session.java index 8ca067720..11496be61 100644 --- a/api/src/main/java/org/apache/unomi/api/Session.java +++ b/api/src/main/java/org/apache/unomi/api/Session.java @@ -222,10 +222,20 @@ public void setSize(int size) { this.size = size; } + /** + * Retrieves the scope this session belongs to. + * + * @return the session scope identifier + */ public String getScope() { return scope; } + /** + * Sets the scope this session belongs to. + * + * @param scope the session scope identifier + */ public void setScope(String scope) { this.scope = scope; } diff --git a/api/src/main/java/org/apache/unomi/api/SystemPropertiesItem.java b/api/src/main/java/org/apache/unomi/api/SystemPropertiesItem.java index e191562cc..45786d5aa 100644 --- a/api/src/main/java/org/apache/unomi/api/SystemPropertiesItem.java +++ b/api/src/main/java/org/apache/unomi/api/SystemPropertiesItem.java @@ -19,7 +19,9 @@ import java.util.Map; /** - * An Item that is holding system properties. + * {@link Item} that stores system-level properties in a map. + * System properties are internal or cross-cutting values kept separately + * from visitor profile properties. */ public interface SystemPropertiesItem { diff --git a/api/src/main/java/org/apache/unomi/api/TimestampedItem.java b/api/src/main/java/org/apache/unomi/api/TimestampedItem.java index 0a0799a15..f383efab8 100644 --- a/api/src/main/java/org/apache/unomi/api/TimestampedItem.java +++ b/api/src/main/java/org/apache/unomi/api/TimestampedItem.java @@ -20,7 +20,9 @@ import java.util.Date; /** - * A context-server entity that is timestamped. + * {@link Item} that records when it was created or last modified. + * Event and profile-like types implement this interface so retention, + * ordering, and auditing can use consistent timestamps. */ public interface TimestampedItem { /** diff --git a/api/src/main/java/org/apache/unomi/api/Topic.java b/api/src/main/java/org/apache/unomi/api/Topic.java index 03d789d38..dbddfd346 100644 --- a/api/src/main/java/org/apache/unomi/api/Topic.java +++ b/api/src/main/java/org/apache/unomi/api/Topic.java @@ -16,26 +16,48 @@ */ package org.apache.unomi.api; +/** + * Named category tag stored as an {@link Item}. + * Topics group configuration or content entities so administrators can + * organize and search related objects across the context server. + */ public class Topic extends Item { + /** Defines the item type string used for Topic entities. */ public static final String ITEM_TYPE = "topic"; private String topicId; private String name; + /** + * Retrieves the unique identifier of this topic. + * @return The {@link String} ID of the topic. + */ public String getTopicId() { return topicId; } + /** + * Sets the unique identifier for this topic. + * @param topicId the unique ID to set. + */ public void setTopicId(String topicId) { this.topicId = topicId; } + /** + * Retrieves the name of the topic. + * @return The {@link String} name of the topic. + */ public String getName() { return name; } + /** + * Sets the display name for this topic. + * @param name the new name to set. + */ public void setName(String name) { this.name = name; } diff --git a/api/src/main/java/org/apache/unomi/api/ValueType.java b/api/src/main/java/org/apache/unomi/api/ValueType.java index d470a694b..9895837f0 100644 --- a/api/src/main/java/org/apache/unomi/api/ValueType.java +++ b/api/src/main/java/org/apache/unomi/api/ValueType.java @@ -23,7 +23,9 @@ import java.util.Set; /** - * A value type to be used to constrain property values. + * Definition of allowed values for a {@link PropertyType}. + * Value types describe validation rules, ranges, and serializers so profile + * and session properties stay consistent with their schema. */ public class ValueType implements PluginType, Serializable { @@ -108,11 +110,19 @@ public void setDescriptionKey(String descriptionKey) { this.descriptionKey = descriptionKey; } + /** + * {@inheritDoc} + */ + @Override @XmlTransient public long getPluginId() { return pluginId; } + /** + * {@inheritDoc} + */ + @Override public void setPluginId(long pluginId) { this.pluginId = pluginId; } diff --git a/api/src/main/java/org/apache/unomi/api/actions/ActionExecutor.java b/api/src/main/java/org/apache/unomi/api/actions/ActionExecutor.java index 6f33b9e02..4941df8bf 100644 --- a/api/src/main/java/org/apache/unomi/api/actions/ActionExecutor.java +++ b/api/src/main/java/org/apache/unomi/api/actions/ActionExecutor.java @@ -21,7 +21,9 @@ import org.apache.unomi.api.services.EventService; /** - * A piece of code that performs a specified {@link Action}, given a triggering {@link Event} + * Runtime implementation of an {@link org.apache.unomi.api.actions.Action}. + * When a rule fires, executors perform the configured action (send email, + * set property, etc.) for the triggering {@link Event}. */ public interface ActionExecutor { diff --git a/api/src/main/java/org/apache/unomi/api/actions/ActionPostExecutor.java b/api/src/main/java/org/apache/unomi/api/actions/ActionPostExecutor.java index e80d6cd95..923ba31cc 100644 --- a/api/src/main/java/org/apache/unomi/api/actions/ActionPostExecutor.java +++ b/api/src/main/java/org/apache/unomi/api/actions/ActionPostExecutor.java @@ -20,7 +20,9 @@ import org.apache.unomi.api.rules.Rule; /** - * An action to be executed after all {@link Rule}-derived have been processed. + * Hook executed after all rule actions for an event have run. + * Use post-executors for cleanup, logging, or follow-up work that must + * happen once per event after the main action chain completes. */ public interface ActionPostExecutor { /** 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 bf6fb477a..f652578b0 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 @@ -31,7 +31,9 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue; /** - * A type definition for {@link Action}s. + * Declarative definition of an action that rules can trigger. + * Action types describe parameters and point to the {@link ActionExecutor} + * implementation that performs the work at runtime. */ public class ActionType extends MetadataItem implements PluginType, YamlConvertible { /** Item type identifier for action types. */ diff --git a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java index c932475c0..ba0936345 100644 --- a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java +++ b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java @@ -26,7 +26,9 @@ import java.util.Date; /** - * A goal-oriented, time-limited marketing operation that needs to be evaluated for return on investment performance by tracking the ratio of visits to conversions. + * Time-bounded marketing program built around {@link org.apache.unomi.api.goals.Goal}s. + * Campaigns track entry conditions, duration, and conversion metrics so teams + * can measure ROI for a specific promotion or experiment. */ public class Campaign extends MetadataItem { /** diff --git a/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java b/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java index db35100a8..adeb4c1f6 100644 --- a/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java +++ b/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java @@ -18,9 +18,9 @@ package org.apache.unomi.api.campaigns; /** - * Details about a {@link Campaign}. - * - * Created by kevan on 03/06/15. + * Aggregated metrics for a {@link Campaign}. + * Summarizes participation and performance figures used in campaign reporting + * dashboards. */ public class CampaignDetail { private long engagedProfiles = 0; diff --git a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java index 248d1c43a..5ef081d22 100644 --- a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java +++ b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java @@ -38,7 +38,9 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue; /** - * A set of elements that can be evaluated. + * Boolean expression evaluated against context (profiles, events, sessions). + * Conditions combine parameters and nested sub-conditions; segments, rules, + * and queries all use them to decide when logic applies. */ public class Condition implements Serializable, YamlConvertible { private static final long serialVersionUID = 7584522402785053206L; 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 9dfd5feaf..4053c5ae5 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 @@ -26,7 +26,9 @@ import static org.apache.unomi.api.utils.YamlUtils.setToSortedList; /** - * Validation metadata for condition parameters + * Rules that constrain values allowed in a {@link Condition} parameter. + * Segment builders and the validation service use this metadata to reject + * invalid parameter values before save. */ public class ConditionValidation implements Serializable, YamlConvertible { private static final long serialVersionUID = 1L; 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 8bd908a2c..d8ee9ea93 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 @@ -18,7 +18,9 @@ package org.apache.unomi.api.exceptions; /** - * Exception thrown when a segment condition is invalid or cannot be used. + * Raised when a segment condition is malformed or cannot be evaluated. + * Indicates the condition JSON or references are invalid so callers can + * surface a clear error to administrators. */ public class BadSegmentConditionException extends RuntimeException { diff --git a/api/src/main/java/org/apache/unomi/api/goals/Goal.java b/api/src/main/java/org/apache/unomi/api/goals/Goal.java index d2cac5db8..efca37cc8 100644 --- a/api/src/main/java/org/apache/unomi/api/goals/Goal.java +++ b/api/src/main/java/org/apache/unomi/api/goals/Goal.java @@ -42,6 +42,7 @@ * */ public class Goal extends MetadataItem implements YamlConvertible { + /** The constant string used to identify this object as a goal item type. */ public static final String ITEM_TYPE = "goal"; private static final long serialVersionUID = 6131648013470949983L; private Condition startEvent; @@ -50,9 +51,20 @@ public class Goal extends MetadataItem implements YamlConvertible { private String campaignId; + /** + * Constructs an empty {@link Goal} instance. + * This constructor is typically used when initializing a goal object + * programmatically before setting its specific metadata and events. + */ public Goal() { } + /** + * Constructs a {@link Goal} instance initialized with + * the provided metadata. + * to be identified or configured within a system context. + * @param metadata The metadata to associate with this goal, allowing it + */ public Goal(Metadata metadata) { super(metadata); } @@ -60,39 +72,58 @@ public Goal(Metadata metadata) { /** * Retrieves the {@link Condition} determining the goal's start event if any, used for more complex goals where an action has to be accomplished first before evaluating the * success of the final goal (funnel goal for example). - * * @return the condition associated with the start event for this goal or {@code null} if no such event exists */ public Condition getStartEvent() { return startEvent; } + /** + * Sets the condition that must occur for the goal to begin tracking. + * This is used when the goal's success depends on an initial event + * occurring, such as in a funnel scenario. + * @param startEvent The {@link Condition} defining the starting event, or + * null if no specific start event is required. + */ public void setStartEvent(Condition startEvent) { this.startEvent = startEvent; } /** * Retrieves the {@link Condition} determining the target event which needs to occur to consider the goal accomplished. - * * @return the condition associated with the event determining if the goal is reached or not */ public Condition getTargetEvent() { return targetEvent; } + /** + * Sets the condition that must occur for the goal to be + * considered accomplished. + * This defines the target event that determines whether the + * goal has been reached. + * @param targetEvent The {@link Condition} defining the target event, or + * null if no specific target event is required. + */ public void setTargetEvent(Condition targetEvent) { this.targetEvent = targetEvent; } /** * Retrieves the identifier of the campaign this goal is part of, if any. - * * @return the identifier of the campaign this goal is part of, or {@code null} if this goal is not part of any campaign */ public String getCampaignId() { return campaignId; } + /** + * Sets the identifier of the campaign to which this goal belongs. + * This allows goals to be scoped and managed within a specific + * marketing campaign context. + * @param campaignId The unique ID of the associated campaign, or + * {@code null} if the goal is not tied to a campaign. + */ public void setCampaignId(String campaignId) { this.campaignId = campaignId; } @@ -100,7 +131,6 @@ public void setCampaignId(String campaignId) { /** * Converts this goal to a Map structure for YAML output. * Implements YamlConvertible interface with circular reference detection. - * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this goal */ diff --git a/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java b/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java index 1d4413805..0211194ba 100644 --- a/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java +++ b/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java @@ -21,28 +21,51 @@ import java.util.List; /** - * Report information about a {@link Goal}. + * Performance statistics for a {@link org.apache.unomi.api.goals.Goal}. + * Contains counts and rates (starts, conversions, etc.) used to measure + * whether visitors complete the tracked goal. */ public class GoalReport implements Serializable { private static final long serialVersionUID = -9150361970326342064L; private Stat globalStats; private List split; + /** + * Constructs a new GoalReport instance. + */ public GoalReport() { } + /** + * Retrieves the global statistics associated with this report. + * @return The {@link Stat} containing global statistics. + */ public Stat getGlobalStats() { return globalStats; } + /** + * Sets the global statistics for this report. + * @param globalStats The {@link Stat} object representing + * the global statistics. + */ public void setGlobalStats(Stat globalStats) { this.globalStats = globalStats; } + /** + * Retrieves the list of split statistics associated with this report. + * @return A {@link List} of {@link Stat} objects representing the splits. + */ public List getSplit() { return split; } + /** + * Sets the list of split statistics for this report. + * @param split The {@link List} of {@link Stat} objects + * defining the splits. + */ public void setSplit(List split) { this.split = split; } @@ -58,45 +81,95 @@ public static class Stat implements Serializable { private double conversionRate; private double percentage; + /** + * Constructs a new {@code Stat} instance. + */ public Stat() { } + /** + * Retrieves the key associated with this statistic. + * @return The unique identifier (key) for the stat. + */ public String getKey() { return key; } + /** + * Sets the key used to identify this statistic. + * @param key The unique string key. + */ public void setKey(String key) { this.key = key; } + /** + * Retrieves the starting count of records analyzed. + * @return The total number of records at the start count. + */ public long getStartCount() { return startCount; } + /** + * Sets the initial count for this statistic. + * @param startCount The total number of records. + */ public void setStartCount(long startCount) { this.startCount = startCount; } + /** + * Retrieves the target count of successful records. + * @return The number of records that met the target criteria. + */ public long getTargetCount() { return targetCount; } + /** + * Sets the target count for this statistic. + * @param targetCount The desired or measured target count. + */ public void setTargetCount(long targetCount) { this.targetCount = targetCount; } + /** + * Retrieves the calculated conversion rate (Target + * Count / Start Count). + * @return The conversion rate as a double value. + */ public double getConversionRate() { return conversionRate; } + /** + * Sets the conversion rate statistic. + * This value is typically calculated as a ratio of successful + * targets to starting counts. + * @param conversionRate the calculated conversion rate (target + * count / start count). + */ public void setConversionRate(double conversionRate) { this.conversionRate = conversionRate; } + /** + * Retrieves the percentage value stored in this statistic. + * This value represents a proportion, often relative to another total. + * @return the percentage value. + */ public double getPercentage() { return percentage; } + /** + * Sets the calculated percentage for this goal report statistic. + * This value is typically derived by dividing target counts by + * a larger total count. + * @param percentage the calculated percentage value. + */ public void setPercentage(double percentage) { this.percentage = percentage; } diff --git a/api/src/main/java/org/apache/unomi/api/lists/UserList.java b/api/src/main/java/org/apache/unomi/api/lists/UserList.java index dc82b89b5..35d99d446 100644 --- a/api/src/main/java/org/apache/unomi/api/lists/UserList.java +++ b/api/src/main/java/org/apache/unomi/api/lists/UserList.java @@ -22,7 +22,9 @@ import org.apache.unomi.api.MetadataItem; /** - * Created by amidani on 24/03/2017. + * Named list of profile identifiers maintained for campaigns or exports. + * User lists let marketers target or exclude a fixed audience without + * defining a dynamic segment. */ public class UserList extends MetadataItem{ diff --git a/api/src/main/java/org/apache/unomi/api/query/DateRange.java b/api/src/main/java/org/apache/unomi/api/query/DateRange.java index 47390b3d8..029073759 100644 --- a/api/src/main/java/org/apache/unomi/api/query/DateRange.java +++ b/api/src/main/java/org/apache/unomi/api/query/DateRange.java @@ -20,36 +20,65 @@ import java.io.Serializable; /** - * A data range. + * Named inclusive date interval used in queries and aggregations. + * Pairs a label ({@code key}) with {@code from} and {@code to} bounds so + * conditions can match events or profiles inside a calendar window. */ public class DateRange implements Serializable { private String key; private Object from; private Object to; + /** + * Constructs a new {@link DateRange} instance. + */ public DateRange() { } + /** + * Retrieves the unique key associated with this date range. + * @return The string key of the range. + */ public String getKey() { return key; } + /** + * Sets the unique identifier (key) for this date range. + * @param key the key to set + */ public void setKey(String key) { this.key = key; } + /** + * Retrieves the starting value (from) of the date range. + * @return The object representing the start time or value. + */ public Object getFrom() { return from; } + /** + * Sets the starting value (from) for this date range. + * @param from the starting value to set + */ public void setFrom(Object from) { this.from = from; } + /** + * Retrieves the ending value (to) of the date range. + * @return The object representing the end time or value. + */ public Object getTo() { return to; } + /** + * Sets the ending value (to) for this date range. + * @param to the ending value to set + */ public void setTo(Object to) { this.to = to; } diff --git a/api/src/main/java/org/apache/unomi/api/query/IpRange.java b/api/src/main/java/org/apache/unomi/api/query/IpRange.java index 411f58598..b33f16dcc 100644 --- a/api/src/main/java/org/apache/unomi/api/query/IpRange.java +++ b/api/src/main/java/org/apache/unomi/api/query/IpRange.java @@ -20,36 +20,65 @@ import java.io.Serializable; /** - * An IP address range. + * Named inclusive IP address interval for geo or network filtering. + * Used in segment conditions to match visitors whose IP falls between + * the configured lower and upper bounds. */ public class IpRange implements Serializable { private String key; private String from; private String to; + /** + * Constructs an empty {@link IpRange} instance. + */ public IpRange() { } + /** + * Retrieves the unique key associated with this IP range. + * @return The string key identifying the range. + */ public String getKey() { return key; } + /** + * Sets the unique identifier (key) for this IP range. + * @param key The key to assign to the range. + */ public void setKey(String key) { this.key = key; } + /** + * Retrieves the starting IP address string of the range. + * @return The 'from' boundary of the IP range. + */ public String getFrom() { return from; } + /** + * Sets the starting IP address string for this range. + * @param from The start boundary of the IP range. + */ public void setFrom(String from) { this.from = from; } + /** + * Retrieves the ending IP address string of the range. + * @return The 'to' boundary of the IP range. + */ public String getTo() { return to; } + /** + * Sets the ending IP address string for this range. + * @param to The end boundary of the IP range. + */ public void setTo(String to) { this.to = to; } diff --git a/api/src/main/java/org/apache/unomi/api/query/NumericRange.java b/api/src/main/java/org/apache/unomi/api/query/NumericRange.java index 149a35805..bb8933785 100644 --- a/api/src/main/java/org/apache/unomi/api/query/NumericRange.java +++ b/api/src/main/java/org/apache/unomi/api/query/NumericRange.java @@ -20,36 +20,66 @@ import java.io.Serializable; /** - * A numeric range. + * Named inclusive numeric interval for query filters. + * Stores a label plus lower and upper bounds so conditions can match + * numeric profile or event properties in a range. */ public class NumericRange implements Serializable { private String key; private Double from; private Double to; + /** + * Constructs a new NumericRange object. + */ public NumericRange() { } + /** + * Retrieves the unique key associated with this numeric range. + * @return The string key identifying the range. + */ public String getKey() { return key; } + /** + * Sets the unique identifier (key) for this numeric range. + * @param key the key to set + */ public void setKey(String key) { this.key = key; } + /** + * Retrieves the starting value of the numeric range. + * @return The lower bound (inclusive start) of the + * range, or null if not set. + */ public Double getFrom() { return from; } + /** + * Sets the starting value for this numeric range. + * @param from the inclusive starting value of the range + */ public void setFrom(Double from) { this.from = from; } + /** + * Retrieves the ending value of the numeric range. + * @return The upper bound (inclusive end) of the range, or null if not set. + */ public Double getTo() { return to; } + /** + * Sets the ending value for this numeric range. + * @param to the inclusive ending value of the range + */ public void setTo(Double to) { this.to = to; } diff --git a/api/src/main/java/org/apache/unomi/api/query/Query.java b/api/src/main/java/org/apache/unomi/api/query/Query.java index 48e0c1a6b..bcfc9e3d0 100644 --- a/api/src/main/java/org/apache/unomi/api/query/Query.java +++ b/api/src/main/java/org/apache/unomi/api/query/Query.java @@ -22,9 +22,9 @@ import java.io.Serializable; /** - * A query wrapper gathering all elements needed for a potentially complex CXS query: {@link Condition}, offset, limit, sorting specification, etc. - * - * Created by kevan on 14/05/15. + * Full specification for a context server search or aggregation. + * Combines a {@link Condition}, paging (offset/limit), sort order, and + * optional aggregate definitions in one request object. */ public class Query implements Serializable { private String text; @@ -44,7 +44,6 @@ public Query() { /** * Retrieves the text to be used in full-text searches, if any. - * * @return the text to be used in full-text searches or {@code null} if no full-text search is needed for this Query */ public String getText() { @@ -53,7 +52,6 @@ public String getText() { /** * Sets to be used in full-text searches - * * @param text the text to be used in full-text searches or {@code null} if no full-text search is needed for this Query */ public void setText(String text) { @@ -63,7 +61,6 @@ public void setText(String text) { /** * Retrieves the offset of the first element to be retrieved - * * @return zero or a positive integer specifying the position of the first item to be retrieved in the total ordered collection of matching items */ public int getOffset() { @@ -72,7 +69,6 @@ public int getOffset() { /** * Sets the offset of the first element to be retrieved - * * @param offset zero or a positive integer specifying the position of the first item to be retrieved in the total ordered collection of matching items */ public void setOffset(int offset) { @@ -81,7 +77,6 @@ public void setOffset(int offset) { /** * Retrieves the number of elements to retrieve. - * * @return a positive integer specifying how many matching items should be retrieved or {@code -1} if all of them should be retrieved */ public int getLimit() { @@ -90,7 +85,6 @@ public int getLimit() { /** * Sets the number of elements to retrieve. - * * @param limit a positive integer specifying how many matching items should be retrieved or {@code -1} if all of them should be retrieved */ public void setLimit(int limit) { @@ -99,7 +93,6 @@ public void setLimit(int limit) { /** * Retrieves the sorting specifications for this Query in String format, if any. - * * @return an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements * according to the property order in the String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is * optionally followed by a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. @@ -110,7 +103,6 @@ public String getSortby() { /** * Sets the String representation of the sorting specifications for this Query if any. See {@link #getSortby()} method documentation for format. - * * @param sortby the String representation of the sorting specifications for this Query or {@code null} if no sorting is required */ public void setSortby(String sortby) { @@ -119,7 +111,6 @@ public void setSortby(String sortby) { /** * Retrieves the {@link Condition} associated with this Query. - * * @return the {@link Condition} associated with this Query */ public Condition getCondition() { @@ -128,7 +119,6 @@ public Condition getCondition() { /** * Sets the {@link Condition} associated with this Query. - * * @param condition the {@link Condition} associated with this Query */ public void setCondition(Condition condition) { @@ -137,7 +127,6 @@ public void setCondition(Condition condition) { /** * Determines whether or not an index refresh is needed before performing this Query - * * @return {@code true} if an index refresh is needed before performing this Query, {@code false} otherwise */ public boolean isForceRefresh() { @@ -146,25 +135,56 @@ public boolean isForceRefresh() { /** * Specifies whether or not an index refresh is needed before performing this Query. - * * @param forceRefresh {@code true} if an index refresh is needed before performing this Query, {@code false} otherwise */ public void setForceRefresh(boolean forceRefresh) { this.forceRefresh = forceRefresh; } + /** + * Retrieves the unique identifier used to continue a + * scrolling search session. + * This identifier must be provided when performing subsequent searches + * that rely on the results of a previous query. + * @return the scroll identifier, or {@code null} if no + * scroll session is active. + */ public String getScrollIdentifier() { return scrollIdentifier; } + /** + * Sets the unique identifier required to continue a + * scrolling search session. + * This value should be obtained from the results of a previous query when + * performing subsequent searches that rely on the continuity of results. + * @param scrollIdentifier the scroll identifier used for continuation, or + * {@code null} if no scroll is needed. + */ public void setScrollIdentifier(String scrollIdentifier) { this.scrollIdentifier = scrollIdentifier; } + /** + * Retrieves the configured time validity period for the + * current scroll session. + * This value determines how long the search results remain valid for + * continuation queries. + * @return the string representation of the time validity (e.g., "10m"), or + * {@code null} if no specific validity is set. + */ public String getScrollTimeValidity() { return scrollTimeValidity; } + /** + * Sets the time validity period for the current scroll session. + * This value specifies how long the search results remain available for + * continuation queries. + * @param scrollTimeValidity the string representation of the time validity + * (e.g., "10m"), or {@code null} if no specific + * validity is required. + */ public void setScrollTimeValidity(String scrollTimeValidity) { this.scrollTimeValidity = scrollTimeValidity; } diff --git a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java index 4dd18c79b..2cad6e77b 100644 --- a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java +++ b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java @@ -29,7 +29,6 @@ public class RuleStatistics extends Item { /** * The RuleStatistics ITEM_TYPE. - * * @see Item for a discussion of ITEM_TYPE */ public static final String ITEM_TYPE = "rulestats"; @@ -43,9 +42,20 @@ public class RuleStatistics extends Item { private long localActionsTime = 0; private Date lastSyncDate; + /** + * Constructs a new RuleStatistics instance with default values. + * This constructor is used when initializing statistics objects without + * an associated item ID. + */ public RuleStatistics() { } + /** + * Constructs a new RuleStatistics instance, associating it with + * the given rule item ID. + * @param itemId The unique identifier of the rule item to which + * these statistics belong. + */ public RuleStatistics(String itemId) { super(itemId); } diff --git a/api/src/main/java/org/apache/unomi/api/security/EncryptionService.java b/api/src/main/java/org/apache/unomi/api/security/EncryptionService.java index d77c8c1e6..5d23c84af 100644 --- a/api/src/main/java/org/apache/unomi/api/security/EncryptionService.java +++ b/api/src/main/java/org/apache/unomi/api/security/EncryptionService.java @@ -17,7 +17,8 @@ package org.apache.unomi.api.security; /** - * Service for handling encryption operations. + * Encrypts and decrypts sensitive tenant or configuration values. + * Implementations hide key management details from the rest of the API. */ public interface EncryptionService { /** diff --git a/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java b/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java index f3ccc0d31..058b183fb 100644 --- a/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java +++ b/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java @@ -22,22 +22,85 @@ import java.util.Set; /** - * Configuration for the Security Service + * Settings that control authentication, authorization, and API access. + * Loaded at startup to configure how the security layer validates users, + * roles, and tenant principals. */ public class SecurityServiceConfiguration { - // Permission constants + /** + * Defines the permission key used when querying items, which is also + * utilized to validate the tenant ID scope for query operations. + */ public static final String PERMISSION_QUERY = "QUERY"; + /** + * Defines the permission key required for performing aggregations on item + * data, and is used internally to validate the tenant ID scope during + * aggregation queries. + */ public static final String PERMISSION_AGGREGATE = "AGGREGATE"; + /** + * Defines the permission key necessary for continuing or + * initiating a scroll query. + * This constant is used to validate the tenant ID when + * executing scroll operations. + */ public static final String PERMISSION_SCROLL_QUERY = "SCROLL_QUERY"; + /** + * Defines the permission key required for saving new items. It is also used + * internally to ensure that the correct tenant ID is set + * during save operations. + */ public static final String PERMISSION_SAVE = "SAVE"; + /** + * Defines the permission key necessary for updating existing items. This + * constant is utilized to validate the tenant ID scope when + * performing item updates. + */ public static final String PERMISSION_UPDATE = "UPDATE"; + /** + * Defines the permission key required for deleting items. It is used both + * in defining default roles and internally to validate the tenant ID during + * deletion operations. + */ public static final String PERMISSION_DELETE = "DELETE"; + /** + * Defines the permission key needed when removing multiple items + * using a query condition. + * This constant is used to validate the tenant ID scope for remove + * by query operations. + */ public static final String PERMISSION_REMOVE_BY_QUERY = "REMOVE_BY_QUERY"; + /** + * Defines the permission key required for purging data. It is utilized both + * in defining default roles and internally to validate the tenant ID + * during purge operations. + */ public static final String PERMISSION_PURGE = "PURGE"; + /** + * Defines the permission key required for performing system + * maintenance operations. + */ public static final String PERMISSION_SYSTEM_MAINTENANCE = "SYSTEM_MAINTENANCE"; + /** + * Defines the permission key needed when profile data + * encryption is required. + */ public static final String PERMISSION_ENCRYPT_PROFILE_DATA = "ENCRYPT_PROFILE_DATA"; + /** + * Defines the permission key needed when profile data decryption is + * required. This constant is used to validate the tenant ID scope + * for remove operations. + */ public static final String PERMISSION_DECRYPT_PROFILE_DATA = "DECRYPT_PROFILE_DATA"; + /** + * Defines the permission key required for writing or modifying schemas. + * This constant is used to validate access during schema write operations. + */ public static final String PERMISSION_SCHEMA_WRITE = "SCHEMA_WRITE"; + /** + * Defines the permission key required for deleting schemas. This constant + * is used to validate access during schema delete operations. + */ public static final String PERMISSION_SCHEMA_DELETE = "SCHEMA_DELETE"; private Map permissionRoles; @@ -45,6 +108,11 @@ public class SecurityServiceConfiguration { private Set systemRoles = new HashSet<>(); private boolean enableEncryption = false; + /** + * Constructs a default security service configuration, initializing system + * roles and mapping permissions for standard operations like query, save, + * update, etc., with default roles. + */ public SecurityServiceConfiguration() { // Initialize default system roles systemRoles.add(UnomiRoles.ADMINISTRATOR); @@ -68,18 +136,42 @@ public SecurityServiceConfiguration() { defaultRole = UnomiRoles.USER; } + /** + * Retrieves the map containing defined permission keys and the array of + * roles authorized to use them. + * The returned map is a direct reference to the internal configuration. + * @return A {@link Map} where keys are permission strings and values are + * arrays of required role names. + */ public Map getPermissionRoles() { return permissionRoles; } + /** + * Sets the complete mapping of permissions to roles for the security + * service. This allows external configuration of which roles can perform + * specific operations. + * @param permissionRoles The map containing all permission keys and their + * associated authorized roles. + */ public void setPermissionRoles(Map permissionRoles) { this.permissionRoles = permissionRoles; } + /** + * Retrieves the default role assigned to users or + * entities within the system. + * @return The configured default role string. + */ public String getDefaultRole() { return defaultRole; } + /** + * Sets the default role that should be applied when a user + * or entity is created. + * @param defaultRole the role to set as the default for new entities. + */ public void setDefaultRole(String defaultRole) { this.defaultRole = defaultRole; } @@ -93,26 +185,59 @@ public String[] getRequiredRolesForPermission(String permission) { return permissionRoles.getOrDefault(permission, new String[]{defaultRole}); } + /** + * Gets the immutable set of system roles recognized by the security + * service, such as administrative or core platform roles. + * @return A {@link Set} of strings representing the defined system roles. + */ public Set getSystemRoles() { return systemRoles; } + /** + * Sets all system roles that the security service should + * recognize and manage. + * @param systemRoles a set containing all system role names. + */ public void setSystemRoles(Set systemRoles) { this.systemRoles = systemRoles; } + /** + * Adds a single specified role name to the collection of + * recognized system roles. + * This allows dynamic configuration of core platform roles. + * @param role the role string to add to the system roles list. + */ public void addSystemRole(String role) { systemRoles.add(role); } + /** + * Removes a specified role name from the set of recognized system roles. + * This should be used when a system role is deprecated or removed. + * @param role the role string to remove from the system roles list. + */ public void removeSystemRole(String role) { systemRoles.remove(role); } + /** + * Checks if profile data encryption has been enabled for + * the security service. + * @return {@code true} if encryption is active, {@code false} otherwise. + */ public boolean isEnableEncryption() { return enableEncryption; } + /** + * Sets whether sensitive profile data should be encrypted when stored and + * retrieved by the security service. This controls the use of + * encryption mechanisms. + * @param enableEncryption {@code true} to enable encryption, + * {@code false} otherwise. + */ public void setEnableEncryption(boolean enableEncryption) { this.enableEncryption = enableEncryption; } diff --git a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java index 1830c1ee5..0eec441e4 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java +++ b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java @@ -22,29 +22,56 @@ import java.io.Serializable; import java.util.List; +/** + * Holds segment and scoring definitions that depend on each other. + * Used when exporting or editing related segment/scoring metadata as one + * unit in the segmentation UI. + */ public class DependentMetadata implements Serializable { private List segments; private List scorings; + /** + * Constructs a {@link DependentMetadata} instance with specified + * segment and scoring lists. + * @param segments the list of metadata defining required segments. + * @param scorings the list of metadata defining associated scores. + */ public DependentMetadata(List segments, List scorings) { this.segments = segments; this.scorings = scorings; } + /** + * Retrieves the list of metadata representing the dependent segments. + * @return a {@link List} containing the segment definitions. + */ public List getSegments() { return segments; } + /** + * Sets the list of metadata defining the required segments for this object. + * @param segments the new list of segment metadata. + */ public void setSegments(List segments) { this.segments = segments; } + /** + * Retrieves the list of metadata representing the dependent scorings. + * @return a {@link List} containing the scoring definitions. + */ public List getScorings() { return scorings; } + /** + * Sets the list of metadata defining the associated scores for this object. + * @param scorings the new list of score metadata. + */ public void setScorings(List scorings) { this.scorings = scorings; } diff --git a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java index ab79d132a..801132c57 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java +++ b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java @@ -30,7 +30,9 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue; /** - * A scoring dimension along profiles can be evaluated and associated value to be assigned. + * One dimension of a {@link org.apache.unomi.api.segments.Scoring} model. + * Assigns a numeric value along an axis (for example engagement) when a + * profile matches the element's condition. */ public class ScoringElement implements Serializable, YamlConvertible { private Condition condition; diff --git a/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java b/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java index f9fafaf3a..da8fe4dec 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java +++ b/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java @@ -24,7 +24,9 @@ import java.util.Set; /** - * A combination of {@link Segment} and scores (usually associated with a {@link Profile}). + * Snapshot of segment memberships and scoring values for a profile. + * Returned when clients need both which segments apply and the current + * score totals in one structure. */ public class SegmentsAndScores implements Serializable { private Set segments; diff --git a/api/src/main/java/org/apache/unomi/api/services/ClusterService.java b/api/src/main/java/org/apache/unomi/api/services/ClusterService.java index f100be286..1b507959c 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ClusterService.java +++ b/api/src/main/java/org/apache/unomi/api/services/ClusterService.java @@ -23,8 +23,9 @@ import java.util.List; /** - * A service to access information about the context server's cluster. - * + * Access point for cluster topology and node health. + * Returns {@link ClusterNode} records and coordinates cluster-wide + * operations such as viewing which nodes store data. */ public interface ClusterService { 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 2f77c95a8..5757b3133 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 @@ -38,7 +38,6 @@ public interface ConditionValidationService { *

* 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 when the condition is valid */ @@ -58,7 +57,6 @@ class ValidationError { /** * 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 @@ -69,7 +67,6 @@ public ValidationError(String parameterName, String message, ValidationErrorType /** * 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 @@ -92,7 +89,6 @@ public ValidationError(String parameterName, String message, ValidationErrorType /** * Retrieves the name of the parameter that caused the error. - * * @return the parameter name, or {@code null} if not applicable */ public String getParameterName() { @@ -101,7 +97,6 @@ public String getParameterName() { /** * Retrieves the error description message. - * * @return the error message */ public String getMessage() { @@ -110,7 +105,6 @@ public String getMessage() { /** * Retrieves the type of validation error. - * * @return the error type */ public ValidationErrorType getType() { @@ -119,7 +113,6 @@ public ValidationErrorType getType() { /** * Retrieves the identifier of the condition associated with this error. - * * @return the condition identifier, or {@code null} if not applicable */ public String getConditionId() { @@ -128,7 +121,6 @@ public String getConditionId() { /** * 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() { @@ -137,6 +129,7 @@ public String getConditionTypeId() { /** * @deprecated Use {@link #getConditionTypeId()} instead. + * @return the condition type identifier (same as {@link #getConditionTypeId()}) */ @Deprecated public String getConditionTypeName() { @@ -145,7 +138,6 @@ public String getConditionTypeName() { /** * Retrieves a copy of the additional context map for this error. - * * @return the context map */ public Map getContext() { @@ -154,7 +146,6 @@ public Map getContext() { /** * Retrieves the parent error that caused this error, if any. - * * @return the parent error, or {@code null} if none */ public ValidationError getParentError() { @@ -163,7 +154,6 @@ public ValidationError getParentError() { /** * Retrieves a detailed error message including condition, parameter, context, and parent error information. - * * @return the detailed error message */ public String getDetailedMessage() { @@ -231,13 +221,20 @@ enum ValidationErrorType { private final String description; + /** + * Constructs an error type, setting its descriptive label. + * This initializes the {@code ValidationErrorType} constant with a + * specific human-readable description used to identify + * validation failures. + * @param description the descriptive text associated + * with this error type. + */ ValidationErrorType(String description) { this.description = description; } /** * Retrieves the human-readable description of this error type. - * * @return the error type description */ public String getDescription() { diff --git a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java index 03da8090b..96e00a065 100644 --- a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java +++ b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java @@ -31,7 +31,9 @@ import java.util.Set; /** - * A service to retrieve definition information about core context server entities such as conditions, actions and values. + * Registry of built-in and plugin condition, action, and value type definitions. + * Used when loading rules from JSON, validating conditions, and resolving + * type metadata in the administration UI. */ public interface DefinitionsService { /** diff --git a/api/src/main/java/org/apache/unomi/api/services/EventListenerService.java b/api/src/main/java/org/apache/unomi/api/services/EventListenerService.java index 00bccb07e..8a5a4d46a 100644 --- a/api/src/main/java/org/apache/unomi/api/services/EventListenerService.java +++ b/api/src/main/java/org/apache/unomi/api/services/EventListenerService.java @@ -20,7 +20,9 @@ import org.apache.unomi.api.Event; /** - * A service that gets notified (via {@link #onEvent(Event)}) whenever an event it can handle as decided by {@link #canHandle(Event)} occurs in the context server. + * Callback invoked for each {@link Event} the listener chooses to handle. + * Plugins implement this interface to react to events in real time alongside + * the rules engine. */ public interface EventListenerService { diff --git a/api/src/main/java/org/apache/unomi/api/services/EventService.java b/api/src/main/java/org/apache/unomi/api/services/EventService.java index d68a2c703..f251c1961 100644 --- a/api/src/main/java/org/apache/unomi/api/services/EventService.java +++ b/api/src/main/java/org/apache/unomi/api/services/EventService.java @@ -29,7 +29,9 @@ import java.util.Set; /** - * A service to publish events, notably issued from user interactions with tracked entities, in the context server. + * Publishes and retrieves {@link Event}s in the context server. + * Client integrations send visitor actions here; rules and listeners + * consume the same event stream. */ public interface EventService { diff --git a/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java b/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java index da1ab18a0..a163edd4d 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java +++ b/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java @@ -21,7 +21,8 @@ import java.util.function.Supplier; /** - * Service interface for managing execution contexts in Unomi. + * Creates and binds {@link ExecutionContext} instances to the current thread. + * Ensures service calls run with the correct tenant and security credentials. */ public interface ExecutionContextManager { diff --git a/api/src/main/java/org/apache/unomi/api/services/GoalsService.java b/api/src/main/java/org/apache/unomi/api/services/GoalsService.java index 08e384e36..c04c3a06b 100644 --- a/api/src/main/java/org/apache/unomi/api/services/GoalsService.java +++ b/api/src/main/java/org/apache/unomi/api/services/GoalsService.java @@ -31,7 +31,9 @@ import java.util.Set; /** - * A service to interact with {@link Goal}s and {@link Campaign}s. + * CRUD and reporting API for {@link org.apache.unomi.api.goals.Goal}s and + * {@link Campaign}s. Manages goal definitions, campaign lifecycle, and + * related statistics. */ public interface GoalsService { /** diff --git a/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java b/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java index 7307fb4bd..aa443af7b 100644 --- a/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java +++ b/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java @@ -23,7 +23,9 @@ import java.util.concurrent.atomic.AtomicLong; /** - * Information about an invalid object, including detailed reasons for invalidation. + * Diagnostic record for a configuration item that failed validation. + * Lists missing condition or action types and the contexts where the + * invalid object was seen so operators can fix definitions. */ public class InvalidObjectInfo { private final String objectType; @@ -36,10 +38,35 @@ public class InvalidObjectInfo { private final Set missingActionTypeIds; private final Set contextNames; + /** + * Constructs an {@code InvalidObjectInfo} object using only the basic + * identifying information. + * This constructor delegates to the full constructor, initializing missing + * lists and context names as null or empty. + * @param objectType The type name of the invalid object. Must not be null. + * @param objectId The unique ID of the invalid object. Must not be null. + * @param reason A detailed explanation for why the object + * is considered invalid. + */ public InvalidObjectInfo(String objectType, String objectId, String reason) { this(objectType, objectId, reason, null, null, null); } + /** + * Constructs an {@code InvalidObjectInfo} object with full details, + * including initial tracking information. + * This initializes timestamps and encounter counts upon creation. + * @param objectType The type name of the invalid object. Must not be null. + * @param objectId The unique ID of the invalid object. Must not be null. + * @param reason A detailed explanation for why the object + * is considered invalid. + * @param missingConditionTypeIds Initial list of condition types that were + * missing during validation. + * @param missingActionTypeIds Initial list of action types that were + * missing during validation. + * @param contextName The specific context where this initial encounter + * occurred. Can be null. + */ public InvalidObjectInfo(String objectType, String objectId, String reason, List missingConditionTypeIds, List missingActionTypeIds, @@ -60,38 +87,82 @@ public InvalidObjectInfo(String objectType, String objectId, String reason, } } + /** + * Retrieves the type name associated with this invalid object record. + * @return The {@code String} representing the object's type name. + */ public String getObjectType() { return objectType; } + /** + * Retrieves the unique ID of the invalid object record. + * @return The {@code String} representing the object's ID. + */ public String getObjectId() { return objectId; } + /** + * Retrieves the reason why this object was flagged as invalid. + * @return The detailed explanation for the invalidation, or {@code null} + * if none is provided. + */ public String getReason() { return reason; } + /** + * Gets the timestamp when this object record was first observed as invalid. + * @return The initial {@code long} timestamp (milliseconds since epoch). + */ public long getFirstSeenTimestamp() { return firstSeenTimestamp; } + /** + * Gets the most recent timestamp when this object record was + * observed as invalid. + * This value is updated whenever an encounter occurs. + * @return The latest {@code long} timestamp (milliseconds since epoch). + */ public long getLastSeenTimestamp() { return lastSeenTimestamp.get(); } + /** + * Returns the total number of times this object has been + * encountered as invalid. + * @return An integer count representing the cumulative encounters. + */ public int getEncounterCount() { return encounterCount.get(); } + /** + * Retrieves an unmodifiable list of condition type IDs that were found to + * be missing or unresolved for this object instance. + * @return A {@link java.util.List} containing the + * missing condition type IDs. + */ public List getMissingConditionTypeIds() { return Collections.unmodifiableList(new ArrayList<>(missingConditionTypeIds)); } + /** + * Retrieves an unmodifiable list of action type IDs that were found to be + * missing or unresolved for this object instance. + * @return A {@link java.util.List} containing the missing action type IDs. + */ public List getMissingActionTypeIds() { return Collections.unmodifiableList(new ArrayList<>(missingActionTypeIds)); } + /** + * Returns an unmodifiable set of context names in which this invalid + * object was encountered. + * @return A {@link java.util.Set} containing all recorded context names. + */ public Set getContextNames() { return Collections.unmodifiableSet(contextNames); } @@ -101,7 +172,6 @@ public Set getContextNames() { * Thread-safe: backed by CopyOnWriteArrayList/CopyOnWriteArraySet, so reads via * {@code getMissingConditionTypeIds()}, {@code getMissingActionTypeIds()}, and * {@code getContextNames()} are safe during concurrent writes. - * * @param missingConditionTypeIds additional missing condition type IDs found in this encounter * @param missingActionTypeIds additional missing action type IDs found in this encounter * @param contextName context where this encounter occurred diff --git a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java index f4dea3d9d..4c0c7b9d9 100644 --- a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java +++ b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java @@ -26,13 +26,14 @@ import java.util.Map; /** - * A service to fulfill personalization request. + * Resolves which content variants to show a visitor. + * Evaluates personalization requests against profiles, sessions, and + * segments, returning {@link PersonalizationResult} with matching content ids. */ public interface PersonalizationService { /** * Check if an item is visible for the specified profile and session - * * @param profile The profile * @param session The session * @param personalizedContent Personalized content, containing a list of filters @@ -42,7 +43,6 @@ public interface PersonalizationService { /** * Get the best match among a list of items, for the specified profile and session - * * @param profile The profile * @param session The session * @param personalizationRequest Personalization request, containing the list of variants and the required strategy @@ -52,7 +52,6 @@ public interface PersonalizationService { /** * Get a personalized list, filtered and sorted, based on the profile and session - * * @param profile The profile * @param session The session * @param personalizationRequest Personalization request, containing the list of variants and the required strategy @@ -69,34 +68,70 @@ class PersonalizationRequest { private Map strategyOptions; private List contents; + /** + * Gets the unique identifier of this personalization request. + * @return The ID string. + */ public String getId() { return id; } + /** + * Sets the unique identifier for this personalization request. + * @param id The unique ID to set. + */ public void setId(String id) { this.id = id; } + /** + * Retrieves the name of the personalization strategy to be used. + * @return The configured strategy name string. + */ public String getStrategy() { return strategy; } + /** + * Sets the personalization strategy that should process this request. + * @param strategy The name of the strategy (e.g., "alwaysSet"). + */ public void setStrategy(String strategy) { this.strategy = strategy; } + /** + * Gets the list of personalized content items + * associated with this request. + * @return A list of {@link PersonalizedContent} objects. + */ public List getContents() { return contents; } + /** + * Sets the collection of personalized content that should be processed. + * @param contents The list of {@link PersonalizedContent} to set. + */ public void setContents(List contents) { this.contents = contents; } + /** + * Retrieves optional configuration parameters specific to the + * personalization strategy. + * @return A map containing strategy-specific options, or + * null if none are set. + */ public Map getStrategyOptions() { return strategyOptions; } + /** + * Sets the map of optional configuration parameters for the + * personalization process. + * @param strategyOptions The map of options to apply. + */ public void setStrategyOptions(Map strategyOptions) { this.strategyOptions = strategyOptions; } @@ -112,7 +147,6 @@ class PersonalizedContent { /** * Retrieves the filter identifier associated with this content filtering definition. - * * @return the filter identifier associated with this content filtering definition */ public String getId() { @@ -121,7 +155,6 @@ public String getId() { /** * Sets the filter identifier associated with this content filtering definition. - * * @param id the filter identifier associated with this content filtering definition */ public void setId(String id) { @@ -130,7 +163,6 @@ public void setId(String id) { /** * Sets the filter identifier associated with this content filtering definition. - * * @param filterid the filter identifier associated with this content filtering definition * @deprecated As of version 1.3.0-incubating, please use {@link #setId(String)} instead */ @@ -141,7 +173,6 @@ public void setFilterid(String filterid) { /** * Retrieves the filters. - * * @return the filters */ public List getFilters() { @@ -150,17 +181,24 @@ public List getFilters() { /** * Sets the filters. - * * @param filters the filters */ public void setFilters(List filters) { this.filters = filters; } + /** + * Retrieves the properties associated with this content definition. + * @return The map of properties stored in this object. + */ public Map getProperties() { return properties; } + /** + * Sets the properties associated with this content definition. + * @param properties The map containing the properties to set. + */ public void setProperties(Map properties) { this.properties = properties; } @@ -176,7 +214,6 @@ class Filter { /** * Retrieves the list of targets this filter applies on. - * * @return the applies on */ public List getAppliesOn() { @@ -185,7 +222,6 @@ public List getAppliesOn() { /** * Specifies which targets this filter applies on. - * * @param appliesOn the list of {@link Target} this filter should be applied on */ public void setAppliesOn(List appliesOn) { @@ -194,7 +230,6 @@ public void setAppliesOn(List appliesOn) { /** * Retrieves the condition associated with this filter. - * * @return the condition associated with this filter */ public Condition getCondition() { @@ -203,17 +238,27 @@ public Condition getCondition() { /** * Sets the condition associated with this filter. - * * @param condition the condition associated with this filter */ public void setCondition(Condition condition) { this.condition = condition; } + /** + * Retrieves the map of properties associated with this filter. + * This method returns a reference to the internal + * {@code properties} map. + * @return the properties map + */ public Map getProperties() { return properties; } + /** + * Sets the properties associated with this filter. + * The provided map replaces any existing properties. + * @param properties the map of properties to set + */ public void setProperties(Map properties) { this.properties = properties; } @@ -228,7 +273,6 @@ class Target { /** * Retrieves the target. - * * @return the target */ public String getTarget() { @@ -237,7 +281,6 @@ public String getTarget() { /** * Sets the target. - * * @param target the target */ public void setTarget(String target) { @@ -246,7 +289,6 @@ public void setTarget(String target) { /** * Retrieves the values. - * * @return the values */ public List getValues() { @@ -255,7 +297,6 @@ public List getValues() { /** * Sets the values. - * * @param values the values */ public void setValues(List values) { diff --git a/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java b/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java index 3f7d5554b..fb3cf98a6 100644 --- a/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java +++ b/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java @@ -23,7 +23,8 @@ import java.util.List; /** - * This service regroups all privacy-related operations + * Entry point for consent, anonymization, and privacy-related operations. + * Wraps profile updates required for GDPR-style requests and consent tracking. */ public interface PrivacyService { 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 78ecb1ffb..b2493530f 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 @@ -25,22 +25,26 @@ import java.util.*; /** - * A service to access and operate on {@link Profile}s, {@link Session}s and {@link Persona}s. + * Primary API for {@link Profile}s, {@link Session}s, and {@link Persona}s. + * Loads and saves visitor data, merges profiles, manages sessions, and + * supports persona-based testing workflows. */ public interface ProfileService { + /** + * The system tag name used to identify property types that store + * personal identifiers. + */ String PERSONAL_IDENTIFIER_TAG_NAME = "personalIdentifierProperties"; /** * Retrieves the number of unique profiles. - * * @return the number of unique profiles. */ long getAllProfilesCount(); /** * Retrieves profiles or personas matching the specified query. - * * @param the specific sub-type of {@link Profile} to retrieve * @param query a {@link Query} specifying which elements to retrieve * @param clazz the class of elements to retrieve @@ -50,7 +54,6 @@ public interface ProfileService { /** * Retrieves sessions matching the specified query. - * * @param query a {@link Query} specifying which elements to retrieve * @return a {@link PartialList} of sessions matching the specified query */ @@ -58,7 +61,6 @@ public interface ProfileService { /** * Creates a String containing comma-separated values (CSV) formatted version of profiles matching the specified query. - * * @param query the query specifying which profiles to export * @return a CSV-formatted String version of the profiles matching the specified query */ @@ -67,10 +69,8 @@ public interface ProfileService { /** * Find profiles which have the specified property with the specified value, ordered according to the specified {@code sortBy} String and paged: only * {@code size} of them are retrieved, starting with the {@code offset}-th one. - * * TODO: replace with version using a query instead of separate parameters * TODO: remove as it's unused? - * * @param propertyName the name of the property we're interested in * @param propertyValue the value of the property we want profiles to have * @param offset zero or a positive integer specifying the position of the first profile in the total ordered collection of matching profiles @@ -85,7 +85,6 @@ public interface ProfileService { /** * Merges the specified profiles into the provided so-called master profile, merging properties according to the {@link PropertyMergeStrategyType} specified on their {@link * PropertyType}. - * * @param masterProfile the profile into which the specified profiles will be merged * @param profilesToMerge the list of profiles to merge into the specified master profile * @return the merged profile @@ -94,7 +93,6 @@ public interface ProfileService { /** * Retrieves the profile identified by the specified identifier. - * * @param profileId the identifier of the profile to retrieve * @return the profile identified by the specified identifier or {@code null} if no such profile exists */ @@ -102,7 +100,6 @@ public interface ProfileService { /** * Saves the specified profile in the context server. - * * @param profile the profile to be saved * @return the newly saved profile */ @@ -110,7 +107,6 @@ public interface ProfileService { /** * Adds the alias to the profile. - * * @param profileID the identifier of the profile * @param alias the alias which should be linked with of the profile * @param clientID the identifier of the client @@ -119,7 +115,6 @@ public interface ProfileService { /** * Removes the alias from the profile. - * * @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 @@ -130,7 +125,6 @@ public interface ProfileService { /** * Find profile aliases which have the specified property with the specified value, ordered according to the specified {@code sortBy} String and paged: only * {@code size} of them are retrieved, starting with the {@code offset}-th one. - * * @param profileId the identifier of the profile * @param offset zero or a positive integer specifying the position of the first profile in the total ordered collection of matching profiles * @param size a positive integer specifying how many matching profiles should be retrieved or {@code -1} if all of them should be retrieved @@ -143,7 +137,6 @@ public interface ProfileService { /** * Merge the specified profile properties in an existing profile,or save new profile if it does not exist yet - * * @param profile the profile to be saved * @return the newly saved or merged profile or null if the save or merge operation failed. */ @@ -151,7 +144,6 @@ public interface ProfileService { /** * Removes the profile (or persona if the {@code persona} parameter is set to {@code true}) identified by the specified identifier. - * * @param profileId the identifier of the profile or persona to delete * @param persona {@code true} if the specified identifier is supposed to refer to a persona, {@code false} if it is supposed to refer to a profile */ @@ -160,9 +152,7 @@ public interface ProfileService { /** * Retrieves the sessions associated with the profile identified by the specified identifier that match the specified query (if specified), ordered according to the specified * {@code sortBy} String and and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. - * * TODO: use a Query object instead of distinct parameter - * * @param profileId the identifier of the profile we want to retrieve sessions from * @param query a String of text used for fulltext filtering which sessions we are interested in or {@code null} (or an empty String) if we want to retrieve all sessions * @param offset zero or a positive integer specifying the position of the first session in the total ordered collection of matching sessions @@ -177,7 +167,6 @@ public interface ProfileService { /** * Retrieves the session identified by the specified identifier. * @deprecated {@code dateHint} is not supported anymore, please use {@link #loadSession(String)} - * * @param sessionId the identifier of the session to be retrieved * @param dateHint a Date helping in identifying where the item is located * @return the session identified by the specified identifier @@ -187,7 +176,6 @@ public interface ProfileService { /** * Retrieves the session identified by the specified identifier. - * * @param sessionId the identifier of the session to be retrieved * @return the session identified by the specified identifier */ @@ -197,7 +185,6 @@ default Session loadSession(String sessionId) { /** * Saves the specified session. - * * @param session the session to be saved * @return the newly saved session */ @@ -205,7 +192,6 @@ default Session loadSession(String sessionId) { /** * Retrieves sessions associated with the profile identified by the specified identifier. - * * @param profileId the profile id for which we want to retrieve the sessions * @return a {@link PartialList} of the profile's sessions */ @@ -213,7 +199,6 @@ default Session loadSession(String sessionId) { /** * Removes all sessions of the specified profile - * * @param profileId identifier of the profile that we want to remove it's sessions */ void removeProfileSessions(String profileId); @@ -222,14 +207,12 @@ default Session loadSession(String sessionId) { * Deletes the session identified by the given identifier from persistence. * Note: events belonging to this session are NOT removed; they remain in persistence * with a dangling sessionId reference. - * * @param sessionIdentifier the unique identifier for the session */ void deleteSession(String sessionIdentifier); /** * Checks whether the specified profile and/or session satisfy the specified condition. - * * @param condition the condition we're testing against which might or might not have profile- or session-specific sub-conditions * @param profile the profile we're testing * @param session the session we're testing @@ -239,14 +222,12 @@ default Session loadSession(String sessionId) { /** * Update all profiles in batch according to the specified {@link BatchUpdate} - * * @param update the batch update specification */ void batchProfilesUpdate(BatchUpdate update); /** * Retrieves the persona identified by the specified identifier. - * * @param personaId the identifier of the persona to retrieve * @return the persona associated with the specified identifier or {@code null} if no such persona exists. */ @@ -254,7 +235,6 @@ default Session loadSession(String sessionId) { /** * Persists the specified {@link Persona} in the context server. - * * @param persona the persona to persist * @return the newly persisted persona */ @@ -262,7 +242,6 @@ default Session loadSession(String sessionId) { /** * Retrieves the persona identified by the specified identifier and all its associated sessions - * * @param personaId the identifier of the persona to retrieve * @return a {@link PersonaWithSessions} instance with the persona identified by the specified identifier and all its associated sessions */ @@ -270,7 +249,6 @@ default Session loadSession(String sessionId) { /** * Creates a persona with the specified identifier and automatically creates an associated session with it. - * * @param personaId the identifier to use for the new persona * @return the newly created persona */ @@ -279,7 +257,6 @@ default Session loadSession(String sessionId) { /** * Retrieves the sessions associated with the persona identified by the specified identifier, ordered according to the specified {@code sortBy} String and and paged: only * {@code size} of them are retrieved, starting with the {@code offset}-th one. - * * @param personaId the persona id * @param offset zero or a positive integer specifying the position of the first session in the total ordered collection of matching sessions * @param size a positive integer specifying how many matching sessions should be retrieved or {@code -1} if all of them should be retrieved @@ -292,7 +269,6 @@ default Session loadSession(String sessionId) { /** * Save a persona with its sessions. - * * @param personaToSave the persona object containing all the persona information and sessions * @return the persona with sessions */ @@ -301,9 +277,7 @@ default Session loadSession(String sessionId) { /** * Retrieves all the property types associated with the specified target. - * * TODO: move to a different class - * * @param target the target for which we want to retrieve the associated property types * @return a collection of all the property types associated with the specified target */ @@ -311,19 +285,15 @@ default Session loadSession(String sessionId) { /** * Retrieves all known property types. - * * TODO: move to a different class * TODO: use Map instead of HashMap - * * @return a Map associating targets as keys to related {@link PropertyType}s */ Map> getTargetPropertyTypes(); /** * Retrieves all property types with the specified tag - * * TODO: move to a different class - * * @param tag the tag name marking property types we want to retrieve * @return a Set of the property types with the specified tag */ @@ -331,9 +301,7 @@ default Session loadSession(String sessionId) { /** * Retrieves all property types with the specified system tag - * * TODO: move to a different class - * * @param tag the system tag name marking property types we want to retrieve * @return a Set of the property types with the specified system tag */ @@ -355,9 +323,7 @@ default Session loadSession(String sessionId) { /** * Retrieves the property type identified by the specified identifier. - * * TODO: move to a different class - * * @param id the identifier of the property type to retrieve * @return the property type identified by the specified identifier or {@code null} if no such property type exists */ @@ -365,9 +331,7 @@ default Session loadSession(String sessionId) { /** * Persists the specified property type in the context server. - * * TODO: move to a different class - * * @param property the property type to persist * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed */ @@ -375,7 +339,6 @@ default Session loadSession(String sessionId) { /** * This function will try to set the target on the property type if not set already, based on the file URL - * * @param predefinedPropertyTypeURL the URL to extract the target from if the target is not yet. By default it will * use the 5's part after a "/" character * @param propertyType the property type to register @@ -384,9 +347,7 @@ default Session loadSession(String sessionId) { /** * Deletes the property type identified by the specified identifier. - * * TODO: move to a different class - * * @param propertyId the identifier of the property type to delete * @return {@code true} if the property type was properly deleted, {@code false} otherwise */ @@ -394,9 +355,7 @@ default Session loadSession(String sessionId) { /** * Retrieves the existing property types for the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and with the specified tag. - * * TODO: move to a different class - * * @param tag the tag we're interested in * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field * @return all property types defined for the specified item type and with the specified tag @@ -406,9 +365,7 @@ default Session loadSession(String sessionId) { /** * Retrieves the existing property types for the specified type as defined by the Item subclass public * field {@code ITEM_TYPE} and with the specified tag (system or regular) - * * TODO: move to a different class - * * @param tag the tag we're interested in * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field * @param systemTag whether the specified is a system tag or a regular one @@ -429,7 +386,6 @@ default Session loadSession(String sessionId) { * purgeProfiles(10, 0); * example: Purge profile created since 30 days only: * purgeProfiles(0, 30); - * * @param inactiveNumberOfDays will purge profiles with no visits since this number of days (0 or negative value, will have no effect) * @param existsNumberOfDays will purge profiles created since this number of days (0 or negative value, will have no effect) */ diff --git a/api/src/main/java/org/apache/unomi/api/services/QueryService.java b/api/src/main/java/org/apache/unomi/api/services/QueryService.java index c397fda75..657c8ab4e 100644 --- a/api/src/main/java/org/apache/unomi/api/services/QueryService.java +++ b/api/src/main/java/org/apache/unomi/api/services/QueryService.java @@ -24,7 +24,8 @@ import java.util.Map; /** - * A service to perform queries. + * Runs stored queries and aggregations against the persistence layer. + * Complements segment search with lower-level query and aggregate access. */ public interface QueryService { diff --git a/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java b/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java index f65990403..e6555a2ed 100644 --- a/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java +++ b/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java @@ -21,7 +21,9 @@ import org.apache.unomi.api.rules.Rule; /** - * A service that gets called when a rule's conditions are evaluated or when a rule's actions are executed. + * Observer notified when rules are evaluated or executed. + * Lets extensions audit rule firing or add diagnostics without changing + * core rule processing. */ public interface RuleListenerService { @@ -29,8 +31,11 @@ public interface RuleListenerService { * This enum indicates which type of already raised event we are dealing with */ enum AlreadyRaisedFor { + /** Rule was already raised for the session. */ SESSION, + /** Rule was already raised for the profile. */ PROFILE, + /** Rule was already raised for the event. */ EVENT } diff --git a/api/src/main/java/org/apache/unomi/api/services/RulesService.java b/api/src/main/java/org/apache/unomi/api/services/RulesService.java index dd5a44786..b55e09115 100644 --- a/api/src/main/java/org/apache/unomi/api/services/RulesService.java +++ b/api/src/main/java/org/apache/unomi/api/services/RulesService.java @@ -31,7 +31,9 @@ import java.util.Set; /** - * A service to access and operate on {@link Rule}s. + * Loads, saves, and searches {@link org.apache.unomi.api.rules.Rule} definitions. + * Rules tie conditions to actions and drive most automated behavior when + * events arrive. */ public interface RulesService { 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 6868ebc65..fe97d3dfc 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 @@ -280,6 +280,13 @@ ScheduledTask createTask(String taskType, */ Map getAllMetrics(); + /** + * Gets a list of scheduled tasks matching the specified status. + * This method allows filtering all managed tasks (both persistent and + * in-memory) by their current operational state, such as RUNNING or FAILED. + * @param taskStatus the desired {@link ScheduledTask.TaskStatus} to filter by + * @return tasks that currently match the given status + */ List findTasksByStatus(ScheduledTask.TaskStatus taskStatus); /** @@ -291,6 +298,7 @@ interface TaskBuilder { /** * Sets task parameters. * @param parameters task-specific parameters + * @return this builder for method chaining */ TaskBuilder withParameters(Map parameters); @@ -298,6 +306,7 @@ interface TaskBuilder { * Sets initial execution delay. * @param initialDelay delay before first execution * @param timeUnit time unit for delay + * @return this builder for method chaining */ TaskBuilder withInitialDelay(long initialDelay, TimeUnit timeUnit); @@ -305,54 +314,63 @@ interface TaskBuilder { * Sets execution period. * @param period time between executions * @param timeUnit time unit for period + * @return this builder for method chaining */ TaskBuilder withPeriod(long period, TimeUnit timeUnit); /** * Uses fixed delay scheduling. * Period is measured from completion of one execution to start of next. + * @return this builder for method chaining */ TaskBuilder withFixedDelay(); /** * Uses fixed rate scheduling. * Period is measured from start of one execution to start of next. + * @return this builder for method chaining */ TaskBuilder withFixedRate(); /** * Makes this a one-shot task. * Task will execute once and then be disabled. + * @return this builder for method chaining */ TaskBuilder asOneShot(); /** * Disallows parallel execution. * Task will use locking to ensure only one instance runs at a time. + * @return this builder for method chaining */ TaskBuilder disallowParallelExecution(); /** * Sets the task executor. * @param executor the executor to handle this task + * @return this builder for method chaining */ TaskBuilder withExecutor(TaskExecutor executor); /** * Sets a simple runnable as the executor. * @param runnable the code to execute + * @return this builder for method chaining */ TaskBuilder withSimpleExecutor(Runnable runnable); /** * Makes this a non-persistent task. * Task will only exist in memory on this node. + * @return this builder for method chaining */ TaskBuilder nonPersistent(); /** * Runs the task on all nodes in the cluster rather than just executor nodes. * This is helpful for distributed cache refreshes or local data maintenance. + * @return this builder for method chaining */ TaskBuilder runOnAllNodes(); @@ -381,6 +399,7 @@ interface TaskBuilder { * * @param maxRetries maximum number of retries (must be >= 0) * @throws IllegalArgumentException if maxRetries is negative + * @return this builder for method chaining */ TaskBuilder withMaxRetries(int maxRetries); @@ -397,6 +416,7 @@ interface TaskBuilder { * @param delay delay duration (must be >= 0) * @param unit time unit for delay * @throws IllegalArgumentException if delay is negative + * @return this builder for method chaining */ TaskBuilder withRetryDelay(long delay, TimeUnit unit); @@ -404,6 +424,7 @@ interface TaskBuilder { * Sets the task dependencies. * The task will not execute until all dependencies have completed. * @param taskIds IDs of tasks this task depends on + * @return this builder for method chaining */ TaskBuilder withDependencies(String... taskIds); diff --git a/api/src/main/java/org/apache/unomi/api/services/ScopeService.java b/api/src/main/java/org/apache/unomi/api/services/ScopeService.java index 91980a407..d9c223697 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ScopeService.java +++ b/api/src/main/java/org/apache/unomi/api/services/ScopeService.java @@ -22,7 +22,9 @@ import java.util.List; /** - * A service to create, update and delete scope. + * Manages {@link Scope} definitions. + * Creates, updates, and deletes scopes that partition data between sites + * or applications. */ public interface ScopeService { @@ -51,6 +53,7 @@ public interface ScopeService { /** * Get a scope by its id * + * @param id scope identifier * @return Scope matching the id */ Scope getScope(String id); diff --git a/api/src/main/java/org/apache/unomi/api/services/SegmentService.java b/api/src/main/java/org/apache/unomi/api/services/SegmentService.java index 601cd5633..935dfa1ce 100644 --- a/api/src/main/java/org/apache/unomi/api/services/SegmentService.java +++ b/api/src/main/java/org/apache/unomi/api/services/SegmentService.java @@ -31,7 +31,9 @@ import java.util.List; /** - * A service to access and operate on {@link Segment}s and {@link Scoring}s + * Manages {@link org.apache.unomi.api.segments.Segment}s and + * {@link org.apache.unomi.api.segments.Scoring} definitions. + * Also recalculates segment membership for profiles when definitions change. */ public interface SegmentService { diff --git a/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java b/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java index 5aab7ad78..d5653d9ab 100644 --- a/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java +++ b/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java @@ -17,7 +17,8 @@ package org.apache.unomi.api.services; /** - * Interface for services that need to be notified of tenant lifecycle events. + * Extension point notified when tenants are created, updated, or removed. + * Implementations run setup or teardown logic for multi-tenant deployments. */ public interface TenantLifecycleListener { /** diff --git a/api/src/main/java/org/apache/unomi/api/services/TopicService.java b/api/src/main/java/org/apache/unomi/api/services/TopicService.java index 6cb5e0b38..f8344ae39 100644 --- a/api/src/main/java/org/apache/unomi/api/services/TopicService.java +++ b/api/src/main/java/org/apache/unomi/api/services/TopicService.java @@ -20,11 +20,15 @@ import org.apache.unomi.api.Topic; import org.apache.unomi.api.query.Query; +/** + * Persistence API for {@link Topic} items. + * Supports load, save, search, and delete of topics used to categorize + * entities across the context server. + */ public interface TopicService { /** * Retrieves the topic identified by the specified identifier. - * * @param topicId the identifier of the topic to retrieve * @return the topic identified by the specified identifier or {@code null} if no such topic exists */ @@ -32,7 +36,6 @@ public interface TopicService { /** * Saves the specified topic in the context server. - * * @param topic the topic to be saved * @return the newly saved topic if the creation or update was successful, {@code null} otherwise */ @@ -40,7 +43,6 @@ public interface TopicService { /** * Retrieves topic matching the specified query. - * * @param query a {@link Query} specifying which elements to retrieve * @return a {@link PartialList} of {@link Topic} metadata */ @@ -48,7 +50,6 @@ public interface TopicService { /** * Removes the topic identified by the specified identifier. - * * @param topicId the identifier of the profile or persona to delete * @return {@code true} if the deletion was successful, {@code false} otherwise */ diff --git a/api/src/main/java/org/apache/unomi/api/services/UserListService.java b/api/src/main/java/org/apache/unomi/api/services/UserListService.java index 04d109ab7..7612bde0a 100644 --- a/api/src/main/java/org/apache/unomi/api/services/UserListService.java +++ b/api/src/main/java/org/apache/unomi/api/services/UserListService.java @@ -24,7 +24,8 @@ import java.util.List; /** - * Created by amidani on 24/03/2017. + * CRUD and membership operations for {@link UserList} instances. + * Maintains static audience lists referenced by campaigns and exports. */ public interface UserListService { /** diff --git a/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java b/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java index ec6a3e255..37c92930a 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java +++ b/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java @@ -17,7 +17,8 @@ package org.apache.unomi.api.services; /** - * A service interface for validating values against specific types. + * Validates a value against a {@link org.apache.unomi.api.ValueType} definition. + * Called when saving properties or condition parameters to enforce schema rules. */ public interface ValueTypeValidator { diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java index 69d48e0af..0419bbe59 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java @@ -28,9 +28,22 @@ public class ApiKeyCreationResult { private ApiKey apiKey; private String plainTextKey; + /** + * Constructs an empty {@link ApiKeyCreationResult}. + * This result object must be populated manually with the created API key + * metadata and the plaintext key value. + */ public ApiKeyCreationResult() { } + /** + * Creates a result containing both the persisted {@link ApiKey} metadata + * and the plaintext key value. + * The plaintext key is only available at creation time, as + * it is not persisted. + * @param apiKey the API key metadata that was successfully persisted. + * @param plainTextKey the one-time plaintext key value. + */ public ApiKeyCreationResult(ApiKey apiKey, String plainTextKey) { this.apiKey = apiKey; this.plainTextKey = plainTextKey; diff --git a/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java b/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java index 6fb6460af..758777d8b 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java @@ -22,7 +22,8 @@ import java.util.List; /** - * Combined service interface for both item and tenant auditing operations. + * Combined facade for item-level and tenant-level audit trails. + * Delegates to {@link ItemAuditService} and {@link TenantAuditService}. */ public interface AuditService extends ItemAuditService, TenantAuditService { /** @@ -85,6 +86,12 @@ public interface AuditService extends ItemAuditService, TenantAuditService { */ Date getLastSyncDate(String tenantId, String sourceInstanceId); + /** + * Updates last-modified audit fields on an item. + * + * @param item the item to update + * @param userId the user performing the modification + */ default void updateModificationMetadata(Item item, String userId) { item.setLastModifiedBy(userId); item.setLastModificationDate(new Date()); diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java b/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java index 77b5bb2c2..bccaf5a13 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java @@ -22,7 +22,8 @@ import java.util.List; /** - * A service to track and audit changes to items. + * Records create, update, and delete operations on configuration items. + * Provides an audit trail for who changed segments, rules, and similar entities. */ public interface ItemAuditService { /** diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java index 261a36e23..ab3105a0f 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java @@ -17,7 +17,8 @@ package org.apache.unomi.api.tenants; /** - * A service to audit tenant-related operations. + * Records administrative actions performed in a tenant context. + * Complements item audit with tenant-scoped security and operations history. */ public interface TenantAuditService { /** diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java index 4cae6cfba..fe76b6e57 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java @@ -16,22 +16,48 @@ */ package org.apache.unomi.api.tenants; +/** + * Represents metadata used to track the state or existence of a backup + * for a specific tenant. + * This class encapsulates essential information linking a backup record + * to its owner and time. + * The {@link TenantBackupMetadata} holds two primary pieces of data: the unique + * identifier of the tenant, stored in {@code tenantId}, and a timestamp + * indicating when this metadata was generated or pertains to, stored + * in {@code timestamp}. + */ public class TenantBackupMetadata { private String tenantId; private long timestamp; + /** + * Retrieves the tenant ID associated with this metadata object. + * @return The stored {@link String} tenant ID. + */ public String getTenantId() { return tenantId; } + /** + * Sets the tenant ID for this metadata object. + * @param tenantId The new {@link String} tenant ID to set. + */ public void setTenantId(String tenantId) { this.tenantId = tenantId; } + /** + * Retrieves the timestamp associated with this metadata object. + * @return The stored {@link Long} timestamp. + */ public long getTimestamp() { return timestamp; } + /** + * Sets the timestamp for this metadata object. + * @param timestamp The new {@link Long} timestamp to set. + */ public void setTimestamp(long timestamp) { this.timestamp = timestamp; } diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java index 7199a64b1..1c070aa76 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java @@ -17,7 +17,9 @@ package org.apache.unomi.api.tenants; /** - * Result of a tenant-scoped event retention purge request. + * Outcome of a request to delete old events for one tenant. + * Reports how many events matched the retention cutoff and whether the + * delete-by-query completed successfully. */ public class TenantEventPurgeResult { @@ -34,42 +36,94 @@ public class TenantEventPurgeResult { private boolean purgeRequested; private long requestedAt; + /** + * Retrieves the unique identifier of the tenant + * associated with this result. + * @return The tenant ID string. + */ public String getTenantId() { return tenantId; } + /** + * Sets the unique identifier of the tenant associated with this result. + * @param tenantId The tenant ID to set. + */ public void setTenantId(String tenantId) { this.tenantId = tenantId; } + /** + * Retrieves the number of days events must be retained before + * purge consideration. + * @return The retention period in days. + */ public int getRetentionDays() { return retentionDays; } + /** + * Sets the number of days events must be retained before + * purge consideration. + * @param retentionDays The retention period in days. + */ public void setRetentionDays(int retentionDays) { this.retentionDays = retentionDays; } + /** + * Retrieves the estimated number of events that matched the retention + * cutoff before the delete-by-query was submitted; not a post-delete count. + * @return The count of matching events. + */ public long getEventsMatched() { return eventsMatched; } + /** + * Sets the estimated number of events that matched the retention cutoff + * before the delete-by-query was submitted; not a post-delete count. + * @param eventsMatched The estimated count of matching events. + */ public void setEventsMatched(long eventsMatched) { this.eventsMatched = eventsMatched; } + /** + * Checks whether the delete-by-query for event purging completed + * successfully. + * + * @return {@code true} if the delete-by-query completed successfully, + * {@code false} if the persistence layer reported a failure + */ public boolean isPurgeRequested() { return purgeRequested; } + /** + * Sets whether the delete-by-query for event purging completed + * successfully. + * + * @param purgeRequested {@code true} if the delete-by-query completed + * successfully, {@code false} otherwise + */ public void setPurgeRequested(boolean purgeRequested) { this.purgeRequested = purgeRequested; } + /** + * Gets the timestamp (in milliseconds) when this event retention purge + * request was initiated. + * @return The requested time in milliseconds since the epoch. + */ public long getRequestedAt() { return requestedAt; } + /** + * Sets the timestamp when the event retention purge request was initiated. + * @param requestedAt the time in milliseconds since the epoch + */ public void setRequestedAt(long requestedAt) { this.requestedAt = requestedAt; } diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java index 3029b1fb4..775b7be28 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java @@ -17,7 +17,9 @@ package org.apache.unomi.api.tenants; /** - * Segment and rule counts for one scope within a tenant. + * Usage counters for a single scope inside a tenant usage snapshot. + * Tracks how many segments and rules exist in that scope when + * {@link TenantUsageService} collects statistics. */ public class TenantScopeUsage { @@ -25,26 +27,50 @@ public class TenantScopeUsage { private long segmentCount; private long ruleCount; + /** + * Retrieves the unique identifier of the scope. + * @return The scope ID as a {@link String}. + */ public String getScopeId() { return scopeId; } + /** + * Sets the unique identifier of the scope. + * @param scopeId The new scope ID to set. + */ public void setScopeId(String scopeId) { this.scopeId = scopeId; } + /** + * Retrieves the count of segments associated with this scope usage. + * @return The segment count as a {@link Long}. + */ public long getSegmentCount() { return segmentCount; } + /** + * Sets the count of segments associated with this scope usage. + * @param segmentCount The new segment count to set. + */ public void setSegmentCount(long segmentCount) { this.segmentCount = segmentCount; } + /** + * Retrieves the count of rules associated with this scope usage. + * @return The rule count as a {@link Long}. + */ public long getRuleCount() { return ruleCount; } + /** + * Sets the count of rules associated with this scope usage. + * @param ruleCount The new rule count to set. + */ public void setRuleCount(long ruleCount) { this.ruleCount = ruleCount; } diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java index aad239918..021f71b6e 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java @@ -17,8 +17,8 @@ package org.apache.unomi.api.tenants; /** - * Enumeration of possible tenant statuses. - * This enum defines the various states a tenant can be in within the system. + * Lifecycle state of a tenant (for example active, suspended, or pending). + * Administrators filter and manage tenants based on these enum values. */ public enum TenantStatus { /** diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java index 5b78ac10d..91414ce90 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java @@ -23,7 +23,6 @@ /** * Read-only usage snapshot for a tenant. Values are refreshed on a background schedule * (see {@link TenantUsageService}) and may be stale until the next collection cycle. - * *

Profile, scope, segment, and rule counts are point-in-time totals. {@link #eventCount} * counts events whose {@code timeStamp} falls within {@link #periodStart} (inclusive) and * {@link #periodEnd} (exclusive).

@@ -53,116 +52,249 @@ public class TenantUsage { private List scopeUsages = new ArrayList<>(); private long collectedAt; + /** + * Retrieves the unique identifier for the tenant associated with + * this usage snapshot. + * @return The tenant ID string. + */ public String getTenantId() { return tenantId; } + /** + * Sets the unique identifier for the tenant associated with + * this usage snapshot. + * @param tenantId The ID of the tenant. + */ public void setTenantId(String tenantId) { this.tenantId = tenantId; } + /** + * Returns the normalized period label, e.g., {@code 2026-07} + * for a calendar month. + * @return The period label string. + */ public String getPeriod() { return period; } + /** + * Sets the normalized period label, e.g., {@code 2026-07} + * for a calendar month. + * @param period The desired period label. + */ public void setPeriod(String period) { this.period = period; } + /** + * Returns the inclusive start of the reporting period (epoch millis, UTC). + * @return The starting epoch millisecond value. + */ public long getPeriodStart() { return periodStart; } + /** + * Sets the inclusive start of the reporting period (epoch millis, UTC). + * @param periodStart The starting epoch millisecond value. + */ public void setPeriodStart(long periodStart) { this.periodStart = periodStart; } + /** + * Returns the exclusive end of the reporting period (epoch millis, UTC). + * @return The ending epoch millisecond value. + */ public long getPeriodEnd() { return periodEnd; } + /** + * Sets the exclusive end of the reporting period (epoch millis, UTC). + * @param periodEnd The ending epoch millisecond value. + */ public void setPeriodEnd(long periodEnd) { this.periodEnd = periodEnd; } + /** + * Retrieves the number of profiles associated with this + * tenant usage snapshot. + * @return The profile count. + */ public long getProfileCount() { return profileCount; } + /** + * Sets the number of profiles for this tenant usage snapshot. + * @param profileCount The total count of profiles. + */ public void setProfileCount(long profileCount) { this.profileCount = profileCount; } + /** + * Returns the number of events counted for this tenant in the reporting + * period. Events are included when their {@code timeStamp} falls within + * {@link #getPeriodStart()} (inclusive) and {@link #getPeriodEnd()} + * (exclusive). + * + * @return the event count for the reporting period + */ public long getEventCount() { return eventCount; } + /** + * Sets the count of events for this tenant usage snapshot. + * @param eventCount The total number of events. + */ public void setEventCount(long eventCount) { this.eventCount = eventCount; } + /** + * Retrieves the count of tenant scopes excluding + * {@code systemscope} for this period. + * @return The scope count. + */ public long getScopeCount() { return scopeCount; } + /** + * Sets the count of tenant scopes excluding {@code systemscope}. + * @param scopeCount The total number of scopes. + */ public void setScopeCount(long scopeCount) { this.scopeCount = scopeCount; } + /** + * Retrieves the count of segments associated with this + * tenant usage snapshot. + * @return The segment count. + */ public long getSegmentCount() { return segmentCount; } + /** + * Sets the count of segments for this tenant usage snapshot. + * @param segmentCount The total number of segments. + */ public void setSegmentCount(long segmentCount) { this.segmentCount = segmentCount; } + /** + * Retrieves the count of rules associated with this tenant usage snapshot. + * @return The number of rules. + */ public long getRuleCount() { return ruleCount; } + /** + * Sets the count of rules associated with this tenant usage snapshot. + * @param ruleCount The total number of rules. + */ public void setRuleCount(long ruleCount) { this.ruleCount = ruleCount; } + /** + * Retrieves the document count across all tenant indices + * recorded in this snapshot. + * @return The total document count. + */ public long getStorageDocumentCount() { return storageDocumentCount; } + /** + * Sets the document count across all tenant indices + * recorded in this snapshot. + * @param storageDocumentCount The total number of documents. + */ public void setStorageDocumentCount(long storageDocumentCount) { this.storageDocumentCount = storageDocumentCount; } + /** + * Retrieves the count of active API keys on the tenant record + * for this usage period. + * @return The number of active API keys. + */ public long getActiveApiKeyCount() { return activeApiKeyCount; } + /** + * Sets the count of active API keys on the tenant record + * for this usage period. + * @param activeApiKeyCount The total number of active API keys. + */ public void setActiveApiKeyCount(long activeApiKeyCount) { this.activeApiKeyCount = activeApiKeyCount; } + /** + * Retrieves the in-memory REST request counter for this tenant since the + * Unomi process started. + * @return The count of REST requests. + */ public long getRestRequestCount() { return restRequestCount; } + /** + * Sets the in-memory REST request counter for this tenant since the + * Unomi process started. + * @param restRequestCount The total number of REST requests recorded. + */ public void setRestRequestCount(long restRequestCount) { this.restRequestCount = restRequestCount; } + /** + * Retrieves an unmodifiable list of usage details for all tenant scopes. + * @return An unmodifiable {@link List} containing + * scope usage snapshots. + */ public List getScopeUsages() { return Collections.unmodifiableList(scopeUsages); } + /** + * Sets the list of usage details for all tenant scopes. This method + * performs a defensive copy of the provided list to ensure + * internal state immutability. + * @param scopeUsages The list of {@link TenantScopeUsage} objects. + */ public void setScopeUsages(List scopeUsages) { // Copy defensively: callers (including the internal usage cache) must not be able to // mutate this snapshot's state through a shared list reference after construction. this.scopeUsages = scopeUsages != null ? new ArrayList<>(scopeUsages) : new ArrayList<>(); } + /** + * Gets the timestamp (epoch millis) when this usage snapshot was collected. + * @return The collection time in epoch milliseconds. + */ public long getCollectedAt() { return collectedAt; } + /** + * Sets the timestamp (epoch millis) indicating when this usage + * snapshot was collected. + * @param collectedAt The collection time in epoch milliseconds. + */ public void setCollectedAt(long collectedAt) { this.collectedAt = collectedAt; } diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java index 789584683..5ca4850a9 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java @@ -43,6 +43,7 @@ public interface TenantUsageService { /** * Records one authenticated REST request for the tenant (in-memory counter). + * @param tenantId tenant identifier */ void recordRestRequest(String tenantId); diff --git a/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java b/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java index eb1215bfc..3c4166531 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java @@ -140,50 +140,99 @@ public static class SecurityEvent { private String ipAddress; private Map details; + /** + * Retrieves the type identifier of this security event. + * @return The string representing the event type. + */ public String getType() { return type; } + /** + * Sets the type identifier for this security event. + * @param type The type string to set. + */ public void setType(String type) { this.type = type; } + /** + * Retrieves the timestamp when the security event occurred. + * @return The {@link java.util.Date} representing the event time. + */ public Date getTimestamp() { return timestamp; } + /** + * Sets the timestamp for this security event. + * @param timestamp The {@link java.util.Date} to set as + * the event timestamp. + */ public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } + /** + * Retrieves a detailed description of the security event. + * @return A string containing the event's description. + */ public String getDescription() { return description; } + /** + * Sets the descriptive text for this security event. + * @param description The description string to set. + */ public void setDescription(String description) { this.description = description; } + /** + * Retrieves the user ID associated with this security event. + * @return The unique identifier of the user involved in the event. + */ public String getUserId() { return userId; } + /** + * Sets the user ID associated with this security event. + * @param userId The user ID to set. + */ public void setUserId(String userId) { this.userId = userId; } + /** + * Gets the IP address associated with this security event. + * @return The stored {@link String} IP address. + */ public String getIpAddress() { return ipAddress; } + /** + * Sets the IP address associated with this security event. + * @param ipAddress the IP address to set. + */ public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } + /** + * Gets a map containing additional details about the security event. + * @return The {@link java.util.Map} of details. + */ public Map getDetails() { return details; } + /** + * Sets a map containing additional details about the security event. + * @param details the map of details to set. + */ public void setDetails(Map details) { this.details = details; } diff --git a/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java b/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java index ade4913db..0667d2de5 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java @@ -103,34 +103,72 @@ public static class AuthenticationConfig { private int lockoutDurationMinutes; private boolean requireMfa; + /** + * Gets the list of allowed authentication methods. + * @return A {@link java.util.List} of {@link String} representing the + * allowed auth methods. + */ public List getAllowedAuthMethods() { return allowedAuthMethods; } + /** + * Sets the list of allowed authentication methods for the tenant. + * @param allowedAuthMethods The list of allowed authentication methods. + */ public void setAllowedAuthMethods(List allowedAuthMethods) { this.allowedAuthMethods = allowedAuthMethods; } + /** + * Retrieves the maximum number of login attempts + * permitted before lockout. + * @return The maximum number of login attempts. + */ public int getMaxLoginAttempts() { return maxLoginAttempts; } + /** + * Sets the maximum number of consecutive failed login attempts allowed. + * @param maxLoginAttempts The maximum number of login attempts. + */ public void setMaxLoginAttempts(int maxLoginAttempts) { this.maxLoginAttempts = maxLoginAttempts; } + /** + * Gets the duration, in minutes, that an account remains locked after + * exceeding login attempts. + * @return The lockout duration in minutes. + */ public int getLockoutDurationMinutes() { return lockoutDurationMinutes; } + /** + * Sets the duration (in minutes) for which a user account will be + * locked out upon failed logins. + * @param lockoutDurationMinutes The desired lockout + * duration in minutes. + */ public void setLockoutDurationMinutes(int lockoutDurationMinutes) { this.lockoutDurationMinutes = lockoutDurationMinutes; } + /** + * Checks if multi-factor authentication is required for login. + * @return {@code true} if MFA is required, {@code false} otherwise. + */ public boolean isRequireMfa() { return requireMfa; } + /** + * Sets whether multi-factor authentication must be used during login. + * @param requireMfa If {@code true}, MFA is mandatory; + * otherwise, it is optional. + */ public void setRequireMfa(boolean requireMfa) { this.requireMfa = requireMfa; } @@ -144,26 +182,54 @@ public static class AuthorizationConfig { private List permissions; private Map> rolePermissions; + /** + * Retrieves the list of roles configured for this tenant. + * @return A {@link java.util.List} of strings + * representing the assigned roles. + */ public List getRoles() { return roles; } + /** + * Sets the list of roles for this tenant. + * @param roles The list of role names to set. + */ public void setRoles(List roles) { this.roles = roles; } + /** + * Retrieves the list of permissions configured for this tenant. + * @return A {@link java.util.List} of strings representing the + * assigned permissions. + */ public List getPermissions() { return permissions; } + /** + * Sets the list of permissions for this tenant. + * @param permissions The list of permission names to set. + */ public void setPermissions(List permissions) { this.permissions = permissions; } + /** + * Retrieves the mapping of roles to their associated permissions. + * @return A {@link java.util.Map} where keys are role names and values + * are lists of permissions. + */ public Map> getRolePermissions() { return rolePermissions; } + /** + * Sets the mapping defining which permissions belong to specific roles. + * @param rolePermissions The map containing + * role-to-permission mappings to set. + */ public void setRolePermissions(Map> rolePermissions) { this.rolePermissions = rolePermissions; } diff --git a/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java b/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java index 0ab6f08d6..f7fe61f37 100644 --- a/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java +++ b/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java @@ -37,7 +37,9 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Helper class to resolve condition, action and values types when loading definitions from JSON files + * Utilities for resolving condition, action, and value type references when + * importing JSON definitions. Walks condition trees, fills in missing type + * metadata, and extracts event types referenced by conditions. */ public class ParserHelper { @@ -54,15 +56,62 @@ public class ParserHelper { private static final int MAX_RECURSION_DEPTH = 1000; + /** + * A visitor implementation used by {@link ParserHelper} to traverse and + * process {@link Condition} nodes found within parsed + * definitions (e.g., from JSON). + * Subclasses must implement the required methods to define how conditions + * are visited upon entry and exited upon completion of processing. + */ public interface ConditionVisitor { + /** + * Visits a given {@link Condition} object, executing necessary logic + * for that specific condition during traversal. + * @param condition The condition being visited. + */ void visit(Condition condition); + /** + * Executes post-visit logic for the specified {@link Condition} object. + * This method is called after all descendants of the given condition + * have been processed. + * @param condition The condition whose children have + * finished processing. + */ void postVisit(Condition condition); } + /** + * Utility class responsible for extracting and converting raw string values + * found in configuration definitions (such as conditions or actions) into + * usable Java objects. + * This abstract helper requires subclasses to implement the core logic of + * value extraction, utilizing both the {@code String} representation of the + * value and the associated {@link Event} context for resolution. + */ public interface ValueExtractor { + /** + * Extracts an object value from a given string representation using the + * provided event context. + * This method is abstract and must be implemented by + * concrete subclasses. + * @param valueAsString The string value that needs to be + * extracted or parsed. + * @param event The {@link Event} providing context for + * the extraction process. + * @return An object representing the extracted value. + * @throws IllegalAccessException if access to a required member fails. + * @throws NoSuchMethodException if a necessary method cannot be found. + * @throws InvocationTargetException if an exception occurs during + * invocation of the target method. + */ Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException; } + /** + * Default map containing predefined {@link ValueExtractor} implementations. + * These extractors are used to retrieve values from various sources + * (profile, session, event) when processing definitions. + */ public static final Map DEFAULT_VALUE_EXTRACTORS = new HashMap<>(); static { DEFAULT_VALUE_EXTRACTORS.put("profileProperty", (valueAsString, event) -> PropertyUtils.getProperty(event.getProfile(), "properties." + valueAsString)); @@ -73,6 +122,20 @@ public interface ValueExtractor { DEFAULT_VALUE_EXTRACTORS.put("simpleEventProperty", (valueAsString, event) -> event.getProperty(valueAsString)); } + /** + * Resolves the condition types recursively starting from a root condition. + * This method traverses the parent chain and parameter values of the given + * {@code rootCondition} to ensure all nested conditions have their types + * resolved against the provided definitions service. It updates the + * condition type on the condition object if successful. + * {@code false} otherwise. + * @param definitionsService service used to resolve condition types. + * @param rootCondition the starting condition for resolution. + * @param contextObjectName name of the object providing context + * (for error messages). + * @return {@code true} if all reachable conditions were + * successfully resolved, + */ public static boolean resolveConditionType(final DefinitionsService definitionsService, Condition rootCondition, String contextObjectName) { return resolveConditionType(definitionsService, rootCondition, contextObjectName, new HashSet<>(), false, 0); @@ -158,21 +221,48 @@ private static boolean resolveConditionType(final DefinitionsService definitions } } + /** + * Collects a list of unique condition type IDs present within the + * given root condition. + * This method traverses the entire structure of the condition, including + * nested parameters and collections, and gathers all associated + * condition type ids. + * @param rootCondition the condition whose types are to be collected. + * @return a list of unique strings representing the condition type IDs + * found in the condition tree. + */ public static List getConditionTypeIds(Condition rootCondition) { final List result = new ArrayList(); visitConditions(rootCondition, new ConditionVisitor() { @Override - public void visit(Condition condition) { + /** + * {@inheritDoc} + */ + @Override + public void visit(Condition condition) { result.add(condition.getConditionTypeId()); } @Override - public void postVisit(Condition condition) { + /** + * {@inheritDoc} + */ + @Override + public void postVisit(Condition condition) { } }); return result; } + /** + * Visits every condition within the given condition structure, executing + * the provided visitor for each one. This method handles recursive + * traversal through parameters and collections that may + * contain nested conditions. + * @param rootCondition the starting condition to visit. + * @param visitor the {@link ConditionVisitor} implementation to execute on + * every visited condition. + */ public static void visitConditions(Condition rootCondition, ConditionVisitor visitor) { visitor.visit(rootCondition); // recursive call for sub-conditions as parameters @@ -194,6 +284,20 @@ public static void visitConditions(Condition rootCondition, ConditionVisitor vis visitor.postVisit(rootCondition); } + /** + * Resolves action types for all actions contained within a given rule. + * This method checks if the rule has null or empty actions. If not, it + * attempts to resolve the type of each action using the + * {@link DefinitionsService}. It logs warnings if resolution fails and + * returns {@code false} if any structural error (null/empty actions) or + * resolution failure occurs. + * @param definitionsService service used to resolve action types. + * @param rule the rule containing actions whose types need resolving. + * @param ignoreErrors if {@code true}, warnings about missing or empty + * actions are suppressed. + * @return {@code true} if all actions were successfully resolved, + * {@code false} otherwise. + */ public static boolean resolveActionTypes(DefinitionsService definitionsService, Rule rule, boolean ignoreErrors) { boolean result = true; String ruleId = rule.getItemId(); @@ -227,6 +331,17 @@ public static boolean resolveActionTypes(DefinitionsService definitionsService, return result; } + /** + * Attempts to resolve the action type for a single given action. + * If the action's type is not already set, this method queries the + * definitions service to find and set the correct {@link ActionType}. It + * tracks unresolved types internally, logging warnings if resolution fails. + * {@code false} otherwise. + * @param definitionsService service used to resolve action types. + * @param action the action whose type needs resolving. + * @return {@code true} if the action's type was successfully + * resolved or already set, + */ public static boolean resolveActionType(DefinitionsService definitionsService, Action action) { if (definitionsService == null) { return false; @@ -247,6 +362,16 @@ public static boolean resolveActionType(DefinitionsService definitionsService, A return true; } + /** + * Resolves the value type for a given property type. + * This method first attempts to use the {@link DefinitionsService}'s + * internal {@link TypeResolutionService}. If that service is unavailable, + * it falls back to resolving the value type directly using the definitions + * service's lookup mechanism. + * The provided {@code PropertyType} object will be modified if successful. + * @param definitionsService service used to resolve property types. + * @param propertyType the property type whose value needs resolution. + */ public static void resolveValueType(DefinitionsService definitionsService, PropertyType propertyType) { if (definitionsService == null) { return; @@ -268,12 +393,24 @@ public static void resolveValueType(DefinitionsService definitionsService, Prope /** * @deprecated Use {@link #resolveConditionEventTypes(Condition, DefinitionsService)} instead. + * @param rootCondition the condition whose event types are to be resolved + * @return a set of unique event type identifiers */ @Deprecated public static Set resolveConditionEventTypes(Condition rootCondition) { return resolveConditionEventTypes(rootCondition, null); } + /** + * Resolves all unique event types associated with a condition structure. + * This method traverses the entire condition tree starting from + * {@code rootCondition} and collects every distinct event type ID found in + * any nested condition or parameter. + * @param rootCondition the condition whose event types are to be resolved. + * @param definitionsService service used for resolving event types. + * @return a set of unique strings representing all event types + * associated with the condition. + */ public static Set resolveConditionEventTypes(Condition rootCondition, DefinitionsService definitionsService) { if (rootCondition == null) { return new HashSet<>(); @@ -283,16 +420,34 @@ public static Set resolveConditionEventTypes(Condition rootCondition, De return eventTypeConditionVisitor.getEventTypeIds(); } + /** + * A {@link ConditionVisitor} implementation used to traverse condition + * definitions within a rule or definition structure. It collects all unique + * event type IDs encountered during the traversal of conditions. + * This visitor maintains internal state, using a stack to track the current + * path of visited conditions and accumulating discovered {@code String} + * event type identifiers in a set. + */ public static class EventTypeConditionVisitor implements ConditionVisitor { private final DefinitionsService definitionsService; private Set eventTypeIds = new HashSet<>(); private Stack conditionTypeStack = new Stack<>(); + /** + * Constructs an {@code EventTypeConditionVisitor} and initializes it + * with a service used to resolve condition types. + * @param definitionsService The service providing definitions necessary + * for resolving type information. + */ public EventTypeConditionVisitor(DefinitionsService definitionsService) { this.definitionsService = definitionsService; } + /** + * {@inheritDoc} + */ + @Override public void visit(Condition condition) { conditionTypeStack.push(condition.getConditionTypeId()); @@ -344,15 +499,41 @@ public void visit(Condition condition) { } } + /** + * {@inheritDoc} + */ + @Override public void postVisit(Condition condition) { conditionTypeStack.pop(); } + /** + * Retrieves the set of event type IDs that have been collected by + * this visitor instance. + * @return A {@link Set} containing all resolved event type identifiers. + */ public Set getEventTypeIds() { return eventTypeIds; } } + /** + * Parses a map of values, resolving any placeholders found within string + * values using provided extractors and event context. + * This method iterates through the input map. If a value is a String + * containing placeholders (e.g., ${placeholder}), it recursively resolves + * these placeholders by calling {@link #extractValue(String, Event, Map)}. + * The resulting map contains the processed values with resolved objects + * instead of placeholder strings. + * @param event The current event used for value + * extraction during resolution. + * @param map The input map containing potentially + * placeholder-filled values. + * @param valueExtractors A map linking value type names to their + * respective extractors. + * @return A new {@link Map} with the string placeholders + * resolved into actual object values. + */ @SuppressWarnings("unchecked") public static Map parseMap(Event event, Map map, Map valueExtractors) { Map values = new HashMap<>(); @@ -391,6 +572,27 @@ public static Map parseMap(Event event, Map map, return values; } + /** + * Extracts an object value from a string that follows a specific + * format (type::value). + * The method splits the input string using "::" to separate the expected + * value type from the raw value content. It then uses the provided + * {@link ValueExtractor} to perform the actual extraction based + * on the event context. + * @param s The input string containing the type and + * value separated by "::". + * @param event The current event used during value extraction. + * @param valueExtractors A map of available extractors + * keyed by value type name. + * @return The extracted object value, or null if no extractor is found + * or extraction fails. + * @throws IllegalAccessException If the underlying extractor + * throws this exception. + * @throws NoSuchMethodException If the underlying extractor + * throws this exception. + * @throws InvocationTargetException If the underlying extractor + * throws this exception. + */ public static Object extractValue(String s, Event event, Map valueExtractors) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { Object value = null; @@ -409,6 +611,19 @@ public static Object extractValue(String s, Event event, Map values, Map valueExtractors) { for (Map.Entry entry : values.entrySet()) { @@ -434,10 +649,8 @@ public static boolean hasContextualParameter(Map values, Map getParentChain( /** * Deep copies a parameter value, handling Condition objects and collections containing Conditions. * This is a helper method to avoid code duplication when merging parameters. - * * @param value the parameter value to deep copy * @return a deep copy of the value, or the original value if it's not a Condition or collection */ @@ -535,11 +747,9 @@ private static Object deepCopyParameterValue(Object value) { /** * Resolves the effective condition to use, following the parent chain. - * * This method traverses the parent condition chain and returns the condition * at the end of the chain (the root parent). It merges parameters from all * levels in the chain into the context and the effective condition. - * * @param condition the condition to resolve * @param definitionsService service to resolve condition types * @param context context map for parameter merging (will be modified) @@ -557,7 +767,6 @@ public static Condition resolveEffectiveCondition( /** * Resolves the effective condition to use, following the parent chain. - * * @param condition the condition to resolve * @param definitionsService service to resolve condition types * @param context context map for parameter merging (will be modified) diff --git a/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java b/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java index 0755fbe6d..07d3c4cba 100644 --- a/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java +++ b/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java @@ -210,6 +210,7 @@ public Map build() { /** * Converts a Set to a sorted List for YAML output. * + * @param the element type, which must be {@link Comparable} * @param set the set to convert * @return a sorted list, or null if the set is null or empty */ @@ -223,6 +224,8 @@ public static > List setToSortedList(Set set) { /** * Converts a Set to a sorted List using a mapper function. * + * @param the source element type + * @param the mapped element type, which must be {@link Comparable} * @param set the set to convert * @param mapper the mapper function (must not be null) * @return a sorted list, or null if the set is null or empty diff --git a/build.sh b/build.sh index d4dfb2f30..fd28ed1ff 100755 --- a/build.sh +++ b/build.sh @@ -326,7 +326,7 @@ EOF echo -e " ${CYAN}--search-engine-logs${NC} Stream search engine Docker logs to the Maven console during integration tests" echo -e " ${CYAN}--no-memory-sampler${NC} Disable JVM/system memory sampling during integration tests" echo -e " ${CYAN}--memory-interval SEC${NC} Memory sample interval in seconds (default: 30)" - echo -e " ${CYAN}--javadoc${NC} Build and validate Javadoc after install (fails on doclint errors)" + echo -e " ${CYAN}--javadoc${NC} Build and validate Javadoc after install (doclint errors fail; public/protected tag gaps warn)" echo -e " ${CYAN}--ci${NC} CI mode: no Karaf, 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" @@ -369,7 +369,7 @@ EOF echo " --search-engine-logs Stream search engine Docker logs to the Maven console during integration tests" echo " --no-memory-sampler Disable JVM/system memory sampling during integration tests" echo " --memory-interval SEC Memory sample interval in seconds (default: 30)" - echo " --javadoc Build and validate Javadoc after install (fails on doclint errors)" + echo " --javadoc Build and validate Javadoc after install (doclint errors fail; public/protected tag gaps warn)" echo " --ci CI mode: no Karaf, 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" @@ -1161,7 +1161,7 @@ echo "Estimated time: 3-5 minutes for build, 50-60 minutes with integration test start_timer # Build phases with enhanced output -[ "$JAVADOC" = true ] && total_steps=3 || total_steps=2 +[ "$JAVADOC" = true ] && total_steps=4 || total_steps=2 current_step=0 write_it_run_trace_start() { @@ -1276,7 +1276,7 @@ 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..." + print_progress $((++current_step)) $total_steps "Generating Javadoc (doclint: links, HTML, syntax)..." if [ "$HAS_COLORS" -eq 1 ]; then echo -e "${GRAY}Running: $MVN_CMD javadoc:javadoc -DskipTests $MVN_OPTS${NC}" else @@ -1286,7 +1286,19 @@ if [ "$JAVADOC" = true ]; then print_status "error" "Javadoc validation failed — fix doclint errors above before pushing" exit 1 } - print_status "success" "Javadoc validated successfully" + print_status "success" "Javadoc doclint passed (no broken links, HTML, or syntax errors)" + + print_progress $((++current_step)) $total_steps "Checking public/protected Javadoc tags (warnings only)..." + if [ "$HAS_COLORS" -eq 1 ]; then + echo -e "${GRAY}Running: $MVN_CMD -Pjavadoc-tags-warn checkstyle:check -DskipTests $MVN_OPTS${NC}" + else + echo "Running: $MVN_CMD -Pjavadoc-tags-warn checkstyle:check -DskipTests $MVN_OPTS" + fi + $MVN_CMD -Pjavadoc-tags-warn checkstyle:check -DskipTests $MVN_OPTS || { + print_status "error" "Javadoc tag check failed unexpectedly — see output above" + exit 1 + } + print_status "success" "Javadoc tag check completed (review any warnings above)" fi # Deployment section with enhanced output diff --git a/config/checkstyle-javadoc.xml b/config/checkstyle-javadoc.xml new file mode 100644 index 000000000..775fffc0b --- /dev/null +++ b/config/checkstyle-javadoc.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 02bd8aa79..00b844bdb 100644 --- a/pom.xml +++ b/pom.xml @@ -165,6 +165,7 @@ 1.7 3.2.2 2.13 + 3.6.0 2.0.0 1.0.6 @@ -567,6 +568,37 @@ + + javadoc-tags-warn + + false + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-javadoc-plugin.version} + + + com.puppycrawl.tools + checkstyle + 10.21.4 + + + + config/checkstyle-javadoc.xml + true + false + false + true + warning + + + + + + update-licenses @@ -880,6 +912,16 @@ org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc.plugin.version} + + + protected + + all,-missing + true + + true + false + org.apache.maven.plugins From 83ed5f30608ab0f43cb9297d20b327d0b60b79b7 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 06:34:11 +0200 Subject: [PATCH 02/19] UNOMI-960: Improve tracing-api Javadoc for TraceNode --- .../apache/unomi/tracing/api/TraceNode.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java index 42eaf755a..aec758fe4 100644 --- a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java +++ b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java @@ -38,83 +38,181 @@ public class TraceNode implements Serializable { private List traces; private List children; + /** + * Creates an empty trace node with empty trace and child lists. + */ public TraceNode() { this.traces = new ArrayList<>(); this.children = new ArrayList<>(); } + /** + * Returns the type of operation represented by this node. + * + * @return the operation type + */ public String getOperationType() { return operationType; } + /** + * Sets the type of operation represented by this node. + * + * @param operationType the operation type + */ public void setOperationType(String operationType) { this.operationType = operationType; } + /** + * Returns the human-readable description of this operation. + * + * @return the operation description + */ public String getDescription() { return description; } + /** + * Sets the human-readable description of this operation. + * + * @param description the operation description + */ public void setDescription(String description) { this.description = description; } + /** + * Returns additional context information for this operation. + * + * @return the operation context + */ public String getContext() { return context; } + /** + * Sets additional context information for this operation. + * + * @param context the operation context + */ public void setContext(String context) { this.context = context; } + /** + * Returns the result summary for this operation. + * + * @return the operation result + */ public String getResult() { return result; } + /** + * Sets the result summary for this operation. + * + * @param result the operation result + */ public void setResult(String result) { this.result = result; } + /** + * Returns the start time of this operation in milliseconds. + * + * @return the start time + */ public long getStartTime() { return startTime; } + /** + * Sets the start time of this operation in milliseconds. + * + * @param startTime the start time + */ public void setStartTime(long startTime) { this.startTime = startTime; } + /** + * Returns the end time of this operation in milliseconds. + * + * @return the end time + */ public long getEndTime() { return endTime; } + /** + * Sets the end time of this operation in milliseconds. + * + * @param endTime the end time + */ public void setEndTime(long endTime) { this.endTime = endTime; } + /** + * Returns the operation duration in milliseconds. + * + * @return duration as {@code endTime - startTime}, or zero if end time precedes start time + */ public long getDuration() { return Math.max(0, endTime - startTime); } + /** + * Adds a trace message to this node. + * + * @param trace the trace message to add + */ public void addTrace(String trace) { this.traces.add(trace); } + /** + * Returns the trace messages recorded for this node. + * + * @return an unmodifiable view of trace messages + */ public List getTraces() { return Collections.unmodifiableList(traces); } + /** + * Replaces the trace messages for this node. + * + * @param traces the new trace message list + */ public void setTraces(List traces) { this.traces = traces; } + /** + * Adds a child trace node under this node. + * + * @param child the child trace node to add + */ public void addChild(TraceNode child) { this.children.add(child); } + /** + * Returns the child trace nodes. + * + * @return an unmodifiable view of child nodes + */ public List getChildren() { return Collections.unmodifiableList(children); } + /** + * Replaces the child trace nodes. + * + * @param children the new child node list + */ public void setChildren(List children) { this.children = children; } From 71984f3344fab9a11a937ed59f155448c6bcb403 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 06:35:45 +0200 Subject: [PATCH 03/19] UNOMI-960: Add ASF license header to checkstyle-javadoc.xml --- config/checkstyle-javadoc.xml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/config/checkstyle-javadoc.xml b/config/checkstyle-javadoc.xml index 775fffc0b..1c5f09ced 100644 --- a/config/checkstyle-javadoc.xml +++ b/config/checkstyle-javadoc.xml @@ -2,6 +2,22 @@ + API +Shell --> API +API --> Impl +Impl --> Exec +Impl --> Lock +Impl --> Recovery +Impl --> Registry +Registry --> ExecutorAPI +Impl --> DB : persistent tasks +Impl --> Impl : in-memory tasks + +note right of Impl + Only **executor nodes** pick up most tasks. + Tasks with runOnAllNodes=true + run on every node. +end note +@enduml +---- + +==== Task lifecycle + +A `ScheduledTask` moves through well-defined states. Operators usually see `SCHEDULED`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`, or `CRASHED`. + +[plantuml] +---- +@startuml +[*] --> SCHEDULED : task created + +SCHEDULED --> WAITING : lock held or\ndependency not done +WAITING --> SCHEDULED : prerequisites met + +SCHEDULED --> RUNNING : executor node\nacquires lock +RUNNING --> COMPLETED : success +RUNNING --> FAILED : error (retries may apply) +RUNNING --> CRASHED : node failure + +FAILED --> SCHEDULED : manual retry\nor auto-retry +CRASHED --> RUNNING : resume from\ncheckpoint + +SCHEDULED --> CANCELLED : cancel/delete +RUNNING --> CANCELLED : cancel during run +COMPLETED --> [*] +CANCELLED --> [*] +@enduml +---- + +==== Execution flow in a cluster + +[plantuml] +---- +@startuml +participant "Node A\n(executor)" as A +participant "SchedulerService" as S +participant "TaskLockManager" as L +participant "Persistence" as P +participant "TaskExecutor" as E + +A -> S : check due tasks (every ~1s) +S -> P : load persistent tasks +S -> S : load in-memory tasks +S -> L : try acquire lock +alt lock acquired + L --> S : granted + S -> E : execute(task, callback) + E --> S : complete() / fail() + S -> P : save final state +else lock held by another node + L --> S : denied + S -> S : task stays WAITING or retries later +end +@enduml +---- + +=== Core concepts + +[cols="1,3",options="header"] +|=== +|Concept |Description + +|`taskType` +|Logical name of the work (for example `cache-refresh-segment`, `merge-profiles-reassign-data`). Must match a registered `TaskExecutor`. + +|`TaskExecutor` +|Java component that runs the work. Plugins register executors at activation time. + +|`persistent` task +|Stored in the persistence layer (`scheduledTask` items). Visible on all cluster nodes. Survives restarts. + +|In-memory task +|Exists only on the node that created it (`nonPersistent()`). Typical for local cache refresh. + +|Executor node +|A node configured with `scheduler.executorNode=true` that actually runs tasks. Non-executor nodes can still create and manage tasks. + +|`runOnAllNodes` +|When `true`, the task is eligible on every node (useful for per-node cache maintenance). + +|Lock +|Prevents duplicate execution of the same task (or task type when parallel execution is disallowed) across the cluster. + +|Checkpoint / resume +|Long tasks can save progress and be resumed after a `CRASHED` state via REST or shell. +|=== + +=== Task model + +Tasks are `ScheduledTask` items (`itemType = scheduledTask`). Important fields: + +[cols="1,3",options="header"] +|=== +|Field |Meaning + +|`taskType` |Executor lookup key +|`parameters` |JSON-serializable map passed to the executor +|`status` |`SCHEDULED`, `WAITING`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`, `CRASHED` +|`persistent` |`true` = cluster-visible storage; `false` = node-local memory +|`oneShot` |Run once then stop +|`period` / `timeUnit` |Recurrence interval (0 means one-shot) +|`fixedRate` |`true` = interval from start; `false` = fixed delay from end +|`allowParallelExecution` |`false` = exclusive lock per task type +|`maxRetries` / `retryDelay` |Automatic retry policy after failure +|`dependsOn` |Task IDs that must complete first +|`checkpointData` / `currentStep` |Progress for resumable work +|`executingNodeId` / `lockOwner` |Cluster coordination metadata +|=== + +.Example task JSON (as returned by REST) +[source,json] +---- +{ + "itemId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "itemType": "scheduledTask", + "taskType": "merge-profiles-reassign-data", + "status": "COMPLETED", + "persistent": true, + "oneShot": true, + "allowParallelExecution": false, + "fixedRate": false, + "period": 0, + "timeUnit": "MILLISECONDS", + "initialDelay": 1000, + "failureCount": 0, + "successCount": 1, + "maxRetries": 3, + "retryDelay": 60000, + "parameters": { + "tenantId": "mytenant", + "masterProfileId": "profile-123", + "mergedProfileIds": ["profile-456", "profile-789"], + "anonymousBrowsing": false + }, + "lastExecutedBy": "unomi-node-1", + "lastExecutionDate": "2026-07-12T10:15:30.000Z" +} +---- + +=== Configuration + +Scheduler settings live in `etc/org.apache.unomi.services.cfg` (OSGi PID `org.apache.unomi.services`). + +[cols="2,2,4",options="header"] +|=== +|Property |Default |Description + +|`scheduler.thread.poolSize` |`5` |Thread pool size for task execution +|`scheduler.nodeId` |`test-scheduler-node` |Unique ID for this node in scheduler logs and task metadata. Set a stable value per node in production clusters. +|`scheduler.executorNode` |`true` |Whether this node runs tasks. Set `false` on dedicated REST-only nodes if you split roles. +|`scheduler.lockTimeout` |`10000` |Lock timeout in milliseconds +|`scheduler.purgeTaskEnabled` |`true` |Enables the built-in periodic purge of old completed tasks +|`task.purge.completedTaskTtlDays` |`30` |Age in days after which completed tasks may be purged +|=== + +.Example: dedicated executor node in a 3-node cluster + +[source,properties] +---- +# Node 1 and 2: REST + task execution +scheduler.executorNode=true +scheduler.nodeId=unomi-executor-1 + +# Node 3: API only, no task execution +scheduler.executorNode=false +scheduler.nodeId=unomi-api-3 +---- + +After changing configuration, restart the Unomi bundle or the Karaf container. + +=== REST API + +Base path: `{unomi}/cxs/tasks` + +Requires role `ROLE_UNOMI_ADMIN` or `ROLE_UNOMI_TENANT_ADMIN` (same as other administrative REST endpoints). + +NOTE: The REST API is for **monitoring and operations** (list, inspect, cancel, retry, resume). **Creating** tasks is done from Java code (`SchedulerService`) inside services or plugins, not via a public REST `POST /tasks` endpoint. + +==== List tasks + +.List all tasks (paginated) +[source,bash] +---- +curl -s -u karaf:karaf \ + "http://localhost:8181/cxs/tasks?offset=0&limit=20" +---- + +.Filter by status +[source,bash] +---- +curl -s -u karaf:karaf \ + "http://localhost:8181/cxs/tasks?status=FAILED&limit=50" +---- + +Valid status values: `SCHEDULED`, `WAITING`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`, `CRASHED`. + +.Filter by task type +[source,bash] +---- +curl -s -u karaf:karaf \ + "http://localhost:8181/cxs/tasks?type=cache-refresh-segment&limit=10" +---- + +.Example response (truncated) +[source,json] +---- +{ + "list": [ + { + "itemId": "task-uuid-here", + "taskType": "cache-refresh-segment", + "status": "SCHEDULED", + "persistent": false, + "nextScheduledExecution": "2026-07-12T10:16:01.000Z" + } + ], + "offset": 0, + "pageSize": 20, + "totalSize": 42, + "totalSizeRelation": "EQUAL" +} +---- + +==== Get one task + +[source,bash] +---- +curl -s -u karaf:karaf \ + "http://localhost:8181/cxs/tasks/TASK_ID" +---- + +==== Cancel a task + +Cancellation stops future runs and marks the task `CANCELLED`. The task record remains in storage. + +[source,bash] +---- +curl -s -u karaf:karaf -X DELETE \ + "http://localhost:8181/cxs/tasks/TASK_ID" +---- + +Returns HTTP `204 No Content` on success. + +==== Retry a failed task + +[source,bash] +---- +# Retry keeping failure count +curl -s -u karaf:karaf -X POST \ + "http://localhost:8181/cxs/tasks/TASK_ID/retry" + +# Retry and reset failure count to zero +curl -s -u karaf:karaf -X POST \ + "http://localhost:8181/cxs/tasks/TASK_ID/retry?resetFailureCount=true" +---- + +==== Resume a crashed task + +Use when status is `CRASHED` and the executor supports checkpoint resume. + +[source,bash] +---- +curl -s -u karaf:karaf -X POST \ + "http://localhost:8181/cxs/tasks/TASK_ID/resume" +---- + +=== Karaf shell commands + +Connect to the shell (`ssh -p 8102 karaf@localhost`) and use the `unomi:task-*` commands. + +[cols="2,4",options="header"] +|=== +|Command |Description + +|`unomi:task-list` |List tasks. Options: `-s/--status`, `-t/--type`, `--limit` +|`unomi:task-show TASK_ID` |Detailed view including parameters and checkpoint data +|`unomi:task-cancel TASK_ID` |Cancel a task +|`unomi:task-retry TASK_ID` |Retry a failed task (`-r` resets failure count) +|`unomi:task-purge` |Delete old `COMPLETED` tasks (`-d` days, `-f` force) +|`unomi:task-executor` |Show whether this node is an executor node and its `nodeId` +|=== + +.Examples + +[source,shell] +---- +# List running tasks +unomi:task-list -s RUNNING + +# Show tasks created by profile merge follow-up +unomi:task-list -t merge-profiles-reassign-data + +# Inspect a single task +unomi:task-show a1b2c3d4-e5f6-7890-abcd-ef1234567890 + +# Retry after fixing upstream data +unomi:task-retry a1b2c3d4-e5f6-7890-abcd-ef1234567890 --reset + +# Purge completed tasks older than 14 days +unomi:task-purge --days 14 --force + +# Check executor configuration on this node +unomi:task-executor +---- + +=== Using the scheduler in plugins + +Plugins schedule work by: + +1. Injecting `SchedulerService` (OSGi reference). +2. Registering a `TaskExecutor` for a unique `taskType`. +3. Creating tasks with `newTask(...)`, `createRecurringTask(...)`, or `createTask(...)`. + +==== Pattern A: one-shot async work (real example) + +The `mergeProfilesOnProperty` action in `baseplugin` reassigns events and sessions in the background after a profile merge: + +[plantuml] +---- +@startuml +actor "Rule engine" as R +participant "MergeProfilesOnPropertyAction" as A +participant "SchedulerService" as S +participant "TaskExecutor\nmerge-profiles-reassign-data" as E +database "Persistence" as P + +R -> A : execute merge action +A -> S : registerTaskExecutor(...) +A -> S : newTask(...).asOneShot().schedule() +S -> P : save task (persistent) +S -> E : execute later on executor node +E -> P : update sessions/events\nin tenant context +E --> S : callback.complete() +@enduml +---- + +.Simplified plugin code + +[source,java] +---- +@Reference +private SchedulerService schedulerService; + +@Reference +private ExecutionContextManager executionContextManager; + +private static final String TASK_TYPE = "merge-profiles-reassign-data"; + +public void activate() { + schedulerService.registerTaskExecutor(new TaskExecutor() { + @Override + public String getTaskType() { + return TASK_TYPE; + } + + @Override + public void execute(ScheduledTask task, TaskStatusCallback callback) { + try { + String tenantId = (String) task.getParameters().get("tenantId"); + executionContextManager.executeAsTenant(tenantId, () -> { + // ... reassign sessions/events ... + return null; + }); + callback.complete(); + } catch (Exception e) { + callback.fail(e.getMessage()); + } + } + }); +} + +public void scheduleReassign(String tenantId, String masterId, List mergedIds) { + schedulerService.newTask(TASK_TYPE) + .withParameters(Map.of( + "tenantId", tenantId, + "masterProfileId", masterId, + "mergedProfileIds", mergedIds, + "anonymousBrowsing", false + )) + .withInitialDelay(1, TimeUnit.SECONDS) + .asOneShot() + .disallowParallelExecution() + .schedule(); +} +---- + +IMPORTANT: When task parameters include a `tenantId`, always run the work inside `executionContextManager.executeAsTenant(...)` (or set the security subject explicitly) so persistence queries respect tenant isolation. + +==== Pattern B: simple recurring maintenance + +For straightforward periodic code without a custom executor class: + +[source,java] +---- +schedulerService.createRecurringTask( + "my-plugin-daily-cleanup", + 24, TimeUnit.HOURS, + () -> cleanupStaleLocalCache(), + false // non-persistent: each node runs its own copy if executor +); +---- + +==== Pattern C: fluent builder (recommended) + +The builder covers most scheduling options in one place. + +.One-shot task with retries +[source,java] +---- +schedulerService.newTask("export-tenant-snapshot") + .withParameters(Map.of("tenantId", "mytenant", "destination", "/tmp/export")) + .withInitialDelay(5, TimeUnit.SECONDS) + .asOneShot() + .withMaxRetries(3) + .withRetryDelay(2, TimeUnit.MINUTES) + .disallowParallelExecution() + .withExecutor(myExportExecutor) + .schedule(); +---- + +.Periodic task with fixed delay +[source,java] +---- +schedulerService.newTask("refresh-external-lookup") + .withPeriod(15, TimeUnit.MINUTES) + .withFixedDelay() + .disallowParallelExecution() + .nonPersistent() + .withSimpleExecutor(() -> refreshLookupTable()) + .schedule(); +---- + +.Periodic task on every cluster node +[source,java] +---- +schedulerService.newTask("local-temp-file-cleanup") + .withPeriod(1, TimeUnit.HOURS) + .withFixedDelay() + .runOnAllNodes() + .nonPersistent() + .withSimpleExecutor(() -> deleteStaleTempFiles()) + .schedule(); +---- + +.Task chain with dependencies +[source,java] +---- +ScheduledTask extract = schedulerService.newTask("etl-extract") + .asOneShot() + .withExecutor(extractExecutor) + .schedule(); + +schedulerService.newTask("etl-load") + .asOneShot() + .withDependencies(extract.getItemId()) + .withExecutor(loadExecutor) + .schedule(); +---- + +==== Pattern D: resumable long-running executor + +For work that may run longer than a node failover window, save checkpoints and implement `canResume` / `resume`: + +[source,java] +---- +public class BulkReindexExecutor implements TaskExecutor { + + @Override + public String getTaskType() { + return "bulk-reindex"; + } + + @Override + public void execute(ScheduledTask task, TaskStatusCallback callback) { + runFromOffset(task, callback, 0); + } + + @Override + public boolean canResume(ScheduledTask task) { + return task.getCheckpointData() != null + && task.getCheckpointData().containsKey("lastOffset"); + } + + @Override + public void resume(ScheduledTask task, TaskStatusCallback callback) { + int offset = ((Number) task.getCheckpointData().get("lastOffset")).intValue(); + runFromOffset(task, callback, offset); + } + + private void runFromOffset(ScheduledTask task, TaskStatusCallback callback, int start) { + try { + for (int i = start; i < total; i += batchSize) { + reindexBatch(i, batchSize); + callback.updateStep("reindex", Map.of("lastOffset", i + batchSize)); + callback.checkpoint(Map.of("lastOffset", i + batchSize)); + } + callback.complete(); + } catch (Exception e) { + callback.fail(e.getMessage()); + } + } +} +---- + +After a crash, operators can call `POST /cxs/tasks/{id}/resume` or `unomi:task-retry` depending on the failure mode. + +==== OSGi Blueprint wiring + +Reference `SchedulerService` from your plugin blueprint: + +[source,xml] +---- + + + + + +---- + +Register the executor in your component's `activate()` method and unregister in `deactivate()` when possible. + +=== Built-in tasks you will see in production + +Unomi core and plugins already create scheduler tasks. These are normal and expected: + +[cols="2,4",options="header"] +|=== +|Task type (examples) |Purpose + +|`cache-refresh-*` |Reload segments, rules, property types, scopes, etc. from persistence into memory +|`task-purge` |System task that purges old completed scheduler tasks +|`merge-profiles-reassign-data` |Async session/event reassignment after profile merge (`baseplugin`) +|=== + +Use `unomi:task-list` or `GET /cxs/tasks?type=...` to inspect them. + +=== Metrics + +`SchedulerService` exposes counters useful for monitoring: + +[source,java] +---- +Map metrics = schedulerService.getAllMetrics(); +// keys include: +// tasks.completed, tasks.failed, tasks.crashed, tasks.running, +// tasks.lock.conflicts, tasks.recovery.attempts, ... +---- + +In custom code: + +[source,java] +---- +long failures = schedulerService.getMetric("tasks.failed"); +---- + +=== Choosing persistence and execution settings + +[cols="1,1,1,2",options="header"] +|=== +|Need |persistent? |runOnAllNodes? |Notes + +|Cluster-wide job (ETL, merge follow-up) |yes |no |Survives restart; one executor runs it +|Per-node cache refresh |no |no or yes |`nonPersistent()` + often `runOnAllNodes()` for local caches +|Fire-and-forget after HTTP request |yes or no |no |One-shot; tune `maxRetries` +|Must never overlap |any |any |`disallowParallelExecution()` +|=== + +=== Troubleshooting + +[cols="1,3",options="header"] +|=== +|Symptom |What to check + +|Task stays `SCHEDULED` forever |Is this node an executor (`scheduler.executorNode=true`)? Is a `TaskExecutor` registered for the `taskType`? +|Task stays `WAITING` |Another run may hold the lock, or a dependency task is not `COMPLETED` +|Task `FAILED` after retries |Read `lastError` via `task-show` or REST; fix root cause; call retry +|Task `CRASHED` |Node failure during run; use resume if checkpoints exist +|Duplicate cache refresh tasks |Use `disallowParallelExecution()` and stable task names (see `AbstractMultiTypeCachingService`) +|Tenant data wrong in background job |Missing `executeAsTenant` / security subject setup in executor +|=== + +=== Related documentation + +* <<_request_examples,Tenant REST examples>> — creating tenants whose data background tasks will process +* <<_shell_commands,Karaf shell>> — operational commands including `unomi:task-*` +* <<_writing_plugins,Writing plugins>> — plugin structure and OSGi wiring +* <<_multitenancy,Multitenancy>> — tenant isolation rules diff --git a/manual/src/main/asciidoc/shell-commands.adoc b/manual/src/main/asciidoc/shell-commands.adoc index b3c0e3946..81e5b9873 100644 --- a/manual/src/main/asciidoc/shell-commands.adoc +++ b/manual/src/main/asciidoc/shell-commands.adoc @@ -204,3 +204,36 @@ working on a plugin. For example to remove all the definitions deployed by a plu command: `undeploy-definition BUNDLE_ID * *` when `BUNDLE_ID` is the identifier of the bundle that contains your plugin. |=== + +==== Scheduler commands + +These commands manage background tasks created by the cluster-aware scheduler (see <<_scheduler,Task scheduler>>). + +.Table Scheduler commands +|=== +|Command|Arguments|Description + +|task-list +|[-s status] [-t type] [--limit N] +|List scheduled tasks. Filter by status (`SCHEDULED`, `RUNNING`, `FAILED`, …) or task type. + +|task-show +|task-id +|Show full details for one task (parameters, checkpoint data, errors). + +|task-cancel +|task-id +|Cancel a task (stops future executions). + +|task-retry +|task-id [-r] +|Retry a failed task. `-r` / `--reset` clears the failure count. + +|task-purge +|[-d days] [-f] +|Delete old `COMPLETED` tasks (default: older than 7 days). `-f` skips confirmation. + +|task-executor +|[true|false] +|Show whether this node runs tasks and its scheduler `nodeId`. +|=== diff --git a/manual/src/main/asciidoc/useful-unomi-urls.adoc b/manual/src/main/asciidoc/useful-unomi-urls.adoc index 49b054659..1f43468e9 100644 --- a/manual/src/main/asciidoc/useful-unomi-urls.adoc +++ b/manual/src/main/asciidoc/useful-unomi-urls.adoc @@ -81,4 +81,24 @@ where PROFILE_ID is a profile identifier. This will indeed retrieve all the even |/cxs/rules/statistics |DELETE |Reset all rule execution statistics to 0 + +|/cxs/tasks +|GET +|Listing scheduled background tasks (filter with `?status=` or `?type=`) + +|/cxs/tasks/{taskId} +|GET +|View a single scheduled task + +|/cxs/tasks/{taskId} +|DELETE +|Cancel a scheduled task + +|/cxs/tasks/{taskId}/retry +|POST +|Retry a failed task (`?resetFailureCount=true` optional) + +|/cxs/tasks/{taskId}/resume +|POST +|Resume a crashed task from checkpoint |=== diff --git a/manual/src/main/asciidoc/writing-plugins.adoc b/manual/src/main/asciidoc/writing-plugins.adoc index b5f0f7a8b..43637feb3 100644 --- a/manual/src/main/asciidoc/writing-plugins.adoc +++ b/manual/src/main/asciidoc/writing-plugins.adoc @@ -64,6 +64,14 @@ within the `META-INF/cxs/` directory of the bundle JAR file: http://aries.apache.org/modules/blueprint.html[Blueprint] is used to declare what the plugin provides and inject any required dependency. The Blueprint file is located, as usual, at `OSGI-INF/blueprint/blueprint.xml` in the bundle JAR file. +=== Scheduling background work + +Plugins can register `TaskExecutor` implementations and schedule one-shot or recurring tasks through `SchedulerService`. +This is the recommended way to run maintenance, async follow-up work, or long-running jobs in a cluster-safe way. + +See <<_scheduler,Task scheduler>> for architecture, REST operations, shell commands, and copy-paste examples +(including tenant-aware executors and checkpoint/resume patterns). + === Search Engine Specific Implementations When implementing plugins that need to interact with the search engine layer (ElasticSearch or OpenSearch), special consideration must be taken to ensure compatibility and proper deployment. From 963f9a56ed6796aa72fbdf315eef2e4edb7512d1 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 08:52:24 +0200 Subject: [PATCH 09/19] UNOMI-960: Polish API Javadoc and manual updates for 3.1 Improve Javadoc quality across api, rest, persistence-spi, services, and router modules: fix layout, remove script-style comments, restore field docs, and use fully qualified @link targets. Extend manual cross-links for scheduler, multitenancy, and whats-new; fix TenantRequest Javadoc. --- .../org/apache/unomi/api/BatchUpdate.java | 21 +- .../org/apache/unomi/api/ClusterNode.java | 100 +++-- .../java/org/apache/unomi/api/Consent.java | 100 ++--- .../org/apache/unomi/api/ContextRequest.java | 43 +- .../org/apache/unomi/api/ContextResponse.java | 78 ++-- .../java/org/apache/unomi/api/CustomItem.java | 8 +- .../main/java/org/apache/unomi/api/Event.java | 63 +-- .../java/org/apache/unomi/api/EventInfo.java | 28 +- .../org/apache/unomi/api/EventProperty.java | 26 +- .../org/apache/unomi/api/EventSource.java | 53 +-- .../org/apache/unomi/api/EventTarget.java | 47 ++- .../unomi/api/EventsCollectorRequest.java | 50 ++- .../apache/unomi/api/ExecutionContext.java | 119 +++--- .../java/org/apache/unomi/api/GeoPoint.java | 22 +- .../main/java/org/apache/unomi/api/Item.java | 185 +++++---- .../java/org/apache/unomi/api/Metadata.java | 113 ++--- .../org/apache/unomi/api/MetadataItem.java | 22 +- .../java/org/apache/unomi/api/Parameter.java | 75 ++-- .../org/apache/unomi/api/PartialList.java | 111 ++--- .../main/java/org/apache/unomi/api/Patch.java | 19 +- .../java/org/apache/unomi/api/Persona.java | 1 + .../org/apache/unomi/api/PersonaSession.java | 2 + .../apache/unomi/api/PersonaWithSessions.java | 37 +- .../unomi/api/PersonalizationResult.java | 9 + .../java/org/apache/unomi/api/PluginType.java | 15 +- .../java/org/apache/unomi/api/Profile.java | 68 +-- .../org/apache/unomi/api/ProfileAlias.java | 52 ++- .../unomi/api/PropertyMergeStrategyType.java | 25 +- .../org/apache/unomi/api/PropertyType.java | 119 +++--- .../main/java/org/apache/unomi/api/Scope.java | 2 +- .../java/org/apache/unomi/api/ServerInfo.java | 105 ++--- .../java/org/apache/unomi/api/Session.java | 69 ++-- .../org/apache/unomi/api/TimestampedItem.java | 4 +- .../main/java/org/apache/unomi/api/Topic.java | 20 +- .../java/org/apache/unomi/api/ValueType.java | 40 +- .../org/apache/unomi/api/actions/Action.java | 13 +- .../apache/unomi/api/actions/ActionType.java | 23 +- .../apache/unomi/api/campaigns/Campaign.java | 54 +-- .../unomi/api/campaigns/CampaignDetail.java | 51 +-- .../api/campaigns/events/CampaignEvent.java | 26 +- .../unomi/api/conditions/Condition.java | 29 +- .../unomi/api/conditions/ConditionType.java | 14 +- .../api/conditions/ConditionValidation.java | 3 +- .../BadSegmentConditionException.java | 6 +- .../java/org/apache/unomi/api/goals/Goal.java | 54 ++- .../apache/unomi/api/goals/GoalReport.java | 94 +++-- .../org/apache/unomi/api/lists/UserList.java | 4 +- .../org/apache/unomi/api/query/Aggregate.java | 14 +- .../unomi/api/query/AggregateQuery.java | 12 +- .../org/apache/unomi/api/query/DateRange.java | 39 +- .../org/apache/unomi/api/query/IpRange.java | 38 +- .../apache/unomi/api/query/NumericRange.java | 39 +- .../org/apache/unomi/api/query/Query.java | 110 ++--- .../java/org/apache/unomi/api/rules/Rule.java | 17 +- .../unomi/api/rules/RuleStatistics.java | 96 +++-- .../unomi/api/security/EncryptionService.java | 10 +- .../unomi/api/security/SecurityService.java | 17 +- .../SecurityServiceConfiguration.java | 78 ++-- .../unomi/api/segments/DependentMetadata.java | 28 +- .../apache/unomi/api/segments/Scoring.java | 9 +- .../unomi/api/segments/ScoringElement.java | 14 +- .../apache/unomi/api/segments/Segment.java | 9 +- .../unomi/api/segments/SegmentsAndScores.java | 22 +- .../unomi/api/services/ClusterService.java | 12 +- .../services/ConditionValidationService.java | 66 +-- .../api/services/ConfigSharingService.java | 14 +- .../api/services/DefinitionsService.java | 150 ++++--- .../unomi/api/services/EventService.java | 96 ++--- .../api/services/ExecutionContextManager.java | 7 + .../unomi/api/services/GoalsService.java | 97 +++-- .../unomi/api/services/InvalidObjectInfo.java | 106 +++-- .../api/services/PersonalizationService.java | 160 +++---- .../unomi/api/services/PrivacyService.java | 145 ++++--- .../unomi/api/services/ProfileService.java | 391 +++++++++--------- .../unomi/api/services/QueryService.java | 59 ++- .../api/services/RuleListenerService.java | 3 + .../unomi/api/services/RulesService.java | 71 ++-- .../unomi/api/services/SchedulerService.java | 16 + .../unomi/api/services/SegmentService.java | 121 +++--- .../api/services/TenantLifecycleListener.java | 3 +- .../unomi/api/services/TopicService.java | 28 +- .../api/services/ValueTypeValidator.java | 1 + .../apache/unomi/api/tasks/ScheduledTask.java | 46 +-- .../org/apache/unomi/api/tenants/ApiKey.java | 22 +- .../unomi/api/tenants/ApiKeyConfig.java | 22 +- .../api/tenants/ApiKeyCreationResult.java | 5 + .../unomi/api/tenants/AuditService.java | 24 +- .../unomi/api/tenants/ItemAuditService.java | 26 +- .../org/apache/unomi/api/tenants/Tenant.java | 31 +- .../unomi/api/tenants/TenantAuditService.java | 2 +- .../api/tenants/TenantBackupMetadata.java | 36 +- .../api/tenants/TenantEventPurgeResult.java | 24 +- .../unomi/api/tenants/TenantScopeUsage.java | 37 +- .../apache/unomi/api/tenants/TenantUsage.java | 71 ++-- .../unomi/api/tenants/TenantUsageService.java | 1 + .../tenants/security/SecurityAuditReport.java | 89 ++-- .../tenants/security/SecuritySettings.java | 72 ++-- .../security/SecurityValidationResult.java | 14 +- .../security/TenantSecurityService.java | 7 +- .../apache/unomi/api/utils/ParserHelper.java | 49 ++- .../org/apache/unomi/api/utils/YamlUtils.java | 5 +- .../unomi/router/api/ImportConfiguration.java | 41 +- .../unomi/router/api/RouterConstants.java | 4 +- .../apache/unomi/router/api/RouterUtils.java | 4 +- .../main/asciidoc/building-and-deploying.adoc | 4 +- manual/src/main/asciidoc/clustering.adoc | 83 +++- manual/src/main/asciidoc/configuration.adoc | 3 +- manual/src/main/asciidoc/index.adoc | 5 + .../migrations/migrate-3.0-to-3.1.adoc | 1 + .../migrations/v2-compatibility-mode.adoc | 3 +- manual/src/main/asciidoc/multitenancy.adoc | 10 +- .../src/main/asciidoc/request-examples.adoc | 1 + manual/src/main/asciidoc/security.adoc | 2 + manual/src/main/asciidoc/shell-commands.adoc | 50 +++ .../src/main/asciidoc/useful-unomi-urls.adoc | 36 ++ manual/src/main/asciidoc/whats-new.adoc | 36 +- .../persistence/spi/PersistenceService.java | 194 ++++----- .../conditions/ConditionContextHelper.java | 2 + .../V2ThirdPartyConfigService.java | 90 ++-- .../impl/DefaultRestAuthenticationConfig.java | 4 +- .../endpoints/CampaignsServiceEndPoint.java | 34 +- .../endpoints/ClusterServiceEndPoint.java | 6 +- .../endpoints/DefinitionsServiceEndPoint.java | 100 ++--- .../rest/endpoints/EventServiceEndpoint.java | 20 +- .../rest/endpoints/GoalsServiceEndPoint.java | 31 +- .../rest/endpoints/PatchServiceEndPoint.java | 1 + .../endpoints/ProfileServiceEndPoint.java | 228 +++++----- .../rest/endpoints/QueryServiceEndPoint.java | 59 ++- .../rest/endpoints/RulesServiceEndPoint.java | 41 +- .../rest/endpoints/ScopeServiceEndPoint.java | 10 +- .../endpoints/ScoringServiceEndPoint.java | 39 +- .../endpoints/SegmentServiceEndPoint.java | 58 ++- .../unomi/rest/endpoints/TestEndPoint.java | 4 +- .../endpoints/UserListServiceEndPoint.java | 22 +- .../exception/InvalidRequestException.java | 1 + .../rest/models/EventCollectorResponse.java | 6 +- .../unomi/rest/tenants/TenantRequest.java | 6 +- .../org/apache/unomi/utils/HttpUtils.java | 18 +- .../impl/scheduler/SchedulerProvider.java | 2 + .../impl/scheduler/SchedulerServiceImpl.java | 17 + .../impl/scheduler/TaskHistoryManager.java | 18 +- .../impl/scheduler/TaskLockManager.java | 2 +- .../impl/scheduler/TaskRecoveryManager.java | 58 ++- .../impl/scheduler/TaskStateManager.java | 31 +- .../impl/tenants/TenantSecurityService.java | 2 +- .../apache/unomi/tracing/api/TraceNode.java | 6 +- 146 files changed, 3448 insertions(+), 2848 deletions(-) diff --git a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java index 240b0c8ad..a68068ee3 100644 --- a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java +++ b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java @@ -20,10 +20,10 @@ import org.apache.unomi.api.conditions.Condition; /** - * Describes a bulk update operation that changes a property on every item - * matching a {@link org.apache.unomi.api.conditions.Condition}. - * Used when administrators or imports need to apply the same property change - * to many profiles, sessions, or other items at once. + * Bulk property update specification executed as a scroll query. + * Selects items with a {@link org.apache.unomi.api.conditions.Condition}, sets one + * property (Apache Commons BeanUtils expression) to a new value, and optionally + * applies a {@link PropertyMergeStrategyType} when merging complex fields. */ public class BatchUpdate { private String propertyName; @@ -34,11 +34,10 @@ public class BatchUpdate { private int scrollBatchSize = 1000; /** - * Retrieves the property name which value needs to be updated. Note that the property name follows the - * Apache Commons BeanUtils expression - * format + * Property to update, as an Apache Commons BeanUtils expression + * (see BeanUtils expression format). * - * @return an Apache Commons BeanUtils expression identifying which property we want to update + * @return property expression to update */ public String getPropertyName() { return propertyName; @@ -54,7 +53,7 @@ public void setPropertyName(String propertyName) { } /** - * Retrieves the new property value. + * New value to assign to the property. * * @return the new property value */ @@ -72,7 +71,7 @@ public void setPropertyValue(Object propertyValue) { } /** - * Retrieves the condition which items we want to update must satisfy. + * Condition that items must match to be updated. * * @return the condition which items we want to update must satisfy */ @@ -90,7 +89,7 @@ public void setCondition(Condition condition) { } /** - * Retrieves the identifier for the {@link PropertyMergeStrategyType} to use during the update if needed. + * {@link PropertyMergeStrategyType} id to use when merging the new value. * * @return the identifier for the {@link PropertyMergeStrategyType} to use during the update if needed */ 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 09bed0dd8..302d0b533 100644 --- a/api/src/main/java/org/apache/unomi/api/ClusterNode.java +++ b/api/src/main/java/org/apache/unomi/api/ClusterNode.java @@ -45,7 +45,7 @@ public class ClusterNode extends Item { private ServerInfo serverInfo; /** - * Instantiates a new Cluster node. + * Creates an empty cluster node with item type {@link #ITEM_TYPE}. */ public ClusterNode() { super(); @@ -53,24 +53,27 @@ public ClusterNode() { } /** - * Retrieves the cpu load. - * @return the cpu load + * Current CPU load on this node. + * + * @return CPU load */ public double getCpuLoad() { return cpuLoad; } /** - * Sets the cpu load. - * @param cpuLoad the cpu load + * Sets the CPU load. + * + * @param cpuLoad CPU load */ public void setCpuLoad(double cpuLoad) { this.cpuLoad = cpuLoad; } /** - * Retrieves the public host address. - * @return the public host address + * Public host address clients use to reach this node. + * + * @return public host address */ public String getPublicHostAddress() { return publicHostAddress; @@ -78,135 +81,152 @@ public String getPublicHostAddress() { /** * Sets the public host address. - * @param publicHostAddress the public host address + * + * @param publicHostAddress public host address */ public void setPublicHostAddress(String publicHostAddress) { this.publicHostAddress = publicHostAddress; } /** - * Retrieves the internal host address which uses the HTTP/HTTPS protocol for communications between clients and the context server. - * @return the internal host address + * Internal HTTP/HTTPS address used for client-to-server communication. + * + * @return internal host address */ public String getInternalHostAddress() { return internalHostAddress; } /** - * Sets the internal host address which uses the HTTP/HTTPS protocol for communications between clients and the context server. - * @param internalHostAddress the internal host address + * Sets the internal HTTP/HTTPS host address. + * + * @param internalHostAddress internal host address */ public void setInternalHostAddress(String internalHostAddress) { this.internalHostAddress = internalHostAddress; } /** - * Retrieves the load average for the last minute, five minutes and fifteen minutes. - * @return an array of {@code double} containing, in order and starting from index {@code 0}, the load average for the last minute, last five minutes and last fifteen minutes + * Load averages for the last 1, 5, and 15 minutes. + * + * @return three-element array: index 0 = 1 min, 1 = 5 min, 2 = 15 min */ public double[] getLoadAverage() { return loadAverage; } /** - * Sets the load average for the last minute, five minutes and fifteen minutes. - * @param loadAverage an array of {@code double} containing, in order and starting from index {@code 0}, the load average for the last minute, last five minutes and last fifteen minutes + * Sets load averages for the last 1, 5, and 15 minutes. + * + * @param loadAverage three-element array: index 0 = 1 min, 1 = 5 min, 2 = 15 min */ public void setLoadAverage(double[] loadAverage) { this.loadAverage = loadAverage; } /** - * Retrieves the uptime. - * @return the uptime + * Node uptime in milliseconds. + * + * @return uptime */ public long getUptime() { return uptime; } /** - * Sets the uptime. - * @param uptime the uptime + * Sets the node uptime. + * + * @param uptime uptime in milliseconds */ public void setUptime(long uptime) { this.uptime = uptime; } /** - * Determines whether this ClusterNode is a master node, i.e. this node doesn't store any data but is only focused on cluster management operations. - * @return {@code true} if this node is a master node, {@code false} otherwise + * Whether this node is a master (coordination only, no local context data). + * + * @return {@code true} if this is a master node */ public boolean isMaster() { return master; } /** - * Specifies whether this ClusterNode is a master node, i.e. this node doesn't store any data but is only focused on cluster management operations. - * @param master {@code true} if this node is a master node, {@code false} otherwise + * Sets whether this node is a master (coordination only, no local context data). + * + * @param master {@code true} for a master node */ public void setMaster(boolean master) { this.master = master; } /** - * Determines whether this ClusterNode locally stores data. - * @return {@code true} if this node locally stores data, {@code false} otherwise + * Whether this node stores context data locally. + * + * @return {@code true} if this is a data node */ public boolean isData() { return data; } /** - * Specifies whether this ClusterNode locally stores data. - * @param data {@code true} if this node locally stores data, {@code false} otherwise + * Sets whether this node stores context data locally. + * + * @param data {@code true} for a data node */ public void setData(boolean data) { this.data = data; } /** - * Retrieves the node start time in milliseconds. - * @return the start time + * When this node started (milliseconds since epoch). + * + * @return start time */ public long getStartTime() { return startTime; } /** - * Sets the node start time in milliseconds. - * @param startTime the start time + * Sets the node start time. + * + * @param startTime start time in milliseconds */ public void setStartTime(long startTime) { this.startTime = startTime; } /** - * Retrieves the last heartbeat time in milliseconds. - * @return the last heartbeat time + * When this node last sent a heartbeat (milliseconds since epoch). + * + * @return last heartbeat time */ public long getLastHeartbeat() { return lastHeartbeat; } /** - * Sets the last heartbeat time in milliseconds. - * @param lastHeartbeat the last heartbeat time + * Sets the last heartbeat time. + * + * @param lastHeartbeat last heartbeat time in milliseconds */ public void setLastHeartbeat(long lastHeartbeat) { this.lastHeartbeat = lastHeartbeat; } /** - * Gets the server information. - * @return the server information + * Build and capability details for this node. + * + * @return server information */ public ServerInfo getServerInfo() { return serverInfo; } /** - * Sets the server information. - * @param serverInfo the server information + * Sets the server information for this node. + * + * @param serverInfo server information */ public void setServerInfo(ServerInfo serverInfo) { this.serverInfo = serverInfo; diff --git a/api/src/main/java/org/apache/unomi/api/Consent.java b/api/src/main/java/org/apache/unomi/api/Consent.java index 2cb20ab4b..966c9a53d 100644 --- a/api/src/main/java/org/apache/unomi/api/Consent.java +++ b/api/src/main/java/org/apache/unomi/api/Consent.java @@ -38,18 +38,19 @@ public class Consent implements Serializable { private Date revokeDate; /** - * Empty constructor mostly used for JSON (de-) serialization + * Default constructor for JSON (de)serialization. */ public Consent() { } /** - * A constructor to directly build a consent with all it's properties + * Creates a consent with all properties set. + * * @param scope the scope for this consent * @param typeIdentifier the identifier of the type this consent applies to - * @param status the type of status that we are storing for this consent. May be one of @ConsentStatus.DENIED, @ConsentStatus.GRANTED, @ConsentStatus.REVOKED - * @param statusDate the starting date at which this consent was given - * @param revokeDate the date at which this consent will (automatically) revoke + * @param status the consent status ({@link ConsentStatus#DENIED}, {@link ConsentStatus#GRANTED}, or {@link ConsentStatus#REVOKED}) + * @param statusDate the date when this consent was given + * @param revokeDate the date when this consent will be automatically revoked */ public Consent(String scope, String typeIdentifier, ConsentStatus status, Date statusDate, Date revokeDate) { this.scope = scope; @@ -60,12 +61,12 @@ public Consent(String scope, String typeIdentifier, ConsentStatus status, Date s } /** - * A constructor from a map used for example when we use the deserialized data from event - * properties. - * @param consentMap a Map that contains the following key-value pairs : typeIdentifier:String, status:String (must - * be one of GRANTED, DENIED or REVOKED), statusDate:String (ISO8601 date format !), revokeDate:String (ISO8601 date format !) - * @param dateFormat a DateFormat instance to convert the date string to date objects - * @throws ParseException in case one of the dates failed to parse properly + * Creates a consent from a deserialized property map. + * + * @param consentMap map with keys {@code typeIdentifier}, {@code status} (GRANTED, DENIED, or REVOKED), + * {@code statusDate}, and {@code revokeDate} as ISO-8601 strings + * @param dateFormat date format used to parse date strings + * @throws ParseException if a date string cannot be parsed */ public Consent(Map consentMap, DateFormat dateFormat) throws ParseException { if (consentMap.containsKey("scope")) { @@ -93,58 +94,63 @@ public Consent(Map consentMap, DateFormat dateFormat) throws Pars } /** - * Retrieve the scope for this consent - * @return a scope identifier + * Scope this consent applies to. + * + * @return the scope identifier */ public String getScope() { return scope; } /** - * Set the scope for this consent - * @param scope a scope identifier + * Sets the scope for this consent. + * + * @param scope the scope identifier */ public void setScope(String scope) { this.scope = scope; } /** - * Set the type identifier. This must be (no validation is done) a unique identifier for the consent type. These - * are usually externally defined, Apache Unomi has no knowledge of them except for this type identifier. - * @param typeIdentifier a unique String to identify the consent type + * Sets the consent type identifier (externally defined, not validated by Unomi). + * + * @param typeIdentifier the consent type identifier */ public void setTypeIdentifier(String typeIdentifier) { this.typeIdentifier = typeIdentifier; } /** - * Retrieve the consent type identifier for this consent. - * @return a String containing the type identifier + * Consent type identifier. + * + * @return the type identifier */ public String getTypeIdentifier() { return typeIdentifier; } /** - * Retrieves the status for this consent. This is of type @ConsentStatus - * @return the current value for the status. + * Current consent status. + * + * @return the consent status */ public ConsentStatus getStatus() { return status; } /** - * Sets the status for this consent. A Consent status of type REVOKED means that this consent is meant to be destroyed. - * @param status the status to set on this consent + * Sets the consent status. A status of {@link ConsentStatus#REVOKED} means the consent should be removed. + * + * @param status the consent status to set */ public void setStatus(ConsentStatus status) { this.status = status; } /** - * Retrieve the date at which this consent was given. If this date is in the future the consent should not be - * considered valid yet. - * @return a valid date or null if this date was not set. + * Date when this consent was given. Future dates mean the consent is not yet valid. + * + * @return the status date, or {@code null} if unset */ public Date getStatusDate() { return statusDate; @@ -152,34 +158,35 @@ public Date getStatusDate() { /** * Sets the date from which this consent applies. - * @param statusDate a valid Date or null if we set not starting date (immediately valid) + * + * @param statusDate the effective date, or {@code null} for immediate validity */ public void setStatusDate(Date statusDate) { this.statusDate = statusDate; } /** - * Retrieves the end date for this consent. After this date the consent is no longer valid and should be disposed of. - * If this date is not set it means the consent will never expire - * @return a valid Date or null to indicate an unlimited consent + * Expiration date after which this consent is no longer valid. {@code null} means no expiry. + * + * @return the revoke date, or {@code null} if unlimited */ public Date getRevokeDate() { return revokeDate; } /** - * Sets the end date for this consent. After this date the consent is no longer valid and should be disposed of. - * If this date is not set it means the consent will never expire - * @param revokeDate a valid Date or null to indicate an unlimited consent + * Sets the expiration date for this consent. {@code null} means the consent never expires. + * + * @param revokeDate the revoke date, or {@code null} for unlimited validity */ public void setRevokeDate(Date revokeDate) { this.revokeDate = revokeDate; } /** - * Test if the consent is GRANTED right now. - * @return true if the consent is granted using the current date (internally a new Date() is created and the - * {@link Consent#isConsentGrantedAtDate} is called. + * Whether this consent is granted at the current time. + * + * @return {@code true} if the consent is granted now */ @XmlTransient public boolean isConsentGrantedNow() { @@ -187,9 +194,10 @@ public boolean isConsentGrantedNow() { } /** - * Tests if the consent is GRANTED at the specified date - * @param testDate the date against which to test the consent to be granted. - * @return true if the consent is granted at the specified date, false otherwise. + * Whether this consent is granted at the given date. + * + * @param testDate the date to test against + * @return {@code true} if the consent is granted at the specified date */ @XmlTransient public boolean isConsentGrantedAtDate(Date testDate) { @@ -202,14 +210,10 @@ public boolean isConsentGrantedAtDate(Date testDate) { } /** - * This is a utility method to generate a Map based on the contents of the consents. The format of the map is the - * same as the one used in the Map Consent constructor. For dates you must specify a dateFormat that will be used - * to format the dates. This dateFormat should usually support ISO8601 to make integrate with Javascript clients - * easy to integrate. - * @param dateFormat a dateFormat instance such as ISO8601DateFormat to generate the String formats for the statusDate - * and revokeDate map entries. - * @return a Map that contains the following key-value pairs : typeIdentifier:String, status:String (must - * be one of GRANTED, DENIED or REVOKED), statusDate:String (generated by the dateFormat), revokeDate:String (generated by the dateFormat) + * Serializes this consent to a map using the same format as the map constructor. + * + * @param dateFormat date format for {@code statusDate} and {@code revokeDate} entries (ISO-8601 recommended) + * @return map with keys {@code scope}, {@code typeIdentifier}, {@code status}, {@code statusDate}, and {@code revokeDate} */ @XmlTransient public Map toMap(DateFormat dateFormat) { 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 090fcce18..6a81561e8 100644 --- a/api/src/main/java/org/apache/unomi/api/ContextRequest.java +++ b/api/src/main/java/org/apache/unomi/api/ContextRequest.java @@ -77,7 +77,7 @@ public class ContextRequest { private String publicApiKey; /** - * Retrieves the source of the context request. + * Source item for this context request. * * @return the source */ @@ -114,7 +114,7 @@ public void setRequireSegments(boolean requireSegments) { } /** - * Retrieves the list of profile properties the context server should return with its context response. + * Profile property names the client wants in the response. * * @return the required profile properties the client requested to be returned with the response * @see ContextResponse#getProfileProperties() @@ -133,7 +133,7 @@ public void setRequiredProfileProperties(List requiredProfileProperties) } /** - * Retrieves the list of session properties the context server should return with its context response. + * Session property names the client wants in the response. * * @return the required session properties the client requested to be returned with the response * @see ContextResponse#getSessionProperties() @@ -152,26 +152,25 @@ public void setRequiredSessionProperties(List requiredSessionProperties) } /** - * Specifies whether the profiles scores should be part of the ContextResponse. - * @return a boolean indicating if the scores should be part of the response. + * Whether profile scores should be included in the response. + * + * @return {@code true} if scores should be returned */ public boolean isRequireScores() { return requireScores; } /** - * Setting this value to true indicates that the profile scores should be included in the response. By default this - * value is false. - * @param requireScores set to true if you want the scores to be part of the context response + * Sets whether profile scores should be included in the response. + * + * @param requireScores {@code true} to include scores in the context response */ public void setRequireScores(boolean requireScores) { this.requireScores = requireScores; } /** - * Retrieves the filters aimed at content personalization that should be evaluated for the given session and/or profile so that the context server can tell the client - * whether the content associated with the filter should be activated for this profile/session. The filter identifier is used in the {@link ContextResponse} with the - * associated evaluation result. + * Content personalization filters to evaluate for the current session and profile. * * @return the filters aimed at content personalization that should be evaluated for the given session and/or profile * @see ProfileService#matchCondition(Condition, Profile, Session) Details on how the filter conditions are evaluated @@ -209,7 +208,7 @@ public void setPersonalizations(List events) { } /** - * Retrieves the profile overrides. + * Temporary profile overrides for impersonation or preview. * * @return the profile overrides */ @@ -246,7 +245,7 @@ public void setProfileOverrides(Profile overrides) { } /** - * Retrieves the session properties overrides. + * Temporary session property overrides for impersonation or preview. * * @return the session properties overrides */ @@ -264,10 +263,9 @@ public void setSessionPropertiesOverrides(Map sessionPropertiesO } /** - * Retrieve the sessionId passed along with the request. All events will be processed with this sessionId as a - * default + * Session identifier applied as the default for all events in this request. * - * @return the identifier for the session + * @return the session identifier */ public String getSessionId() { return sessionId; @@ -284,10 +282,9 @@ public void setSessionId(String sessionId) { } /** - * Retrieve the profileId passed along with the request. All events will be processed with this profileId as a - * default + * Profile identifier applied as the default for all events in this request. * - * @return the identifier for the profile + * @return the profile identifier */ public String getProfileId() { return profileId; @@ -321,7 +318,8 @@ public void setClientId(String clientId) { } /** - * Gets the public API key used for tenant authentication. + * Public API key used for tenant authentication. + * * @return the public API key */ public String getPublicApiKey() { @@ -330,7 +328,8 @@ public String getPublicApiKey() { /** * Sets the public API key used for tenant authentication. - * @param publicApiKey the public API key to set + * + * @param publicApiKey the public API key */ public void setPublicApiKey(String publicApiKey) { this.publicApiKey = publicApiKey; 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 aa6a00c18..b6e57c341 100644 --- a/api/src/main/java/org/apache/unomi/api/ContextResponse.java +++ b/api/src/main/java/org/apache/unomi/api/ContextResponse.java @@ -63,9 +63,9 @@ public class ContextResponse implements Serializable { private TraceNode requestTracing; /** - * Retrieves the profile identifier associated with the profile of the user on behalf of which the client performed the context request. + * Profile identifier for the user on whose behalf the context request was made. * - * @return the profile identifier associated with the profile of the active user + * @return the profile identifier */ public String getProfileId() { return profileId; @@ -81,7 +81,7 @@ public void setProfileId(String profileId) { } /** - * Retrieves the session identifier associated with the processed request. + * Session identifier for the processed request. * * @return the session identifier associated with the processed request * @see Session @@ -100,7 +100,7 @@ public void setSessionId(String sessionId) { } /** - * Retrieves the profile properties that were requested by the client. + * Profile properties requested by the client. * * @return the profile properties that were requested by the client * @see ContextRequest#getRequiredProfileProperties() @@ -119,7 +119,7 @@ public void setProfileProperties(Map profileProperties) { } /** - * Retrieves the session properties that were requested by the client. + * Session properties requested by the client. * * @return the session properties that were requested by the client * @see ContextRequest#getRequiredSessionProperties() @@ -138,7 +138,7 @@ public void setSessionProperties(Map sessionProperties) { } /** - * Retrieves the identifiers of the profile segments associated with the user if they were requested by the client. Note that these segments are evaluated taking potential + * Profile segment identifiers for the user if they were requested by the client. Note that these segments are evaluated taking potential * overrides as requested by the client or as a result of evaluating overridden properties. * * @return the profile segments associated with the user accounting for potential overrides @@ -157,23 +157,25 @@ public void setProfileSegments(Set profileSegments) { } /** - * Retrieve the current scores for the profile if they were requested in the request using the requireScores boolean. - * @return a map that contains the score identifier as the key and the score value as the value + * Profile scores when requested via {@link ContextRequest#isRequireScores()}. + * + * @return map of scoring identifier to score value */ public Map getProfileScores() { return profileScores; } /** - * Stores the scores for the current profile if requested using the requireScores boolean in the request. - * @param profileScores a map that contains the score identifier as the key and the score value as the value + * Sets profile scores for the response. + * + * @param profileScores map of scoring identifier to score value */ public void setProfileScores(Map profileScores) { this.profileScores = profileScores; } /** - * Retrieves the results of the evaluation content filtering definitions and whether individual definitions match with the associated profile (potentially modified by + * Content filtering evaluation results and whether individual definitions match with the associated profile (potentially modified by * overridden values). * * @return a Map associating the filter identifier as key to its evaluation result by the context server @@ -193,9 +195,9 @@ public void setFilteringResults(Map filteringResults) { /** - * Returns the number of events processed in this request. + * Number of events processed in this request. * - * @return the number of events processed in this request + * @return the processed event count */ public int getProcessedEvents() { return processedEvents; @@ -211,8 +213,9 @@ public void setProcessedEvents(int processedEvents) { } /** - * @deprecated personalizations results are more complex since 2.1.0 and they are now available under: getPersonalizationResults() - * @return the personalization results map + * @deprecated Personalization results are more complex since 2.1.0; use {@link #getPersonalizationResults()} instead. + * + * @return the legacy personalization results map */ @Deprecated public Map> getPersonalizations() { @@ -220,8 +223,9 @@ public Map> getPersonalizations() { } /** - * @deprecated personalizations results are more complex since 2.1.0 and they are now available under: setPersonalizationResults() - * @param personalizations the personalization results + * @deprecated Personalization results are more complex since 2.1.0; use {@link #setPersonalizationResults(Map)} instead. + * + * @param personalizations the legacy personalization results */ @Deprecated public void setPersonalizations(Map> personalizations) { @@ -229,8 +233,9 @@ public void setPersonalizations(Map> personalizations) { } /** - * Get the result of the personalization resolutions done during the context request. - * @return a Map key/value pair (key:personalization id, value:the result that contains the matching content ids and additional information) + * Personalization resolution results from the context request. + * + * @return map of personalization id to resolution result */ public Map getPersonalizationResults() { return personalizationResults; @@ -246,12 +251,11 @@ public void setPersonalizationResults(Map persona } /** - * Retrieves the tracked conditions, if any, associated with the source of the context request that resulted in this ContextResponse. Upon evaluating the incoming request, - * the context server will determine if there are any rules marked with the "trackedCondition" tag and which source condition matches the source of the incoming request and - * return these tracked conditions to the client that can use them to know that the context server can react to events matching the tracked condition and coming from that - * source. This is, in particular, used to implement form mapping (a solution that allows clients to update user profiles based on values provided when a form is submitted). + * Tracked conditions associated with the request source. + *

+ * Rules tagged with {@code trackedCondition} whose source condition matches the incoming + * request source are returned so clients can emit matching events (for example form mapping). * - * TODO: trackedCondition should be a constant, possibly on the Tag class? * * @return the tracked conditions * @see ContextRequest#getSource() @@ -271,43 +275,43 @@ public void setTrackedConditions(Set trackedConditions) { } /** - * Retrieves the current status of anonymous browsing, as set by the privacy service - * @return anonymous browsing status + * Whether anonymous browsing is enabled, as set by the privacy service. + * + * @return {@code true} if anonymous browsing is active */ public boolean isAnonymousBrowsing() { return anonymousBrowsing; } /** - * Set the user anonymous browsing status - * @param anonymousBrowsing new value for anonymousBrowsing + * Sets the anonymous browsing status. + * + * @param anonymousBrowsing {@code true} to enable anonymous browsing */ public void setAnonymousBrowsing(boolean anonymousBrowsing) { this.anonymousBrowsing = anonymousBrowsing; } /** - * Retrieves the map of consents for the current profile. - * @return a Map where the key is the name of the consent identifier, and the value is a consent object that - * contains all the consent data such as whether the consent was granted or deny, the date of granting/denying - * the date at which the consent will be revoked automatically. + * Consent map for the current profile, keyed by consent identifier. + * + * @return map of consent identifier to consent details */ public Map getConsents() { return consents; } /** - * Sets the map of consents for the current profile. - * @param consents a Map where the key is the name of the consent identifier, and the value is a consent object that - * contains all the consent data such as whether the consent was granted or deny, the date of granting/denying - * the date at which the consent will be revoked automatically. + * Sets the consent map for the current profile. + * + * @param consents map of consent identifier to consent details */ public void setConsents(Map consents) { this.consents = consents; } /** - * Returns the request tracing data. + * Request tracing tree for this context evaluation. * * @return the request tracing data */ 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 7c471ada8..1bd6b0b8f 100644 --- a/api/src/main/java/org/apache/unomi/api/CustomItem.java +++ b/api/src/main/java/org/apache/unomi/api/CustomItem.java @@ -36,13 +36,13 @@ public class CustomItem extends Item { private Map properties = new HashMap(); /** - * Instantiates a new Custom item. + * Default constructor. */ public CustomItem() { } /** - * Instantiates a new Custom item. + * Creates a custom item with the given identifier and item type. * * @param itemId the item id * @param itemType the item type @@ -53,9 +53,9 @@ public CustomItem(String itemId, String itemType) { } /** - * Retrieves this CustomItem's properties. + * Property map for this custom item. * - * @return a Map of the item's properties associating the property name as key to its value. + * @return a map of property names to values */ public Map getProperties() { return properties; diff --git a/api/src/main/java/org/apache/unomi/api/Event.java b/api/src/main/java/org/apache/unomi/api/Event.java index e6a87285c..988e11cda 100644 --- a/api/src/main/java/org/apache/unomi/api/Event.java +++ b/api/src/main/java/org/apache/unomi/api/Event.java @@ -72,13 +72,13 @@ public class Event extends Item implements TimestampedItem { private transient Map attributes; /** - * Instantiates a new Event. + * Default constructor. */ public Event() { } /** - * Instantiates a new Event. + * Creates an event with the given identifier and context fields. * * @param itemId the event item id identifier * @param eventType the event type identifier @@ -95,7 +95,7 @@ public Event(String itemId, String eventType, Session session, Profile profile, } /** - * Instantiates a new Event. + * Creates an event with a generated identifier and the given context fields. * * @param eventType the event type identifier * @param session the session associated with the event @@ -110,7 +110,7 @@ public Event(String eventType, Session session, Profile profile, String scope, I } /** - * Instantiates a new Event. + * Creates an event with a generated identifier, optional properties, and persistence flag. * * @param eventType the event type identifier * @param session the session associated with the event @@ -127,7 +127,7 @@ public Event(String eventType, Session session, Profile profile, String scope, I } /** - * Instantiates a new Event. + * Creates an event with the given identifier, properties, and persistence flag. * * @param itemId the event item id identifier * @param eventType the event type identifier @@ -172,7 +172,7 @@ private void initEvent(String eventType, Session session, Profile profile, Strin } /** - * Retrieves the session identifier if available. + * Session identifier linked to this event, if known. * * @return the session identifier or {@code null} if unavailable */ @@ -181,7 +181,8 @@ public String getSessionId() { } /** - * Set the session id + * Sets the session identifier. + * * @param sessionId the session id */ public void setSessionId(String sessionId) { @@ -189,7 +190,7 @@ public void setSessionId(String sessionId) { } /** - * Retrieves the profile identifier of the Profile associated with this event + * Profile identifier associated with this event. * * @return the profile id */ @@ -207,7 +208,7 @@ public void setProfileId(String profileId) { } /** - * Retrieves the event type. + * Event type identifier (verb of the event sentence). * * @return the event type */ @@ -216,7 +217,8 @@ public String getEventType() { } /** - * Sets the event type + * Sets the event type. + * * @param eventType the event type */ public void setEventType(String eventType) { @@ -224,7 +226,7 @@ public void setEventType(String eventType) { } /** - * Retrieves the event time stamp + * Timestamp when this event occurred. * * @return the event time stamp */ @@ -233,14 +235,16 @@ public Date getTimeStamp() { } /** - * @param timeStamp set the time stamp + * Sets the event timestamp. + * + * @param timeStamp the event timestamp */ public void setTimeStamp(Date timeStamp) { this.timeStamp = timeStamp; } /** - * Retrieves the profile. + * Profile associated with this event (transient, not serialized). * * @return the profile */ @@ -259,7 +263,7 @@ public void setProfile(Profile profile) { } /** - * Retrieves the session. + * Session associated with this event (transient, not serialized). * * @return the session */ @@ -297,7 +301,7 @@ public void setPersistent(boolean persistent) { } /** - * Retrieves the attributes. Attributes are not serializable, and can be used to provide additional contextual objects such as HTTP request or response objects, etc... + * Non-serializable contextual attributes (for example HTTP request or response objects). * * @return the attributes */ @@ -307,7 +311,8 @@ public Map getAttributes() { } /** - * Sets the map of attribues + * Sets the non-serializable attribute map. + * * @param attributes the attributes map */ public void setAttributes(Map attributes) { @@ -328,7 +333,7 @@ public void setProperty(String name, Object value) { } /** - * Retrieves the value of the property identified by the specified name. + * Value of the named event property. * * @param name the name of the property to be retrieved * @return the value of the property identified by the specified name @@ -338,10 +343,10 @@ public Object getProperty(String name) { } /** - * Retrieves the value of the nested property identified by the specified name. + * Value of a nested property, using dot-separated path segments. * - * @param name the name of the property to be retrieved, splited in the nested properties with "." - * @return the value of the property identified by the specified name + * @param name the property path, split on {@code "."} for nested access + * @return the nested value, or {@code null} if any segment is missing */ public Object getNestedProperty(String name) { if (!name.contains(".")) { @@ -362,7 +367,7 @@ public Object getNestedProperty(String name) { } /** - * Retrieves the properties. + * All event properties. * * @return the properties */ @@ -380,23 +385,25 @@ public void setProperties(Map properties) { } /** - * Retrieves the flattened properties - * @return the flattened properties. + * Flattened property map used for indexing and search. + * + * @return the flattened properties */ public Map getFlattenedProperties() { return flattenedProperties; } /** - * Set the flattened properties for current event - * @param flattenedProperties the properties + * Sets the flattened properties map used for indexing and search. + * + * @param flattenedProperties the flattened properties */ public void setFlattenedProperties(Map flattenedProperties) { this.flattenedProperties = flattenedProperties; } /** - * Retrieves the source. + * Source item of this event (subject of the event sentence). * * @return the source */ @@ -414,7 +421,7 @@ public void setSource(Item source) { } /** - * Retrieves the target. + * Target item of this event (object of the event sentence), if any. * * @return the target */ @@ -432,7 +439,7 @@ public void setTarget(Item target) { } /** - * Retrieves the action post executors for this event, if extra actions need to be executed after all Rule-triggered actions have been processed + * Post-executors to run after all rule-triggered actions complete. * * @return the action post executors */ diff --git a/api/src/main/java/org/apache/unomi/api/EventInfo.java b/api/src/main/java/org/apache/unomi/api/EventInfo.java index df2936115..f9d6410a5 100644 --- a/api/src/main/java/org/apache/unomi/api/EventInfo.java +++ b/api/src/main/java/org/apache/unomi/api/EventInfo.java @@ -18,9 +18,9 @@ package org.apache.unomi.api; /** - * Summary of one event type known to the server. - * Pairs an event type name with an occurrence count, for example when - * {@link ServerInfo} lists which events the running instance supports. + * One event type entry in {@link ServerInfo#getEventTypes()}. + * Pairs the event type name with how many matching events exist on the server, + * giving operators and clients a quick view of which event types are active. */ public class EventInfo { @@ -28,38 +28,42 @@ public class EventInfo { private Long occurences; /** - * Constructs a new {@code EventInfo} object. + * Creates an empty event info record. */ public EventInfo() { } /** - * Returns the name of the event. - * @return The event name as a {@link String}. + * Event type name. + * + * @return event type name */ public String getName() { return name; } /** - * Sets the name of the event. - * @param name The name to set for the event. + * Sets the event type name. + * + * @param name event type name */ public void setName(String name) { this.name = name; } /** - * Gets the number of occurrences for this event type. - * @return The count of occurrences as a {@link Long}. + * Number of occurrences for this event type. + * + * @return occurrence count */ public Long getOccurences() { return occurences; } /** - * Sets the number of occurrences for this event type. - * @param occurences The count of occurrences to set. + * Sets the occurrence count for this event type. + * + * @param occurences occurrence count */ public void setOccurences(Long occurences) { this.occurences = occurences; diff --git a/api/src/main/java/org/apache/unomi/api/EventProperty.java b/api/src/main/java/org/apache/unomi/api/EventProperty.java index b7c588f03..aa43d71dc 100644 --- a/api/src/main/java/org/apache/unomi/api/EventProperty.java +++ b/api/src/main/java/org/apache/unomi/api/EventProperty.java @@ -32,26 +32,26 @@ public class EventProperty implements Serializable { private String valueType = "string"; /** - * Initializes an instance of this class. + * Default constructor. */ public EventProperty() { super(); } /** - * Initializes an instance of an event property with the string value type. + * Creates an event property with the default string value type. * - * @param id the event property id + * @param id the property identifier */ public EventProperty(String id) { this(id, null); } /** - * Initializes an instance of this class. + * Creates an event property with the given identifier and value type. * - * @param id the event property id - * @param type the type of the value for this property + * @param id the property identifier + * @param type the value type for this property */ public EventProperty(String id, String type) { this(); @@ -62,27 +62,27 @@ public EventProperty(String id, String type) { } /** - * Retrieves the identifier for this EventProperty. + * Property identifier. * - * @return the identifier for this EventProperty + * @return the property id */ public String getId() { return id; } /** - * Sets the identifier. + * Sets the property identifier. * - * @param id the id + * @param id the property id */ public void setId(String id) { this.id = id; } /** - * Retrieves the type. + * Value type for this property (for example {@code string} or {@code integer}). * - * @return the value type + * @return the value type id */ public String getValueType() { return valueType; @@ -91,7 +91,7 @@ public String getValueType() { /** * Sets the value type. * - * @param type the type + * @param type the value type id */ public void setValueType(String type) { this.valueType = type; diff --git a/api/src/main/java/org/apache/unomi/api/EventSource.java b/api/src/main/java/org/apache/unomi/api/EventSource.java index d43ee3143..df64dd14e 100644 --- a/api/src/main/java/org/apache/unomi/api/EventSource.java +++ b/api/src/main/java/org/apache/unomi/api/EventSource.java @@ -18,11 +18,10 @@ package org.apache.unomi.api; /** - * TODO: REMOVE - * Describes where an event originated in the digital experience. - * Identifies the scope, item id, path, and type of the source page or item - * that triggered the event. Clients attach this information when they send - * events to Unomi. + * Origin of an {@link Event} in the visitor journey. + * Captures scope, item id, path, and type for the page or asset that produced + * the event. Client trackers populate this object when posting to the events + * collector so rules and analytics can attribute actions to a source context. */ public class EventSource { private String scope; @@ -31,72 +30,78 @@ public class EventSource { private String type; /** - * Constructs a new EventSource instance. + * Creates an empty event source. */ public EventSource() { } /** - * Returns the scope associated with this event source. - * @return The scope string. + * Scope of the page or asset that originated the event. + * + * @return scope name */ public String getScope() { return scope; } /** - * Sets the scope for this event source. - * @param scope the new scope to set. + * Sets the source scope. + * + * @param scope scope name */ public void setScope(String scope) { this.scope = scope; } /** - * Returns the unique identifier of this event source. - * @return The ID string. + * Identifier of the originating page or asset within the scope. + * + * @return source item id */ public String getId() { return id; } /** - * Sets the unique identifier for this event source. - * @param id the new ID to set. + * Sets the source item id. + * + * @param id source item id */ public void setId(String id) { this.id = id; } /** - * Returns the path associated with this event source. - * @return The path string. + * URL or logical path of the page where the event was triggered. + * + * @return source path */ public String getPath() { return path; } /** - * Sets the path for this event source. - * @param path the new path to set. + * Sets the source path. + * + * @param path source path */ public void setPath(String path) { this.path = path; } /** - * Returns the type identifier of this event source. - * @return The type string. + * Type label for the source object (for example page, form, or product). + * + * @return source type */ public String getType() { return type; } /** - * Sets the type of this event source. - * This value can be used to classify the data structure or content - * represented by the event source, such as "string", "array", or "boolean". - * @param type The string representation of the desired type. + * Sets the source type label. + * + * @param type source type */ public void setType(String type) { this.type = type; diff --git a/api/src/main/java/org/apache/unomi/api/EventTarget.java b/api/src/main/java/org/apache/unomi/api/EventTarget.java index 8a172aa50..2a5c68d31 100644 --- a/api/src/main/java/org/apache/unomi/api/EventTarget.java +++ b/api/src/main/java/org/apache/unomi/api/EventTarget.java @@ -21,11 +21,10 @@ import java.util.Map; /** - * TODO: REMOVE - * Describes what an event acted upon. - * Holds the target item id and type plus optional properties, for example - * the product or content element a user clicked. Used together with - * {@link Event} to give rules and analytics full context. + * Subject of an {@link Event} — what the visitor interacted with. + * Stores the target item id and type plus optional properties (for example the + * product or content element clicked). Together with {@link EventSource} it + * gives rules enough context to personalize, segment, and report on behavior. */ public class EventTarget implements Serializable { private static final long serialVersionUID = 6370790894348364803L; @@ -34,15 +33,16 @@ public class EventTarget implements Serializable { private Map properties; /** - * Constructs a default {@code EventTarget}. + * Default constructor. */ public EventTarget() { } /** - * Constructs an {@code EventTarget} with the specified identifier and type. - * @param id The unique identifier of the event target. - * @param type The type associated with this event target. + * Creates an event target with the given identifier and type. + * + * @param id the unique identifier of the event target + * @param type the type associated with this event target */ public EventTarget(String id, String type) { this.id = id; @@ -50,8 +50,9 @@ public EventTarget(String id, String type) { } /** - * Returns the unique identifier of this event target. - * @return The {@code String} ID. + * Unique identifier of this event target. + * + * @return the event target id */ public String getId() { return id; @@ -59,40 +60,44 @@ public String getId() { /** * Sets the unique identifier of this event target. - * @param id The new {@code String} ID. + * + * @param id the event target id */ public void setId(String id) { this.id = id; } /** - * Returns the type associated with this event target. - * @return The {@code String} type. + * Type label for this event target. + * + * @return the event target type */ public String getType() { return type; } /** - * Sets the type associated with this event target. - * @param type The new {@code String} type. + * Sets the type label for this event target. + * + * @param type the event target type */ public void setType(String type) { this.type = type; } /** - * Returns a map containing arbitrary properties associated - * with this event target. - * @return The {@link java.util.Map} of properties. + * Arbitrary properties associated with this event target. + * + * @return the property map */ public Map getProperties() { return properties; } /** - * Sets the arbitrary properties associated with this event target. - * @param properties The {@link java.util.Map} of properties to set. + * Sets the arbitrary properties for this event target. + * + * @param properties the property map */ public void setProperties(Map properties) { this.properties = properties; diff --git a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java index 96e69ffc9..49ff69965 100644 --- a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java +++ b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java @@ -20,9 +20,10 @@ import java.util.List; /** - * Payload sent to the events collector endpoint. - * Wraps one or more {@link Event} instances that a client wants Unomi to - * process, evaluate against rules, and persist. + * JSON body accepted by the events collector REST endpoint. + * Bundles one or more {@link Event} instances plus optional {@code sessionId} + * and {@code profileId} hints so Unomi can attach incoming events to the + * correct session and profile before rules run and persistence writes occur. */ public class EventsCollectorRequest { @@ -38,60 +39,64 @@ public class EventsCollectorRequest { private String publicApiKey; /** - * Retrieves the events to be processed. - * @return the events to be processed + * Events submitted for collection and rule evaluation. + * + * @return the event list */ public List getEvents() { return events; } /** - * Sets the list of events to be processed by this request. - * @param events a list containing all events that should be - * collected and processed. + * Sets the events to collect and process. + * + * @param events the events to submit */ public void setEvents(List events) { this.events = events; } /** - * Retrieve the sessionId passed along with the request. All events will be processed with this sessionId as a - * default - * @return the identifier for the session + * Default session id applied to all events in this request when an event does not specify its own. + * + * @return the session id, or {@code null} if none was provided */ public String getSessionId() { return sessionId; } /** - * Sets the sessionId in the request. This is the preferred method of passing along a session identifier with the - * request, as passing it along in the URL can lead to potential security vulnerabilities. - * @param sessionId an unique identifier for the session + * Sets the default session id for events in this request. + * Prefer this over passing the session id in the URL to avoid leaking identifiers in logs or referrers. + * + * @param sessionId the session id */ public void setSessionId(String sessionId) { this.sessionId = sessionId; } /** - * Retrieve the profileId passed along with the request. All events will be processed with this profileId as a - * default - * @return the identifier for the profile + * Default profile id applied to all events in this request when an event does not specify its own. + * + * @return the profile id, or {@code null} if none was provided */ public String getProfileId() { return profileId; } /** - * Sets the profileId in the request. - * @param profileId an unique identifier for the profile + * Sets the default profile id for events in this request. + * + * @param profileId the profile id */ public void setProfileId(String profileId) { this.profileId = profileId; } /** - * Gets the public API key used for tenant authentication. - * @return the public API key + * Public API key used for tenant authentication. + * + * @return the public API key, or {@code null} if none was provided */ public String getPublicApiKey() { return publicApiKey; @@ -99,7 +104,8 @@ public String getPublicApiKey() { /** * Sets the public API key used for tenant authentication. - * @param publicApiKey the public API key to set + * + * @param publicApiKey the public API key */ public void setPublicApiKey(String publicApiKey) { this.publicApiKey = publicApiKey; diff --git a/api/src/main/java/org/apache/unomi/api/ExecutionContext.java b/api/src/main/java/org/apache/unomi/api/ExecutionContext.java index 7506a31e5..93679f099 100644 --- a/api/src/main/java/org/apache/unomi/api/ExecutionContext.java +++ b/api/src/main/java/org/apache/unomi/api/ExecutionContext.java @@ -27,23 +27,21 @@ * enforce multi-tenant isolation and authorization. */ public class ExecutionContext { - /** The constant string used to identify the system tenant. */ + /** Identifier of the system tenant, used for cross-tenant administration. */ public static final String SYSTEM_TENANT = "system"; - + private String tenantId; private Set roles = new HashSet<>(); private Set permissions = new HashSet<>(); private Stack tenantStack = new Stack<>(); private boolean isSystem = false; - + /** - * Constructs a new execution context with specified - * security and tenant details. - * @param tenantId The unique identifier of the tenant for this context. - * @param roles A set of roles assigned to the user in this - * context. Can be null. - * @param permissions A set of explicit permissions granted to the user in - * this context. Can be null. + * Creates a context for the given tenant, roles, and permissions. + * + * @param tenantId tenant id + * @param roles roles assigned in this context, or {@code null} + * @param permissions explicit permissions granted in this context, or {@code null} */ public ExecutionContext(String tenantId, Set roles, Set permissions) { this.tenantId = tenantId; @@ -57,59 +55,58 @@ public ExecutionContext(String tenantId, Set roles, Set permissi this.permissions.addAll(permissions); } } - + /** - * Creates and returns a dedicated execution context - * representing the system tenant. - * @return An {@link ExecutionContext} instance configured - * for the system tenant. + * Returns a context for the system tenant. + * + * @return system execution context */ public static ExecutionContext systemContext() { ExecutionContext context = new ExecutionContext(SYSTEM_TENANT, null, null); context.isSystem = true; return context; } - + /** - * Retrieves the unique identifier of the current tenant - * associated with this context. - * @return The tenant ID string. + * Active tenant id. + * + * @return tenant id */ public String getTenantId() { return tenantId; } - + /** - * Returns a copy of the set of roles assigned to this execution context. - * @return A new {@link Set} containing a copy of all assigned roles. + * Roles assigned to this context. + * + * @return copy of the role set */ public Set getRoles() { return new HashSet<>(roles); } - + /** - * Returns a copy of the set of permissions granted to - * this execution context. - * @return A new {@link Set} containing a copy of all explicit permissions. + * Permissions granted to this context. + * + * @return copy of the permission set */ public Set getPermissions() { return new HashSet<>(permissions); } - + /** - * Checks if this execution context is designated as the system context. - * @return true if the context represents the system - * tenant, false otherwise. + * Whether this context represents the system tenant. + * + * @return {@code true} for the system tenant */ public boolean isSystem() { return isSystem; } - + /** - * Sets a new tenant ID for the current execution context. This operation - * saves the previous tenant ID onto an internal stack and updates the - * system status accordingly. - * @param tenantId The new tenant identifier to set. + * Switches to a new tenant, saving the previous tenant on an internal stack. + * + * @param tenantId new tenant id */ public void setTenant(String tenantId) { tenantStack.push(this.tenantId); @@ -118,9 +115,7 @@ public void setTenant(String tenantId) { } /** - * Restores the execution context to the previously active tenant. - * If a previous tenant was set, this method pops that tenant ID from the - * internal stack and updates the current context state accordingly. + * Restores the tenant saved by the most recent {@link #setTenant(String)} call. */ public void restorePreviousTenant() { if (!tenantStack.isEmpty()) { @@ -128,50 +123,44 @@ public void restorePreviousTenant() { this.isSystem = SYSTEM_TENANT.equals(this.tenantId); } } - + /** - * Validates whether the current execution context has sufficient - * permissions to perform a given operation. - * If the context is system-level, validation passes immediately. Otherwise, - * it checks if the required permission exists in the context's - * granted permissions. - * @param operation The specific operation name required - * for access validation. - * @throws SecurityException if the current context lacks the necessary - * permission for the given operation. + * Checks that this context may perform the given operation. + * System contexts always pass. Otherwise the operation name must be present + * in the granted permissions. + * + * @param operation operation name to validate + * @throws SecurityException if the permission is missing */ public void validateAccess(String operation) { if (isSystem) { return; } - + if (!hasPermission(operation)) { throw new SecurityException("Access denied: Missing permission for operation " + operation + " for tenant " + tenantId + " and roles " + roles); } } - + /** - * Checks if the current execution context possesses a specific permission. - * The context is considered to have the permission if it is system-level or - * if the permission name is explicitly listed in the context's - * granted permissions. - * @param permission The name of the permission to check for. - * @return true if the context is system-level or has been granted the - * permission, false otherwise. + * Whether this context has the given permission. + * System contexts always return {@code true}. + * + * @param permission permission name + * @return {@code true} if the permission is granted */ public boolean hasPermission(String permission) { return isSystem || permissions.contains(permission); } - + /** - * Checks if the current execution context possesses a specific role. - * The context is considered to have the role if it is system-level or if - * the role name is explicitly listed in the context's granted roles. - * @param role The name of the role to check for. - * @return true if the context is system-level or has been granted the - * role, false otherwise. + * Whether this context has the given role. + * System contexts always return {@code true}. + * + * @param role role name + * @return {@code true} if the role is granted */ public boolean hasRole(String role) { return isSystem || roles.contains(role); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/GeoPoint.java b/api/src/main/java/org/apache/unomi/api/GeoPoint.java index e7481d2db..77a1ebe97 100644 --- a/api/src/main/java/org/apache/unomi/api/GeoPoint.java +++ b/api/src/main/java/org/apache/unomi/api/GeoPoint.java @@ -37,7 +37,7 @@ public class GeoPoint { private Double lon; /** - * Instantiates a new GeoPoint + * Creates a geo point at the given coordinates. * * @param lat latitude of the geo point * @param lon longitude of the geo point @@ -48,7 +48,7 @@ public GeoPoint(Double lat, Double lon) { } /** - * Retrieves latitude of the geo point + * Latitude of this geo point. * * @return geo point latitude */ @@ -57,7 +57,7 @@ public Double getLat() { } /** - * Retrieves longitude of the geo point + * Longitude of this geo point. * * @return geo point longitude */ @@ -100,11 +100,11 @@ public double distanceTo(final GeoPoint other) { } /** - * Instantiates geo point from map of coordinates + * Parses a geo point from a coordinate map with {@code lat} and {@code lon} keys. * - * @param map Map containing coordinates with keys "lat" and "lon" - * @return New geo point or null if map is not a valid geo point - * @throws IllegalArgumentException Thrown if the input is not valid + * @param map map containing coordinates with keys {@code lat} and {@code lon} + * @return a new geo point + * @throws IllegalArgumentException if the map is null, empty, or missing required keys */ public static GeoPoint fromMap(Map map) { if (map == null || map.isEmpty() || !map.containsKey("lat") || !map.containsKey("lon")) { @@ -114,11 +114,11 @@ public static GeoPoint fromMap(Map map) { } /** - * Instantiates geo point from string representation + * Parses a geo point from a {@code "lat, lon"} string. * - * @param input String geo point representation in the following format: "lat, lon" - * @return New geo point or null if string is not a valid geo point - * @throws IllegalArgumentException Thrown if the input is not valid + * @param input string geo point representation in the format {@code "lat, lon"} + * @return a new geo point, or {@code null} if the input is blank + * @throws IllegalArgumentException if the input cannot be parsed */ public static GeoPoint fromString(final String input) { if (input == null || input.trim().length() == 0) { 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 44ff53b0f..b8e63d4f2 100644 --- a/api/src/main/java/org/apache/unomi/api/Item.java +++ b/api/src/main/java/org/apache/unomi/api/Item.java @@ -51,13 +51,11 @@ public abstract class Item implements Serializable, YamlConvertible { private static final Map itemTypeCache = new ConcurrentHashMap<>(); /** - * Retrieves the type string for a given class by attempting to read a - * public static String constant named {@code ITEM_TYPE} from that class. - * This method caches the result based on the provided class to improve - * performance during repeated calls. - * @param clazz The Class object whose item type is required. - * @return A string representation of the Item's type, or null if the - * {@code ITEM_TYPE} field cannot be found or accessed. + * Resolves the item type string from a class's {@code ITEM_TYPE} constant. + * Results are cached per class. + * + * @param clazz item class + * @return item type, or {@code null} if {@code ITEM_TYPE} is missing or inaccessible */ public static String getItemType(Class clazz) { String itemType = itemTypeCache.get(clazz); @@ -77,28 +75,27 @@ public static String getItemType(Class clazz) { } /** - * Protected field representing the unique identifier used to identify this - * Item when persisted or referenced. + * Unique id used when this item is persisted or referenced. + * Must be unique among items of the same {@link #itemType}. */ protected String itemId; /** - * Protected field storing the type of this Item. This value is typically - * derived from a constant defined within the implementing class. + * Persistence type string for this item. + * Subclasses must define a public {@code ITEM_TYPE} constant with this value. */ protected String itemType; /** - * Protected field defining the scope of this Item, allowing related items - * to be grouped together during processing or retrieval. + * Scope that groups related items (often one analyzed site). + * Used by clients to filter data returned from the context server. */ protected String scope; /** - * Protected field storing the current version number of this Item. This is - * used for tracking changes over time. + * Optimistic-locking version, incremented when the item is updated. */ protected Long version; /** - * A protected map used to store additional, non-core metadata about this - * Item using string keys and arbitrary object values. + * Server-managed metadata keyed by string. + * Stores values that are not part of the item's core properties. */ protected Map systemMetadata = new HashMap<>(); private String tenantId; @@ -112,10 +109,9 @@ public static String getItemType(Class clazz) { private Date lastSyncDate; /** - * Default constructor that initializes the Item's type by calling - * {@link #getItemType(Class)} and sets up initial audit metadata fields. - * If the implementing class does not provide a public static String - * constant named {@code ITEM_TYPE}, an error is logged. + * Initializes {@link #itemType} from the subclass {@code ITEM_TYPE} constant + * and sets default audit metadata ({@link #creationDate}, {@link #version}). + * Logs an error when {@code ITEM_TYPE} is missing on the concrete class. */ public Item() { this.itemType = getItemType(this.getClass()); @@ -126,9 +122,9 @@ public Item() { } /** - * Constructor that initializes the Item using the default mechanism and - * explicitly sets the item's unique identifier. - * @param itemId The unique ID to assign to this Item upon creation. + * Creates an item with the given id. + * + * @param itemId item id */ public Item(String itemId) { this(); @@ -142,50 +138,56 @@ private void initializeAuditMetadata() { } /** - * Retrieves the Item's identifier used to uniquely identify this Item when persisted or when referred to. An Item's identifier must be unique among Items with the same type. - * @return a String representation of the identifier, no particular format is prescribed as long as it is guaranteed unique for this particular Item. + * Unique id among items of the same type. + * No particular format is required as long as the id is unique for this item type. + * + * @return item id */ public String getItemId() { return itemId; } /** - * Sets the unique identifier for this Item. This ID is used when persisting - * or referencing the item in various contexts (e.g., profiles, sessions). - * @param itemId the unique identifier for the Item + * Sets the item id. + * + * @param itemId item id */ public void setItemId(String itemId) { this.itemId = itemId; } /** - * Retrieves the Item's type used to assert metadata and structure common to Items of this type, notably for persistence purposes. The Item's type must - * match the value defined by the implementation's {@code ITEM_TYPE} public constant. - * @return a String representation of this Item's type, must equal the {@code ITEM_TYPE} value + * Item type used for persistence and metadata. + * Must match the implementing class {@code ITEM_TYPE} constant. + * + * @return item type */ public String getItemType() { return itemType; } /** - * Sets the Item's type. - * @param itemType the Item's type + * Sets the item type. + * + * @param itemType item type */ public void setItemType(String itemType) { this.itemType = itemType; } /** - * Retrieves the Item's scope. - * @return the Item's scope name + * Scope that groups related items. + * + * @return scope name */ public String getScope() { return scope; } /** - * Sets the Item's scope. - * @param scope the Item's scope + * Sets the scope. + * + * @param scope scope name */ public void setScope(String scope) { this.scope = scope; @@ -207,160 +209,175 @@ public int hashCode() { } /** - * Retrieves the current version number of the Item. - * @return a {@link Long} representing the version of this Item. + * Optimistic-locking version. + * + * @return item version */ public Long getVersion() { return version; } /** - * Sets the version number of the Item. This is typically used when updating - * an existing item's state. - * @param version the new version to set + * Sets the item version. + * + * @param version item version */ public void setVersion(Long version) { this.version = version; } /** - * Returns the system metadata for the given key. - * @param key the key - * @return the system metadata for the given key + * Returns system metadata for the given key. + * + * @param key metadata key + * @return metadata value */ 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 + * Sets system metadata for the given key. + * + * @param key metadata key + * @param value metadata value */ public void setSystemMetadata(String key, Object value) { systemMetadata.put(key, value); } /** - * Retrieves the unique identifier of the tenant associated with this Item, - * useful for context management and logging. - * @return the tenant ID as a {@link String}. + * Tenant that owns this item. + * + * @return tenant id */ public String getTenantId() { return tenantId; } /** - * Sets the tenant ID. - * @param tenantId the tenant ID + * Sets the tenant id. + * + * @param tenantId tenant id */ public void setTenantId(String tenantId) { this.tenantId = tenantId; } /** - * Retrieves the identifier of the user or system that originally created - * this Item. This is part of the item's audit metadata. - * @return the creator's ID as a {@link String}. + * User or system that created this item. + * + * @return creator id */ public String getCreatedBy() { return createdBy; } /** - * Sets the created by. - * @param createdBy the created by + * Sets the creator id. + * + * @param createdBy creator id */ public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } /** - * Retrieves the identifier of the user or system that last modified this - * Item. This is part of the item's audit metadata. - * @return the last modifier's ID as a {@link String}. + * User or system that last modified this item. + * + * @return last modifier id */ public String getLastModifiedBy() { return lastModifiedBy; } /** - * Sets the last modified by. - * @param lastModifiedBy the last modified by + * Sets the last modifier id. + * + * @param lastModifiedBy last modifier id */ public void setLastModifiedBy(String lastModifiedBy) { this.lastModifiedBy = lastModifiedBy; } /** - * Returns the date when this item was created. - * @return the date when this item was created + * When this item was created. + * + * @return creation date */ public Date getCreationDate() { return creationDate; } /** - * Sets the date when this item was created. - * @param creationDate the creation date + * Sets the creation date. + * + * @param creationDate 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 + * When this item was last modified. + * + * @return last modification date */ public Date getLastModificationDate() { return lastModificationDate; } /** - * Sets the date when this item was last modified. - * @param lastModificationDate the last modification date + * Sets the last modification date. + * + * @param lastModificationDate last modification date */ public void setLastModificationDate(Date lastModificationDate) { this.lastModificationDate = lastModificationDate; } /** - * Returns the source instance ID. - * @return the source instance ID + * Cluster node that originated this item. + * + * @return source instance id */ public String getSourceInstanceId() { return sourceInstanceId; } /** - * Sets the source instance ID. - * @param sourceInstanceId the source instance ID + * Sets the source instance id. + * + * @param sourceInstanceId source instance id */ public void setSourceInstanceId(String sourceInstanceId) { this.sourceInstanceId = sourceInstanceId; } /** - * Returns the last synchronization date. - * @return the last synchronization date + * When this item was last synchronized from another node. + * + * @return last sync date */ public Date getLastSyncDate() { return lastSyncDate; } /** - * Sets the last synchronization date. - * @param lastSyncDate the last synchronization date + * Sets the last sync date. + * + * @param lastSyncDate last sync date */ public void setLastSyncDate(Date lastSyncDate) { this.lastSyncDate = lastSyncDate; } /** - * Converts this item to a Map structure for YAML output. - * Implements YamlConvertible interface with circular reference detection. - * @param visited set of already visited objects to prevent infinite recursion (may be null) - * @return a Map representation of this item + * Converts this item to a map for YAML output. + * + * @param visited objects already visited while converting (may be {@code null}) + * @param maxDepth remaining recursion depth + * @return map representation of this item */ @Override public Map toYaml(Set visited, int maxDepth) { diff --git a/api/src/main/java/org/apache/unomi/api/Metadata.java b/api/src/main/java/org/apache/unomi/api/Metadata.java index b33647771..ec643e2d8 100644 --- a/api/src/main/java/org/apache/unomi/api/Metadata.java +++ b/api/src/main/java/org/apache/unomi/api/Metadata.java @@ -55,25 +55,27 @@ public class Metadata implements Comparable, Serializable, YamlConvert private boolean readOnly = false; /** - * Instantiates a new Metadata. + * Default constructor. */ public Metadata() { } /** - * Instantiates a new Metadata with the specified identifier. - * @param id the identifier for this Metadata + * Creates metadata with the given identifier. + * + * @param id the metadata identifier */ public Metadata(String id) { this.id = id; } /** - * Instantiates a new Metadata with the provided information. - * @param scope the scope for this Metadata - * @param id the identifier of the associated {@link Item} - * @param name the name - * @param description the description + * Creates metadata with scope, identifier, name, and description. + * + * @param scope the item scope + * @param id the item identifier + * @param name the display name + * @param description the human-readable description */ public Metadata(String scope, String id, String name, String description) { this.scope = scope; @@ -83,23 +85,26 @@ public Metadata(String scope, String id, String name, String description) { } /** - * Retrieves the identifier for the entity associated with this Metadata - * @return the identifier + * Identifier of the item described by this metadata. + * + * @return the item id */ public String getId() { return id; } /** - * Sets the id. - * @param id the id + * Sets the item identifier. + * + * @param id the item id */ public void setId(String id) { this.id = id; } /** - * Retrieves the name. + * Display name shown in administrative UIs. + * * @return the name */ public String getName() { @@ -107,7 +112,8 @@ public String getName() { } /** - * Sets the name. + * Sets the display name. + * * @param name the name */ public void setName(String name) { @@ -115,7 +121,8 @@ public void setName(String name) { } /** - * Retrieves the description. + * Human-readable description of the item. + * * @return the description */ public String getDescription() { @@ -124,6 +131,7 @@ public String getDescription() { /** * Sets the description. + * * @param description the description */ public void setDescription(String description) { @@ -131,8 +139,9 @@ public void setDescription(String description) { } /** - * Retrieves the scope for the entity associated with this Metadata - * @return the scope for the entity associated with this Metadata + * Scope that owns this item. + * + * @return the scope id * @see Item Item for a deeper discussion of scopes */ public String getScope() { @@ -140,32 +149,36 @@ public String getScope() { } /** - * Sets the scope. - * @param scope the scope + * Sets the item scope. + * + * @param scope the scope id */ public void setScope(String scope) { this.scope = scope; } /** - * Retrieves a set of {@link String} tag names associated with this Metadata - * @return a set of {@link String} tag names associated with this Metadata + * User-defined tags for organizing and filtering items. + * + * @return the tag names */ public Set getTags() { return tags; } /** - * Sets the tags. - * @param tags the tag ids + * Sets the user-defined tags. + * + * @param tags the tag names */ public void setTags(Set tags) { this.tags = tags; } /** - * Retrieves a set of {@link String} system tag names associated with this Metadata - * @return a set of {@link String} system tag names associated with this Metadata + * System tags applied by the platform (not editable in UIs). + * + * @return the system tag names */ public Set getSystemTags() { return systemTags; @@ -173,30 +186,34 @@ public Set getSystemTags() { /** * Sets the system tags. - * @param systemTags the system tag ids + * + * @param systemTags the system tag names */ public void setSystemTags(Set systemTags) { this.systemTags = systemTags; } /** - * Whether the associated entity is considered active by the context server, in particular to check if rules need to be created / triggered - * @return {@code true} if the associated entity is enabled, {@code false} otherwise + * Whether the associated item is active and eligible for rule evaluation. + * + * @return {@code true} if enabled, {@code false} otherwise */ public boolean isEnabled() { return enabled; } /** - * Specifies whether the associated entity should be active or not. - * @param enabled {@code true} if the associated entity is enabled, {@code false} otherwise + * Sets whether the associated item is active. + * + * @param enabled {@code true} to enable the item, {@code false} to disable it */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** - * Whether the associated entity is waiting on additional plugins to become available to be able to properly perform its function. + * Whether required OSGi plugins are missing and the item cannot run yet. + * * @return {@code true} if plugins are missing, {@code false} otherwise */ public boolean isMissingPlugins() { @@ -204,7 +221,8 @@ public boolean isMissingPlugins() { } /** - * Specifies whether the associated entity is waiting on additional plugins to become available. + * Sets whether required plugins are missing. + * * @param missingPlugins {@code true} if plugins are missing, {@code false} otherwise */ public void setMissingPlugins(boolean missingPlugins) { @@ -212,44 +230,46 @@ public void setMissingPlugins(boolean missingPlugins) { } /** - * Whether the associated entity is considered for internal purposes only and should therefore be hidden to accessing UIs. - * @return {@code true} if the associated entity needs to be hidden, {@code false} otherwise + * Whether the item should be hidden from administrative UIs. + * + * @return {@code true} if hidden, {@code false} otherwise */ public boolean isHidden() { return hidden; } /** - * Specifies whether the associated entity is hidden. - * @param hidden {@code true} if the associated entity needs to be hidden, {@code false} otherwise + * Sets whether the item is hidden from UIs. + * + * @param hidden {@code true} to hide the item, {@code false} to show it */ public void setHidden(boolean hidden) { this.hidden = hidden; } /** - * Whether the associated entity can be accessed but not modified. - * @return {@code true} if the associated entity can be accessed but not modified, {@code false} otherwise + * Whether the item can be read but not modified. + * + * @return {@code true} if read-only, {@code false} otherwise */ public boolean isReadOnly() { return readOnly; } /** - * Specifies whether the associated entity should be only accessed and not modified. - * @param readOnly {@code true} if the associated entity can be accessed but not modified, {@code false} otherwise + * Sets whether the item is read-only. + * + * @param readOnly {@code true} for read-only access, {@code false} to allow updates */ public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } /** - * Compares this Metadata instance to another based on their identifiers. - * @param o The other {@link Metadata} object to compare against. - * @return An integer representing the comparison result of the IDs: a - * negative value if this ID is less than the specified ID, zero - * if the IDs are equal, or a positive value if this ID is - * greater than the specified ID. + * Compares metadata by item identifier. + * + * @param o the other metadata + * @return a negative, zero, or positive value depending on id ordering */ public int compareTo(Metadata o) { return getId().compareTo(o.getId()); @@ -277,6 +297,7 @@ public int hashCode() { /** * Converts this metadata to a Map structure for YAML output. * Implements YamlConvertible interface with circular reference detection. + * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this metadata */ diff --git a/api/src/main/java/org/apache/unomi/api/MetadataItem.java b/api/src/main/java/org/apache/unomi/api/MetadataItem.java index 9ff4ff0d3..53ffc91ad 100644 --- a/api/src/main/java/org/apache/unomi/api/MetadataItem.java +++ b/api/src/main/java/org/apache/unomi/api/MetadataItem.java @@ -38,16 +38,15 @@ public abstract class MetadataItem extends Item { protected Metadata metadata; /** - * Constructs a default {@code MetadataItem}. + * Default constructor. */ public MetadataItem() { } /** - * Constructs a {@code MetadataItem} initialized with the given metadata. - * The parent class ID is set using the ID from the provided metadata, and - * the internal metadata field is populated. - * @param metadata the initial metadata for this item + * Creates an item with the given metadata and sets the item id from it. + * + * @param metadata the item metadata */ public MetadataItem(Metadata metadata) { super(metadata != null ? metadata.getId() : null); @@ -55,8 +54,9 @@ public MetadataItem(Metadata metadata) { } /** - * Retrieves the associated Metadata. - * @return the associated Metadata + * Descriptive metadata for this item. + * + * @return the metadata */ @XmlElement(name = "metadata") public Metadata getMetadata() { @@ -64,10 +64,9 @@ public Metadata getMetadata() { } /** - * Sets the associated {@link Metadata}. - * If the provided metadata is not null, the parent class's ID will be - * updated to match the ID of the new metadata object. - * @param metadata the metadata to set + * Sets the metadata and updates the item id when metadata is non-null. + * + * @param metadata the metadata to assign */ public void setMetadata(Metadata metadata) { if (metadata != null) { @@ -94,6 +93,7 @@ public String getScope() { * Converts this metadata item to a Map structure for YAML output. * Merges fields from Item parent class and adds metadata field. * Subclasses should override this method, call super.toYaml(visited), and add their specific fields. + * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this metadata item */ diff --git a/api/src/main/java/org/apache/unomi/api/Parameter.java b/api/src/main/java/org/apache/unomi/api/Parameter.java index c43949eb0..c34ad37fc 100644 --- a/api/src/main/java/org/apache/unomi/api/Parameter.java +++ b/api/src/main/java/org/apache/unomi/api/Parameter.java @@ -28,8 +28,11 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue; /** - * A representation of a condition parameter, to be used in the segment building UI to either select parameters from a - * choicelist or to enter a specific value. + * Parameter definition for a {@link org.apache.unomi.api.conditions.ConditionType}. + * Describes how the segment builder UI should collect a value: free text, choice + * list, or nested structure. Saved on condition types and validated by + * {@link org.apache.unomi.api.services.ConditionValidationService} when rules + * or segments are stored. */ public class Parameter implements Serializable, YamlConvertible { @@ -45,17 +48,17 @@ public class Parameter implements Serializable, YamlConvertible { private ConditionValidation validation; /** - * Constructs a default {@link Parameter} instance. + * Default constructor. */ public Parameter() { } /** - * Constructs a {@link Parameter} with specified - * identification and type details. - * @param id The unique identifier for this parameter. - * @param type The data type of the parameter (e.g., "string", "integer"). - * @param multivalued Indicates if the parameter can hold multiple values. + * Creates a parameter with id, type, and multivalued flag. + * + * @param id the parameter identifier + * @param type the parameter data type (for example {@code string} or {@code integer}) + * @param multivalued whether multiple values are allowed */ public Parameter(String id, String type, boolean multivalued) { this.id = id; @@ -64,50 +67,54 @@ public Parameter(String id, String type, boolean multivalued) { } /** - * Returns the unique identifier assigned to this parameter. - * @return The ID string of the parameter. + * Parameter identifier. + * + * @return the parameter id */ public String getId() { return id; } /** - * Sets the unique identifier for this parameter. - * @param id The new ID to assign to the parameter. + * Sets the parameter identifier. + * + * @param id the parameter id */ public void setId(String id) { this.id = id; } /** - * Retrieves the data type associated with this parameter. - * @return The string representation of the parameter's type. + * Parameter data type (for example {@code string}, {@code integer}, or {@code boolean}). + * + * @return the type id */ public String getType() { return type; } /** - * Sets the data type for this parameter. - * @param type The new data type string (e.g., "array", "boolean"). + * Sets the parameter data type. + * + * @param type the type id */ public void setType(String type) { this.type = type; } /** - * Checks if this parameter is configured to handle multiple values. - * @return {@code true} if the parameter is multivalued, - * {@code false} otherwise. + * Whether this parameter accepts multiple values. + * + * @return {@code true} if multivalued, {@code false} otherwise */ public boolean isMultivalued() { return multivalued; } /** - * Sets whether this parameter can hold multiple values. - * @param multivalued If {@code true}, the parameter accepts multiple - * values; otherwise, it accepts only one. + * Sets whether this parameter accepts multiple values. + * + * @param multivalued {@code true} to allow multiple values */ public void setMultivalued(boolean multivalued) { this.multivalued = multivalued; @@ -123,35 +130,36 @@ public void setChoiceListInitializerFilter(String choiceListInitializerFilter) { } /** - * Retrieves the default value configured for this parameter. - * @return The stored default value, which may be null if none is set. + * Default value used when a condition does not supply this parameter. + * + * @return the default value, or {@code null} if none is configured */ public Object getDefaultValue() { return defaultValue; } /** - * Sets the default value that should be used when evaluating conditions - * involving this parameter. This value can be of any type. - * @param defaultValue the object to be set as the default value + * Sets the default value for this parameter. + * + * @param defaultValue the default value */ public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; } /** - * Gets the condition validation rules associated with this parameter. - * @return The {@link ConditionValidation} object defining constraints, or - * null if none is configured. + * Validation rules applied when this parameter is used in a condition. + * + * @return the validation rules, or {@code null} if none are configured */ public ConditionValidation getValidation() { return validation; } /** - * Sets the specific condition validation rules for this parameter. This - * allows controlling how the parameter behaves within a condition context. - * @param validation the new {@link ConditionValidation} object to apply + * Sets validation rules for this parameter. + * + * @param validation the validation rules */ public void setValidation(ConditionValidation validation) { this.validation = validation; @@ -160,6 +168,7 @@ public void setValidation(ConditionValidation validation) { /** * Converts this parameter to a Map structure for YAML output. * Implements YamlConvertible interface. + * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this parameter */ diff --git a/api/src/main/java/org/apache/unomi/api/PartialList.java b/api/src/main/java/org/apache/unomi/api/PartialList.java index afe1c9c2a..f6fe43a44 100644 --- a/api/src/main/java/org/apache/unomi/api/PartialList.java +++ b/api/src/main/java/org/apache/unomi/api/PartialList.java @@ -23,9 +23,11 @@ import java.util.List; /** - * A list of elements representing a limited view of a larger list, starting from a given element (offset from the first) and showing only a given number of elements, instead of - * showing all of them. This is useful to retrieve "pages" of large element collections. - * @param the generic type of contained elements + * Window into a larger result set for paginated queries. + * Carries the current page, offset, total hit count, and optional scroll + * continuation tokens for deep result sets. + * + * @param the element type */ public class PartialList implements Serializable { @@ -51,7 +53,7 @@ public enum Relation { } /** - * Instantiates a new PartialList. + * Default constructor with an empty list and zero counts. */ public PartialList() { list = new ArrayList<>(); @@ -62,12 +64,13 @@ public PartialList() { } /** - * Instantiates a new PartialList. - * @param list the limited view into the bigger List this PartialList is representing - * @param offset the offset of the first element in the view - * @param pageSize the number of elements this PartialList contains - * @param totalSize the total size of elements in the original List - * @param totalSizeRelation the relation to the total size (equals or greater than) + * Creates a partial list view over a full result set. + * + * @param list the page of elements + * @param offset index of the first element in the full set + * @param pageSize number of elements in this page + * @param totalSize total elements in the full set + * @param totalSizeRelation whether {@code totalSize} is exact or a lower bound */ public PartialList(List list, long offset, long pageSize, long totalSize, Relation totalSizeRelation) { this.list = list; @@ -78,74 +81,81 @@ public PartialList(List list, long offset, long pageSize, long totalSize, Rel } /** - * Retrieves the limited list view. - * @return a List of the {@code size} elements starting from the {@code offset}-th one from the original, larger list + * Elements in the current page. + * + * @return the page contents */ public List getList() { return list; } /** - * Sets the view list. - * @param list the view list into the bigger List this PartialList is representing + * Sets the page contents. + * + * @param list the elements for this page */ public void setList(List list) { this.list = list; } /** - * Retrieves the offset of the first element of the view. - * @return the offset of the first element of the view + * Index of the first element in the full result set. + * + * @return the offset */ public long getOffset() { return offset; } /** - * Sets the offset of the first element in this partial list view. - * @param offset the starting index (offset) for the elements in the view. + * Sets the offset of the first element in the full result set. + * + * @param offset the starting index */ public void setOffset(long offset) { this.offset = offset; } /** - * Retrieves the number of elements this PartialList contains. - * @return the number of elements this PartialList contains + * Maximum number of elements requested for this page. + * + * @return the page size */ public long getPageSize() { return pageSize; } /** - * Sets the maximum number of elements to be returned in - * this partial list view. - * @param pageSize the size limit for the page view. + * Sets the page size. + * + * @param pageSize the maximum number of elements per page */ public void setPageSize(long pageSize) { this.pageSize = pageSize; } /** - * Retrieves the total size of elements in the original List. - * @return the total size of elements in the original List + * Total number of matching elements in the full result set. + * + * @return the total size */ public long getTotalSize() { return totalSize; } /** - * Sets the total count of elements available in the original, full list. - * @param totalSize the overall size of the collection represented - * by this PartialList. + * Sets the total number of matching elements. + * + * @param totalSize the total size */ public void setTotalSize(long totalSize) { this.totalSize = totalSize; } /** - * Retrieves the size of this PartialList. Should equal {@link #getPageSize()}. - * @return the size of this PartialList + * Number of elements in the current page (should match {@link #getPageSize()}). + * + * @return the number of elements in {@link #getList()} */ @XmlTransient public int size() { @@ -153,9 +163,10 @@ public int size() { } /** - * Retrieves the element at the specified index - * @param index the index of the element to retrieve - * @return the element at the specified index + * Element at the given index within the current page. + * + * @param index the zero-based index in the page list + * @return the element at that index */ @XmlTransient public T get(int index) { @@ -163,52 +174,54 @@ public T get(int index) { } /** - * Retrieve the scroll identifier to make it possible to continue a scrolling list query - * @return a string containing the scroll identifier, to be sent back in an subsequent request + * Scroll token returned by the search backend for the next page of a scroll query. + * + * @return the scroll identifier, or {@code null} if scrolling is not in use */ public String getScrollIdentifier() { return scrollIdentifier; } /** - * Sets the scroll identifier used to continue a scrolling list query. - * @param scrollIdentifier a string containing the unique identifier needed - * for subsequent requests in a scroll operation. + * Sets the scroll token for continuing a scroll query. + * + * @param scrollIdentifier the scroll identifier */ public void setScrollIdentifier(String scrollIdentifier) { this.scrollIdentifier = scrollIdentifier; } /** - * Retrieve the value of the scroll time validity to make it possible to continue a scrolling list query - * @return a string containing a time value for the scroll validity, to be sent back in a subsequent request + * How long the scroll context remains valid (for example {@code 10m}). + * + * @return the scroll time validity, or {@code null} if not set */ public String getScrollTimeValidity() { return scrollTimeValidity; } /** - * Sets the time validity period for the scroll query. - * @param scrollTimeValidity a string representing how long the scroll - * context remains valid (e.g., "10m"). + * Sets how long the scroll context remains valid. + * + * @param scrollTimeValidity the validity period (for example {@code 10m}) */ public void setScrollTimeValidity(String scrollTimeValidity) { this.scrollTimeValidity = scrollTimeValidity; } /** - * Retrieve the relation to the total site, wether it is equal to or greater than the value stored in the - * totalSize property. - * @return a Relation enum value that describes the type of total size we have in this object. + * Whether {@link #getTotalSize()} is exact or only a lower bound. + * + * @return the total-size relation */ public Relation getTotalSizeRelation() { return totalSizeRelation; } /** - * Sets the relation describing the total size count. - * @param totalSizeRelation the relationship type between the total size and - * the actual data set. + * Sets whether the reported total size is exact or a lower bound. + * + * @param totalSizeRelation the total-size relation */ public void setTotalSizeRelation(Relation totalSizeRelation) { this.totalSizeRelation = totalSizeRelation; diff --git a/api/src/main/java/org/apache/unomi/api/Patch.java b/api/src/main/java/org/apache/unomi/api/Patch.java index c703ef2a8..1290452cf 100644 --- a/api/src/main/java/org/apache/unomi/api/Patch.java +++ b/api/src/main/java/org/apache/unomi/api/Patch.java @@ -30,11 +30,10 @@ import java.util.TreeMap; /** - * Represents a patch operation applied to an existing {@link Item}. - * {@link #PATCHABLE_TYPES} lists the item types that support patching. - * The {@link #operation} is one of {@code override}, {@code patch}, or - * {@code remove}: {@code override} replaces the full item, {@code patch} - * applies a JSON Patch payload, and {@code remove} needs no data. + * Declarative update packaged as an {@link Item} and applied by {@link org.apache.unomi.api.services.PatchService}. + * {@link #PATCHABLE_TYPES} lists supported definition types (segments, rules, etc.). + * The {@link #operation} is {@code override} (replace item), {@code patch} (JSON Patch payload), + * or {@code remove} (delete target; no payload required). */ public class Patch extends Item { private static final long serialVersionUID = 4171966405850833985L; @@ -76,6 +75,7 @@ public class Patch extends Item { /** * Get the id of the item that will be concerned by this patch + * * @return item id */ public String getPatchedItemId() { @@ -84,6 +84,7 @@ public String getPatchedItemId() { /** * Sets the ID of the item that will be concerned by this patch. + * * @param patchedItemId the id of the item */ public void setPatchedItemId(String patchedItemId) { @@ -92,6 +93,7 @@ public void setPatchedItemId(String patchedItemId) { /** * Get the item type of the item that will be concerned by this patch + * * @return item type */ public String getPatchedItemType() { @@ -100,6 +102,7 @@ public String getPatchedItemType() { /** * Sets the item type of the item that will be concerned by this patch. + * * @param patchedItemType the item type */ public void setPatchedItemType(String patchedItemType) { @@ -108,6 +111,7 @@ public void setPatchedItemType(String patchedItemType) { /** * Get the type of patch operation : override, patch or remove + * * @return operation */ public String getOperation() { @@ -117,6 +121,7 @@ public String getOperation() { /** * Sets the type of patch operation to perform. This can typically be * 'override', 'patch', or 'remove'. + * * @param operation the patch operation type */ public void setOperation(String operation) { @@ -128,6 +133,7 @@ public void setOperation(String operation) { * For override operation, the data is the full item * For patch, the data is a JsonPatch object * For remove, no data is needed + * * @return data */ public Object getData() { @@ -140,6 +146,7 @@ public Object getData() { * item representation. * For patch operations, this should contain a JsonPatch object. * For remove operations, no data is required. + * * @param data the patch data */ public void setData(Object data) { @@ -148,6 +155,7 @@ public void setData(Object data) { /** * Get the date of the last patch application + * * @return last application date */ public Date getLastApplication() { @@ -156,6 +164,7 @@ public Date getLastApplication() { /** * Sets the date when the patch was last applied. + * * @param lastApplication the date of the last application */ public void setLastApplication(Date lastApplication) { diff --git a/api/src/main/java/org/apache/unomi/api/Persona.java b/api/src/main/java/org/apache/unomi/api/Persona.java index 3dfa2e52c..1b20cbc4c 100644 --- a/api/src/main/java/org/apache/unomi/api/Persona.java +++ b/api/src/main/java/org/apache/unomi/api/Persona.java @@ -41,6 +41,7 @@ public Persona() { /** * Constructs a new {@link Persona} instance with the * specified unique identifier. + * * @param personaId The unique ID to assign to this persona. */ public Persona(String personaId) { diff --git a/api/src/main/java/org/apache/unomi/api/PersonaSession.java b/api/src/main/java/org/apache/unomi/api/PersonaSession.java index 47b36c081..a1f29244f 100644 --- a/api/src/main/java/org/apache/unomi/api/PersonaSession.java +++ b/api/src/main/java/org/apache/unomi/api/PersonaSession.java @@ -44,9 +44,11 @@ public PersonaSession() { * Constructs a {@link PersonaSession} associated with a given profile, * item ID, and timestamp. * The resulting session is initialized with a system scope metadata. + * * @param itemId the unique identifier for the persona session item. * @param profile the {@link Profile} object representing the * context of the session. + * * @param timeStamp the date and time when this session was * created or recorded. */ diff --git a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java index cf4a82cdb..0c6142c89 100644 --- a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java +++ b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java @@ -32,16 +32,16 @@ public class PersonaWithSessions implements Serializable { private List sessions; /** - * Constructs a new, empty {@link PersonaWithSessions} instance. + * Default constructor. */ public PersonaWithSessions() { } /** - * Constructs a {@link PersonaWithSessions} object with the specified - * persona and list of sessions. - * @param persona the associated {@link Persona} - * @param sessions the list of associated {@link PersonaSession}s + * Creates a persona bundled with its sessions. + * + * @param persona the persona + * @param sessions related persona sessions */ public PersonaWithSessions(Persona persona, List sessions) { this.persona = persona; @@ -49,42 +49,45 @@ public PersonaWithSessions(Persona persona, List sessions) { } /** - * Returns the {@link Persona} associated with this object. - * @return the contained {@link Persona} + * The persona being simulated or inspected. + * + * @return the persona */ public Persona getPersona() { return persona; } /** - * Sets the {@link Persona} associated with this object. - * @param persona the new {@link Persona} + * Sets the persona. + * + * @param persona the persona */ public void setPersona(Persona persona) { this.persona = persona; } /** - * Returns all {@link PersonaSession}s associated with this object. - * @return a list of {@link PersonaSession}s + * Sessions linked to the persona. + * + * @return the persona sessions */ public List getSessions() { return sessions; } /** - * Sets the list of {@link PersonaSession}s associated with this object. - * @param sessions the new list of {@link PersonaSession}s + * Sets the persona sessions. + * + * @param sessions the session list */ public void setSessions(List sessions) { this.sessions = sessions; } /** - * Retrieves the last session from the stored list. This is assumed to be - * the first element (index 0). - * If no sessions are present, returns null. - * @return the most recent {@link PersonaSession}, or null if none exist + * Most recent session, taken as the first element of {@link #getSessions()}. + * + * @return the latest session, or {@code null} if none exist */ @XmlTransient public PersonaSession getLastSession() { diff --git a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java index 0da3e2d53..0dab6d134 100644 --- a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java +++ b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java @@ -56,6 +56,7 @@ public PersonalizationResult() { /** * Constructs a PersonalizationResult initialized with a * list of content IDs. + * * @param contentIds the list of matching ids for current personalization */ public PersonalizationResult(List contentIds) { @@ -64,6 +65,7 @@ public PersonalizationResult(List contentIds) { /** * List of matching ids for current personalization + * * @return the list of matching ids */ public List getContentIds() { @@ -73,6 +75,7 @@ public List getContentIds() { /** * Sets the list of content IDs associated with this result. * This overwrites any previously set content IDs. + * * @param contentIds the new list of content IDs */ public void setContentIds(List contentIds) { @@ -81,6 +84,7 @@ public void setContentIds(List contentIds) { /** * Useful open map to return additional result information to the client + * * @return map of key/value pair for additional information, like: inControlGroup */ public Map getAdditionalResultInfos() { @@ -90,6 +94,7 @@ public Map getAdditionalResultInfos() { /** * Sets the map containing additional result information. This map is useful * for returning extra data to the client. + * * @param additionalResultInfos a map of key/value pair for additional * information, like: inControlGroup */ @@ -102,6 +107,7 @@ public void setAdditionalResultInfos(Map additionalResultInfos) * Control group are used to identify a profile or a session that should not get personalized results, * instead the current profile/session should get a specific result (usually the same for all peoples falling in control group) * Note: it's for now the responsibility of the client to decide what to do when the current personalization is under control group. + * * @return true in case current profile or session is in control group for the personalization. */ @XmlTransient @@ -113,6 +119,7 @@ public boolean isInControlGroup() { /** * Sets whether this personalization result belongs to a control group by * storing the boolean value in the internal additional result info map. + * * @param inControlGroup true if the current profile or session is in * control group for the * personalization, false otherwise @@ -124,6 +131,7 @@ public void setInControlGroup(boolean inControlGroup) { /** * Change code in case the personalization resolution modified the profile or the session * Only used internally, and will not be serialized either for storage or response payload. + * * @return change code */ @XmlTransient @@ -135,6 +143,7 @@ public int getChangeType() { * Adds specified change flags to the current accumulated change type. * This method uses bitwise OR operation to ensure that multiple changes * are recorded without overwriting previous ones. + * * @param changes The change code or flag(s) to add to the * result's change type. */ diff --git a/api/src/main/java/org/apache/unomi/api/PluginType.java b/api/src/main/java/org/apache/unomi/api/PluginType.java index 491cd9588..ee28d7bea 100644 --- a/api/src/main/java/org/apache/unomi/api/PluginType.java +++ b/api/src/main/java/org/apache/unomi/api/PluginType.java @@ -18,23 +18,24 @@ package org.apache.unomi.api; /** - * Marker interface for Unomi plugin definition types. - * Implementations such as {@link PropertyMergeStrategyType} expose an id - * and OSGi filter so the runtime can locate the plugin implementation. + * Common contract for pluggable Unomi definition types loaded from OSGi. + * Implementations expose a stable id and an OSGi target filter so the runtime + * can locate executors and validators (for example {@link PropertyMergeStrategyType} + * and {@link ValueType}). */ public interface PluginType { /** - * Retrieves the plugin identifier, corresponding to the identifier of the OSGi bundle implementing the plugin. + * OSGi bundle id of the plugin that registered this type. * - * @return the plugin identifier, corresponding to the identifier of the OSGi bundle implementing the plugin + * @return the plugin bundle id */ long getPluginId(); /** - * Associates this plugin with its associated OSGi bundle identifier. + * Associates this plugin type with its OSGi bundle. * - * @param pluginId the OSGi bundle identifier associated with this plugin + * @param pluginId the plugin bundle id */ void setPluginId(long pluginId); diff --git a/api/src/main/java/org/apache/unomi/api/Profile.java b/api/src/main/java/org/apache/unomi/api/Profile.java index ff83d068b..5a8ee35d3 100644 --- a/api/src/main/java/org/apache/unomi/api/Profile.java +++ b/api/src/main/java/org/apache/unomi/api/Profile.java @@ -64,13 +64,13 @@ public class Profile extends Item implements SystemPropertiesItem { private Map consents = new LinkedHashMap<>(); /** - * Instantiates a new Profile. + * Default constructor. */ public Profile() { } /** - * Instantiates a new Profile with the specified identifier. + * Creates a profile with the given identifier. * * @param profileId the profile identifier */ @@ -90,20 +90,20 @@ public void setProperty(String name, Object value) { } /** - * Retrieves the property identified by the specified name. + * Value of the named profile property. * - * @param name the name of the property to retrieve - * @return the value of the specified property or {@code null} if no such property exists + * @param name the property name + * @return the property value, or {@code null} if unset */ public Object getProperty(String name) { return properties.get(name); } /** - * Retrieves the value of the nested property identified by the specified name. + * Value of a nested property, using dot-separated path segments. * - * @param name the name of the property to be retrieved, splited in the nested properties with "." - * @return the value of the property identified by the specified name + * @param name the property path (for example {@code address.city}) + * @return the nested value, or {@code null} if any segment is missing */ public Object getNestedProperty(String name) { if (!name.contains(".")) { @@ -124,9 +124,9 @@ public Object getNestedProperty(String name) { } /** - * Retrieves a Map of all property name - value pairs for this profile. + * All user-visible profile properties. * - * @return a Map of all property name - value pairs for this profile + * @return the property map */ public Map getProperties() { return properties; @@ -142,10 +142,9 @@ public void setProperties(Map properties) { } /** - * Retrieves a Map of system property name - value pairs for this profile. System properties can be used by implementations to store non-user visible properties needed for - * internal purposes. + * Internal properties used by implementations and not shown in UIs. * - * @return a Map of system property name - value pairs for this profile + * @return the system property map */ public Map getSystemProperties() { return systemProperties; @@ -161,11 +160,11 @@ public void setSystemProperties(Map systemProperties) { } /** - * Sets a system property, overwriting an existing one if it existed. This call will also created the system - * properties hash map if it didn't exist. - * @param key the key for the system property hash map - * @param value the value for the system property hash map - * @return the previous value object if it existing. + * Sets or replaces a system property, creating the map if needed. + * + * @param key the system property name + * @param value the system property value + * @return the previous value, or {@code null} if the key was not set */ public Object setSystemProperty(String key, Object value) { if (this.systemProperties == null) { @@ -185,9 +184,9 @@ public String getScope() { } /** - * Retrieves the identifiers of the segments this profile is a member of. + * Segment ids this profile currently belongs to. * - * @return the identifiers of the segments this profile is a member of + * @return the segment ids */ public Set getSegments() { return segments; @@ -196,8 +195,6 @@ public Set getSegments() { /** * Sets the identifiers of the segments this profile is a member of. * - * TODO: should be removed from the API - * * @param segments the segments */ public void setSegments(Set segments) { @@ -223,16 +220,17 @@ public void setMergedWith(String mergedWith) { } /** - * Retrieves the scores associated to this profile. + * Scoring model values keyed by scoring id. * - * @return the scores associated to this profile as a Map of {@link Scoring} identifier - score pairs + * @return the score map */ public Map getScores() { return scores; } /** - * TODO: should be removed from the API + * Sets scoring model values keyed by scoring id. + * * @param scores new value for scores */ public void setScores(Map scores) { @@ -240,16 +238,18 @@ public void setScores(Map scores) { } /** - * Returns all the consents, including the revokes ones. - * @return a map that contains as a key the scope + "/" + consent type ID (or just the consent type ID if no scope was set on the consent), and the consent itself as a value + * All consents on this profile, including revoked entries. + * + * @return consent map keyed by scope/type path */ public Map getConsents() { return consents; } /** - * Returns true if this profile is an anonymous profile. - * @return true of the profile has been marked as an anonymous profile, false otherwise. + * Whether this profile is marked anonymous via the system property {@code isAnonymousProfile}. + * + * @return {@code true} if anonymous, {@code false} otherwise */ @XmlTransient public boolean isAnonymousProfile() { @@ -258,11 +258,11 @@ public boolean isAnonymousProfile() { } /** - * Set a consent into the profile. - * @param consent if the consent is REVOKED, it will try to remove a consent with the same type id if it - * exists for the profile. - * @return true if the operation was successful (inserted exception in the case of a revoked consent, in which case - * it is successful if there was a consent to revoke). + * Adds or removes a consent on this profile. + * Revoked consents remove the matching entry when present. + * + * @param consent the consent to store or revoke + * @return {@code true} if the operation changed stored consents */ @XmlTransient public boolean setConsent(Consent consent) { diff --git a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java index c7569df2f..cdfe734b1 100644 --- a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java +++ b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java @@ -20,14 +20,15 @@ import java.util.Date; /** - * Maps a client-specific profile identifier to a canonical Unomi profile. - * Lets multiple applications reference the same visitor under different ids - * while sharing one merged profile record. + * Cross-application link between a client profile id and a canonical {@link Profile}. + * When the same visitor is known under different ids in separate scopes or apps, + * aliases let {@link org.apache.unomi.api.services.ProfileService} merge activity + * onto one persisted profile record. */ public class ProfileAlias extends Item { /** - * The constant string used to identify this item type as a profile alias. + * Item type identifier for profile aliases. */ public static final String ITEM_TYPE = "profileAlias"; @@ -40,71 +41,78 @@ public class ProfileAlias extends Item { private Date modifiedTime; /** - * Constructs an empty ProfileAlias object. + * Creates an empty profile alias. */ public ProfileAlias() { } /** - * Retrieves the unique identifier of the associated profile. - * @return The profile ID as a {@link String}. + * Canonical profile id linked by this alias. + * + * @return profile id */ public String getProfileID() { return profileID; } /** - * Sets the unique identifier of the associated profile. - * @param profileID The profile ID to set. + * Sets the canonical profile id. + * + * @param profileID profile id */ public void setProfileID(String profileID) { this.profileID = profileID; } /** - * Retrieves the client identifier associated with this alias. - * @return The client ID as a {@link String}, or null if not set. + * Client-specific profile identifier. + * + * @return client id, or {@code null} if unset */ public String getClientID() { return clientID; } /** - * Sets the client identifier associated with this alias. - * @param clientID The client ID to set. + * Sets the client-specific profile identifier. + * + * @param clientID client id */ public void setClientID(String clientID) { this.clientID = clientID; } /** - * Retrieves the date and time when this profile alias was created. - * @return The creation timestamp as a {@link java.util.Date}. + * When this alias was created. + * + * @return creation time */ public Date getCreationTime() { return creationTime; } /** - * Sets the date and time of creation for this profile alias. - * @param creationTime The creation timestamp to set. + * Sets the creation time. + * + * @param creationTime creation time */ public void setCreationTime(Date creationTime) { this.creationTime = creationTime; } /** - * Retrieves the date and time when this profile alias was last modified. - * or null if no modification time has been recorded. - * @return The {@link java.util.Date} representing the modification time, + * When this alias was last modified. + * + * @return last modification time, or {@code null} if unset */ public Date getModifiedTime() { return modifiedTime; } /** - * Sets the date and time when this profile alias was last modified. - * @param modifiedTime The new timestamp indicating the modification time. + * Sets the last modification time. + * + * @param modifiedTime last modification time */ public void setModifiedTime(Date modifiedTime) { this.modifiedTime = modifiedTime; diff --git a/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java b/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java index 9596d8e63..adf6000c0 100644 --- a/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java +++ b/api/src/main/java/org/apache/unomi/api/PropertyMergeStrategyType.java @@ -33,35 +33,38 @@ public class PropertyMergeStrategyType implements PluginType, Serializable { private long pluginId; /** - * Retrieves the identifier for this PropertyMergeStrategyType. - * @return the identifier for this PropertyMergeStrategyType + * Merge strategy identifier. + * + * @return the strategy id */ public String getId() { return id; } /** - * Sets the identifier for this PropertyMergeStrategyType. - * @param id the unique identifier to be set. + * Sets the merge strategy identifier. + * + * @param id the strategy id */ public void setId(String id) { this.id = id; } /** - * Retrieves the OSGi filter used to identify the implementation associated with this PropertyMergeStrategyType. Filters take the following form: - * {@code (propertyMergeStrategyExecutorId=<id>)} where {@code id} corresponds to the value of the {@code propertyMergeStrategyExecutorId} service property in the - * Blueprint service definition for this PropertyMergeStrategyType. - * @return the filter string used to identify the implementation associated with this PropertyMergeStrategyType + * OSGi LDAP filter that locates the executor for this strategy. + * Format: {@code (propertyMergeStrategyExecutorId=)} where {@code id} matches the + * {@code propertyMergeStrategyExecutorId} service property in the Blueprint definition. + * + * @return the OSGi filter string */ public String getFilter() { return filter; } /** - * Sets the OSGi filter used to identify the implementation associated with - * this PropertyMergeStrategyType. - * @param filter the filter string value. + * Sets the OSGi filter for locating the strategy executor. + * + * @param filter the OSGi filter string */ public void setFilter(String filter) { this.filter = filter; diff --git a/api/src/main/java/org/apache/unomi/api/PropertyType.java b/api/src/main/java/org/apache/unomi/api/PropertyType.java index 93ee1b226..c24f7b837 100644 --- a/api/src/main/java/org/apache/unomi/api/PropertyType.java +++ b/api/src/main/java/org/apache/unomi/api/PropertyType.java @@ -52,41 +52,42 @@ public class PropertyType extends MetadataItem { private Set childPropertyTypes = new LinkedHashSet<>(); /** - * Instantiates a new Property type. + * Default constructor. */ public PropertyType() { } /** - * Instantiates a new Property type with the specified Metadata. - * @param metadata the metadata associated with the specified metadata + * Creates a property type with the given metadata. + * + * @param metadata the property type metadata */ public PropertyType(Metadata metadata) { super(metadata); } /** - * Retrieves the target for this property type, indicating the type of elements this property type is defined for. For example, for property types attached to profiles, {@code - * target} would be {@code "profiles"}. - * TODO: deprecated? - * @return the target for this property type + * Item type this property applies to (for example {@code profiles}). + * + * @return the target item type */ public String getTarget() { return target; } /** - * Sets the target for this property type. - * TODO: deprecated? - * @param target the target for this property type, indicating the type of elements this property type is defined for + * Sets the item type this property applies to. + * + * @param target the target item type */ public void setTarget(String target) { this.target = target; } /** - * Retrieves the identifier of the value type constraining values for properties using this PropertyType. - * @return the value type identifier associated with values defined by this PropertyType + * Identifier of the {@link ValueType} that constrains property values. + * + * @return the value type id * @see ValueType */ @XmlElement(name = "type") @@ -96,15 +97,17 @@ public String getValueTypeId() { /** * Sets the value type identifier. - * @param valueTypeId the value type identifier + * + * @param valueTypeId the value type id */ public void setValueTypeId(String valueTypeId) { this.valueTypeId = valueTypeId; } /** - * Retrieves the value type associated with values defined for properties using this PropertyType. - * @return the value type associated with values defined for properties using this PropertyType + * Resolved value type definition for properties using this schema. + * + * @return the value type */ @XmlTransient public ValueType getValueType() { @@ -112,81 +115,89 @@ public ValueType getValueType() { } /** - * Sets the value type. - * @param valueType the value type associated with values defined for properties using this PropertyType + * Sets the resolved value type definition. + * + * @param valueType the value type */ public void setValueType(ValueType valueType) { this.valueType = valueType; } /** - * Retrieves the default value defined for property using this PropertyType. - * @return the default value defined for property using this PropertyType + * Default value applied when a property is created without an explicit value. + * + * @return the default value */ public String getDefaultValue() { return defaultValue; } /** - * Sets the default value that properties using this PropertyType will use if no value is specified explicitly. - * @param defaultValue the default value that properties using this PropertyType will use if no value is specified explicitly + * Sets the default value for properties of this type. + * + * @param defaultValue the default value */ public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; } /** - * Retrieves the set of JCR properties from which properties of this type would be automatically initialized from. - * TODO: remove from API? - * @return the name of JCR properties properties of this type would be automatically initialized from + * External property names used to auto-populate values for this type. + * + * @return the automatic mapping source names */ public Set getAutomaticMappingsFrom() { return automaticMappingsFrom; } /** - * Specifies the set of JCR properties from which properties of this type would be automatically initialized from. - * TODO: remove from API? - * @param automaticMappingsFrom the set of JCR properties from which properties of this type would be automatically initialized from + * Sets the external property names used for automatic mapping. + * + * @param automaticMappingsFrom the source property names */ public void setAutomaticMappingsFrom(Set automaticMappingsFrom) { this.automaticMappingsFrom = automaticMappingsFrom; } /** - * Retrieves the rank of this PropertyType for ordering purpose. - * @return the rank of this PropertyType for ordering purpose + * Relative ordering weight when multiple property types are shown together. + * + * @return the rank */ public Double getRank() { return rank; } /** - * Specifies the rank of this PropertyType for ordering purpose. - * @param rank the rank of this PropertyType for ordering purpose + * Sets the display ordering rank. + * + * @param rank the rank */ public void setRank(Double rank) { this.rank = rank; } /** - * Retrieves the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged. - * @return the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged + * Merge strategy id used when profiles with this property are merged. + * + * @return the merge strategy id */ public String getMergeStrategy() { return mergeStrategy; } /** - * Sets the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged - * @param mergeStrategy the identifier of the {@link PropertyMergeStrategyType} to be used in case profiles with properties using this PropertyType are being merged + * Sets the merge strategy id for profile merges. + * + * @param mergeStrategy the merge strategy id */ public void setMergeStrategy(String mergeStrategy) { this.mergeStrategy = mergeStrategy; } /** - * Retrieves the date ranges. + * Allowed date ranges for values of this type. + * * @return the date ranges */ public List getDateRanges() { @@ -194,7 +205,8 @@ public List getDateRanges() { } /** - * Sets the date ranges. + * Sets the allowed date ranges. + * * @param dateRanges the date ranges */ public void setDateRanges(List dateRanges) { @@ -202,7 +214,8 @@ public void setDateRanges(List dateRanges) { } /** - * Retrieves the numeric ranges. + * Allowed numeric ranges for values of this type. + * * @return the numeric ranges */ public List getNumericRanges() { @@ -210,7 +223,8 @@ public List getNumericRanges() { } /** - * Sets the numeric ranges. + * Sets the allowed numeric ranges. + * * @param numericRanges the numeric ranges */ public void setNumericRanges(List numericRanges) { @@ -218,16 +232,18 @@ public void setNumericRanges(List numericRanges) { } /** - * Retrieves the ip ranges. - * @return the ip ranges + * Allowed IP ranges for values of this type. + * + * @return the IP ranges */ public List getIpRanges() { return ipRanges; } /** - * Sets the ip ranges. - * @param ipRanges the ip ranges + * Sets the allowed IP ranges. + * + * @param ipRanges the IP ranges */ public void setIpRanges(List ipRanges) { this.ipRanges = ipRanges; @@ -235,6 +251,7 @@ public void setIpRanges(List ipRanges) { /** * Whether properties using this property type are multi-valued. + * * @return {@code true} if properties of this type should be multi-valued, {@code false} otherwise */ public Boolean isMultivalued() { @@ -243,6 +260,7 @@ public Boolean isMultivalued() { /** * Specifies whether properties using this property type are multi-valued. + * * @param multivalued {@code true} if properties of this type should be multi-valued, {@code false} otherwise */ public void setMultivalued(Boolean multivalued) { @@ -251,7 +269,7 @@ public void setMultivalued(Boolean multivalued) { /** * Whether properties with this type are marked as protected. Protected properties can be displayed but their value cannot be changed. - * TODO: rename to readOnly? + * The { protected} flag name may change to { readOnly} in a future release. * * @return {@code true} if properties of this type are protected, {@code false} otherwise */ public Boolean isProtected() { @@ -260,6 +278,7 @@ public Boolean isProtected() { /** * Specifies whether properties with this type are marked as protected. + * * @param protekted {@code true} if properties of this type are protected, {@code false} otherwise */ public void setProtected(boolean protekted) { @@ -267,18 +286,18 @@ public void setProtected(boolean protekted) { } /** - * Retrieves the set of {@link PropertyType} objects that are considered - * children or subtypes of this property type. - * @return a set of child property types associated with this instance + * Nested property types that extend this schema. + * + * @return the child property types */ public Set getChildPropertyTypes() { return childPropertyTypes; } /** - * Sets the collection of child property types for this property type. - * @param childPropertyTypes the set of {@link PropertyType} objects that - * are children or subtypes + * Sets the nested child property types. + * + * @param childPropertyTypes the child property types */ public void setChildPropertyTypes(Set childPropertyTypes) { this.childPropertyTypes = childPropertyTypes; diff --git a/api/src/main/java/org/apache/unomi/api/Scope.java b/api/src/main/java/org/apache/unomi/api/Scope.java index 1618f6f24..110b0b9a0 100644 --- a/api/src/main/java/org/apache/unomi/api/Scope.java +++ b/api/src/main/java/org/apache/unomi/api/Scope.java @@ -32,7 +32,7 @@ public class Scope extends MetadataItem { public static final String ITEM_TYPE = "scope"; /** - * Instantiates a new Scope. + * Default constructor. */ public Scope() { } diff --git a/api/src/main/java/org/apache/unomi/api/ServerInfo.java b/api/src/main/java/org/apache/unomi/api/ServerInfo.java index 4f973cf24..dd0a46d00 100644 --- a/api/src/main/java/org/apache/unomi/api/ServerInfo.java +++ b/api/src/main/java/org/apache/unomi/api/ServerInfo.java @@ -23,9 +23,10 @@ import java.util.Map; /** - * High-level description of a running Unomi instance. - * Exposes version, build, SCM branch, timestamp, and the list of event types - * the server can handle. Clients call this to discover server capabilities. + * Capability and build information exposed by a running Unomi node. + * Populated at startup and returned by health and cluster APIs so clients, + * operators, and other nodes can read version, build metadata, supported + * {@link EventInfo} types, optional capability flags, and banner logo lines. */ public class ServerInfo { @@ -42,156 +43,168 @@ public class ServerInfo { private List logoLines = new ArrayList<>(); /** - * Constructs a new ServerInfo instance. + * Creates an empty server info record. */ public ServerInfo() { } /** - * Returns the unique identifier of the server. - * @return The server's identifier string. + * Unique identifier for this server instance. + * + * @return server identifier */ public String getServerIdentifier() { return serverIdentifier; } /** - * Sets the unique identifier for this server information. - * @param serverIdentifier The unique identifier to set. + * Sets the server identifier. + * + * @param serverIdentifier server identifier */ public void setServerIdentifier(String serverIdentifier) { this.serverIdentifier = serverIdentifier; } /** - * Returns the version string of the server. - * @return The server's version number. + * Running Unomi version string. + * + * @return server version */ public String getServerVersion() { return serverVersion; } /** - * Sets the version string for this server information. - * @param serverVersion The version string to set. + * Sets the server version. + * + * @param serverVersion server version */ public void setServerVersion(String serverVersion) { this.serverVersion = serverVersion; } /** - * Returns the build number of the server. - * @return The server's build number as a string. + * Build number of this server distribution. + * + * @return build number */ public String getServerBuildNumber() { return serverBuildNumber; } /** - * Sets the build number for this server information. - * @param serverBuildNumber The build number to set. + * Sets the build number. + * + * @param serverBuildNumber build number */ public void setServerBuildNumber(String serverBuildNumber) { this.serverBuildNumber = serverBuildNumber; } /** - * Returns the date associated with the server's build. - * @return The {@link java.util.Date} representing the build date. + * When this server build was produced. + * + * @return build date */ public Date getServerBuildDate() { return serverBuildDate; } /** - * Sets the build date of the server. - * @param serverBuildDate the build date of the server + * Sets the build date. + * + * @param serverBuildDate build date */ public void setServerBuildDate(Date serverBuildDate) { this.serverBuildDate = serverBuildDate; } /** - * Retrieves the timestamp associated with the server information. - * @return the server's timestamp string + * Server timestamp string (for example {@code YYYYMMDDHHmmss}). + * + * @return server timestamp */ public String getServerTimestamp() { return serverTimestamp; } /** - * Sets the timestamp for the server information. - * @param serverTimestamp the server's timestamp (e.g., YYYYMMDDHHmmss) + * Sets the server timestamp. + * + * @param serverTimestamp server timestamp */ public void setServerTimestamp(String serverTimestamp) { this.serverTimestamp = serverTimestamp; } /** - * Retrieves the source control management branch name of the server. - * @return the SCM branch name + * Source control branch this build came from. + * + * @return SCM branch name */ public String getServerScmBranch() { return serverScmBranch; } /** - * Sets the source control management branch name for - * the server information. - * @param serverScmBranch the SCM branch name + * Sets the SCM branch name. + * + * @param serverScmBranch SCM branch name */ public void setServerScmBranch(String serverScmBranch) { this.serverScmBranch = serverScmBranch; } /** - * Retrieves the list of event types supported by this server instance. - * @return a list of {@link EventInfo} objects representing - * available event types + * Event types this server instance supports, with occurrence counts. + * + * @return supported event types */ public List getEventTypes() { return eventTypes; } /** - * Sets the collection of event types associated with - * the server information. - * @param eventTypes the list of event types to set + * Sets the supported event types. + * + * @param eventTypes event types */ public void setEventTypes(List eventTypes) { this.eventTypes = eventTypes; } /** - * Retrieves a map containing various capabilities reported by the server. - * @return a map where keys are capability names and values are descriptions + * Optional capability flags reported by the server. + * + * @return map of capability name to description */ public Map getCapabilities() { return capabilities; } /** - * Sets the map containing various capabilities reported - * by the Unomi server. - * @param capabilities The map of capability names and descriptions. + * Sets the capability map. + * + * @param capabilities capability name to description */ public void setCapabilities(Map capabilities) { this.capabilities = capabilities; } /** - * Retrieves the list of strings used to display the server's logo lines. - * @return The {@link java.util.List} - * containing the logo lines. + * ASCII art lines shown in the server logo banner. + * + * @return logo lines */ public List getLogoLines() { return logoLines; } /** - * Sets the list of strings that represent the server's logo lines - * for display purposes. - * @param logoLines The list of strings defining the logo lines. + * Sets the logo banner lines. + * + * @param logoLines logo lines */ public void setLogoLines(List logoLines) { this.logoLines = logoLines; diff --git a/api/src/main/java/org/apache/unomi/api/Session.java b/api/src/main/java/org/apache/unomi/api/Session.java index 11496be61..ad986b931 100644 --- a/api/src/main/java/org/apache/unomi/api/Session.java +++ b/api/src/main/java/org/apache/unomi/api/Session.java @@ -58,18 +58,18 @@ public class Session extends Item implements TimestampedItem, SystemPropertiesIt private List originEventIds = new ArrayList<>(); /** - * Instantiates a new Session. + * Default constructor. */ public Session() { } /** - * Instantiates a new Session. + * Creates a session linked to a profile, timestamp, and scope. * - * @param itemId the identifier for this Session - * @param profile the associated {@link Profile} - * @param timeStamp the time stamp - * @param scope the scope + * @param itemId the session identifier + * @param profile the associated profile + * @param timeStamp the session start time + * @param scope the session scope */ public Session(String itemId, Profile profile, Date timeStamp, String scope) { super(itemId); @@ -80,18 +80,18 @@ public Session(String itemId, Profile profile, Date timeStamp, String scope) { } /** - * Retrieves the identifier of the associated Profile. + * Identifier of the profile linked to this session. * - * @return the identifier of the associated Profile + * @return the profile id */ public String getProfileId() { return profileId; } /** - * Retrieves the associated Profile. + * Profile associated with this session. * - * @return the associated profile + * @return the profile */ public Profile getProfile() { return profile; @@ -119,19 +119,19 @@ public void setProperty(String name, Object value) { } /** - * Retrieves the property identified by the specified name. + * Value of the named session property. * - * @param name the name of the property to retrieve - * @return the value of the specified property or {@code null} if no such property exists + * @param name the property name + * @return the property value, or {@code null} if unset */ public Object getProperty(String name) { return properties.get(name); } /** - * Retrieves a Map of all property name - value pairs. + * All user-visible session properties. * - * @return a Map of all property name - value pairs + * @return the property map */ public Map getProperties() { return properties; @@ -147,10 +147,9 @@ public void setProperties(Map properties) { } /** - * Retrieves a Map of system property name - value pairs. System properties can be used by implementations to store non-user visible properties needed for - * internal purposes. + * Internal properties used by implementations and not shown in UIs. * - * @return a Map of system property name - value pairs + * @return the system property map */ public Map getSystemProperties() { return systemProperties; @@ -166,16 +165,16 @@ public void setSystemProperties(Map systemProperties) { } /** - * Retrieves the session creation timestamp. + * When this session was created. * - * @return the session creation timestamp + * @return the creation timestamp */ public Date getTimeStamp() { return timeStamp; } /** - * Retrieves the last event date. + * Timestamp of the most recent event in this session. * * @return the last event date */ @@ -196,18 +195,18 @@ public void setLastEventDate(Date lastEventDate) { } /** - * Retrieves the duration. + * Elapsed time between session start and last event, in milliseconds. * - * @return the duration + * @return the session duration */ public int getDuration() { return duration; } /** - * Retrieves the size. + * Number of events recorded in this session. * - * @return the size + * @return the event count */ public int getSize() { return size; @@ -223,9 +222,9 @@ public void setSize(int size) { } /** - * Retrieves the scope this session belongs to. + * Scope this session belongs to. * - * @return the session scope identifier + * @return the scope id */ public String getScope() { return scope; @@ -241,36 +240,36 @@ public void setScope(String scope) { } /** - * Get the events types which causes the session creation + * Event types that triggered session creation. * - * @return List of event types + * @return the origin event types */ public List getOriginEventTypes() { return originEventTypes; } /** - * Set the events types which causes the session creation + * Sets the event types that triggered session creation. * - * @param originEventTypes List of event types + * @param originEventTypes the origin event types */ public void setOriginEventTypes(List originEventTypes) { this.originEventTypes = originEventTypes; } /** - * Get the events ids which causes the session creation + * Event ids that triggered session creation. * - * @return event ids + * @return the origin event ids */ public List getOriginEventIds() { return originEventIds; } /** - * Set the events ids which causes the session creation + * Sets the event ids that triggered session creation. * - * @param originEventIds List of event ids + * @param originEventIds the origin event ids */ public void setOriginEventIds(List originEventIds) { this.originEventIds = originEventIds; diff --git a/api/src/main/java/org/apache/unomi/api/TimestampedItem.java b/api/src/main/java/org/apache/unomi/api/TimestampedItem.java index f383efab8..a552e5ee5 100644 --- a/api/src/main/java/org/apache/unomi/api/TimestampedItem.java +++ b/api/src/main/java/org/apache/unomi/api/TimestampedItem.java @@ -26,9 +26,9 @@ */ public interface TimestampedItem { /** - * Retrieves the associated timestamp. + * Creation or last-modification timestamp for timestamped items. * - * @return the associated timestamp + * @return the timestamp */ Date getTimeStamp(); } diff --git a/api/src/main/java/org/apache/unomi/api/Topic.java b/api/src/main/java/org/apache/unomi/api/Topic.java index dbddfd346..01843ad7b 100644 --- a/api/src/main/java/org/apache/unomi/api/Topic.java +++ b/api/src/main/java/org/apache/unomi/api/Topic.java @@ -31,32 +31,36 @@ public class Topic extends Item { private String name; /** - * Retrieves the unique identifier of this topic. - * @return The {@link String} ID of the topic. + * Topic identifier. + * + * @return the topic id */ public String getTopicId() { return topicId; } /** - * Sets the unique identifier for this topic. - * @param topicId the unique ID to set. + * Sets the topic identifier. + * + * @param topicId the topic id */ public void setTopicId(String topicId) { this.topicId = topicId; } /** - * Retrieves the name of the topic. - * @return The {@link String} name of the topic. + * Display name of the topic. + * + * @return the topic name */ public String getName() { return name; } /** - * Sets the display name for this topic. - * @param name the new name to set. + * Sets the display name. + * + * @param name the topic name */ public void setName(String name) { this.name = name; diff --git a/api/src/main/java/org/apache/unomi/api/ValueType.java b/api/src/main/java/org/apache/unomi/api/ValueType.java index 9895837f0..95aff9dbc 100644 --- a/api/src/main/java/org/apache/unomi/api/ValueType.java +++ b/api/src/main/java/org/apache/unomi/api/ValueType.java @@ -36,42 +36,43 @@ public class ValueType implements PluginType, Serializable { private Set tags = new LinkedHashSet<>(); /** - * Instantiates a new Value type. + * Default constructor. */ public ValueType() { } /** - * Instantiates a new Value type with the specified identifier. + * Creates a value type with the given identifier. * - * @param id the identifier + * @param id the value type id */ public ValueType(String id) { this.id = id; } /** - * Retrieves this ValueType's identifier. + * Value type identifier. * - * @return this ValueType's identifier + * @return the value type id */ public String getId() { return id; } /** - * Sets this ValueType's identifier. + * Sets the value type identifier. * - * @param id this ValueType's identifier + * @param id the value type id */ public void setId(String id) { this.id = id; } /** - * Retrieves the {@link java.util.ResourceBundle} key used to localize this ValueType's name. + * Resource bundle key for localizing the display name. + * Defaults to {@code type.} when unset. * - * @return the {@link java.util.ResourceBundle} key used to localize this ValueType's name + * @return the name localization key */ public String getNameKey() { if (nameKey == null) { @@ -81,18 +82,19 @@ public String getNameKey() { } /** - * Sets the name key. + * Sets the name localization key. * - * @param nameKey the name key + * @param nameKey the resource bundle key */ public void setNameKey(String nameKey) { this.nameKey = nameKey; } /** - * Retrieves the {@link java.util.ResourceBundle} key used to localize this ValueType's description. + * Resource bundle key for localizing the description. + * Defaults to {@code type..description} when unset. * - * @return the {@link java.util.ResourceBundle} key used to localize this ValueType's name + * @return the description localization key */ public String getDescriptionKey() { if (descriptionKey == null) { @@ -102,9 +104,9 @@ public String getDescriptionKey() { } /** - * Sets the description key. + * Sets the description localization key. * - * @param descriptionKey the description key + * @param descriptionKey the resource bundle key */ public void setDescriptionKey(String descriptionKey) { this.descriptionKey = descriptionKey; @@ -128,18 +130,18 @@ public void setPluginId(long pluginId) { } /** - * Retrieves the tags used by this ValueType. + * Tags associated with this value type. * - * @return the tags used by this ValueType + * @return the tag names */ public Set getTags() { return tags; } /** - * Sets the tags used by this ValueType. + * Sets the tags for this value type. * - * @param tags the tags used by this ValueType + * @param tags the tag names */ public void setTags(Set tags) { this.tags = tags; diff --git a/api/src/main/java/org/apache/unomi/api/actions/Action.java b/api/src/main/java/org/apache/unomi/api/actions/Action.java index aeaba1bc6..7d10fe5d7 100644 --- a/api/src/main/java/org/apache/unomi/api/actions/Action.java +++ b/api/src/main/java/org/apache/unomi/api/actions/Action.java @@ -42,13 +42,13 @@ public class Action implements Serializable, YamlConvertible { private Map parameterValues = new HashMap<>(); /** - * Instantiates a new Action. + * Default constructor. */ public Action() { } /** - * Instantiates a new Action with the specified {@link ActionType} + * Creates an action with the given action type * * @param actionType the action's type */ @@ -57,7 +57,7 @@ public Action(ActionType actionType) { } /** - * Retrieves the action's type. + * Action type for this action. * * @return the action's type */ @@ -77,7 +77,7 @@ public void setActionType(ActionType actionType) { } /** - * Retrieves the identifier of the associated action type. + * Identifier of the associated action type. * * @return the identifier of the associated action type */ @@ -96,7 +96,7 @@ public void setActionTypeId(String actionTypeId) { } /** - * Retrieves the parameter values as a Map of parameter name - associated value pairs. + * Parameter values keyed by parameter name. * * @return a Map of parameter name - associated value pairs */ @@ -105,7 +105,7 @@ public Map getParameterValues() { } /** - * Sets the parameter values as a Map of parameter name - associated value pairs. + * Sets parameter values keyed by parameter name. * * @param parameterValues the parameter values as a Map of parameter name - associated value pairs */ @@ -129,6 +129,7 @@ public void setParameter(String name, Object value) { * Implements YamlConvertible interface with circular reference detection. * * @param visited set of already visited objects to prevent infinite recursion (may be null) + * @param maxDepth maximum recursion depth to prevent stack overflow * @return a Map representation of this action */ @Override 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 f652578b0..501dafd97 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 @@ -45,50 +45,51 @@ public class ActionType extends MetadataItem implements PluginType, YamlConverti private long pluginId; /** - * Instantiates a new Action type. + * Default constructor. */ public ActionType() { } /** - * Instantiates a new Action type. - * @param metadata the metadata + * Creates an action type with the given metadata. + * + * @param metadata the action type metadata */ public ActionType(Metadata metadata) { super(metadata); } /** - * Retrieves the action executor. + * OSGi executor id that runs this action at runtime. * - * @return the action executor + * @return the action executor id */ public String getActionExecutor() { return actionExecutor; } /** - * Sets the action executor. + * Sets the action executor id. * - * @param actionExecutor the action executor + * @param actionExecutor the executor id */ public void setActionExecutor(String actionExecutor) { this.actionExecutor = actionExecutor; } /** - * Retrieves the parameters. + * Parameters accepted by this action type. * - * @return the parameters + * @return the parameter definitions */ public List getParameters() { return parameters; } /** - * Sets the parameters. + * Sets the parameter definitions. * - * @param parameters the parameters + * @param parameters the parameter list */ public void setParameters(List parameters) { this.parameters = parameters; diff --git a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java index ba0936345..f28789738 100644 --- a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java +++ b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java @@ -53,22 +53,22 @@ public class Campaign extends MetadataItem { private String timezone; /** - * Instantiates a new Campaign. + * Default constructor. */ public Campaign() { } /** - * Instantiates a new Campaign with the specified metadata. + * Creates a campaign with the given metadata. * - * @param metadata the metadata + * @param metadata the campaign metadata */ public Campaign(Metadata metadata) { super(metadata); } /** - * Retrieves the start date for this Campaign. + * When the campaign becomes active. * * @return the start date */ @@ -77,7 +77,7 @@ public Date getStartDate() { } /** - * Sets the start date for this Campaign. + * Sets the campaign start date. * * @param startDate the start date */ @@ -86,7 +86,7 @@ public void setStartDate(Date startDate) { } /** - * Retrieves the end date for this Campaign. + * When the campaign stops being active. * * @return the end date */ @@ -95,7 +95,7 @@ public Date getEndDate() { } /** - * Sets the end date for this Campaign. + * Sets the campaign end date. * * @param endDate the end date */ @@ -104,90 +104,90 @@ public void setEndDate(Date endDate) { } /** - * Retrieves the entry condition that must be satisfied for users to be considered as taking part of this Campaign. + * Condition a profile must satisfy to enter the campaign. * - * @return the entry condition that must be satisfied for users to be considered as taking part of this Campaign + * @return the entry condition */ public Condition getEntryCondition() { return entryCondition; } /** - * Sets the entry condition that must be satisfied for users to be considered as taking part of this Campaign.. + * Sets the campaign entry condition. * - * @param entryCondition the entry condition that must be satisfied for users to be considered as taking part of this Campaign + * @param entryCondition the entry condition */ public void setEntryCondition(Condition entryCondition) { this.entryCondition = entryCondition; } /** - * Retrieves the cost incurred by this Campaign. + * Reported cost of running this campaign. * - * @return the cost incurred by this Campaign + * @return the campaign cost */ public Double getCost() { return cost; } /** - * Sets the cost incurred by this Campaign. + * Sets the campaign cost. * - * @param cost the cost incurred by this Campaign + * @param cost the campaign cost */ public void setCost(Double cost) { this.cost = cost; } /** - * Retrieves the currency associated to the Campaign's cost. + * Currency code for {@link #getCost()}. * - * @return the currency associated to the Campaign's cost + * @return the currency code */ public String getCurrency() { return currency; } /** - * Sets the currency associated to the Campaign's cost. + * Sets the currency code for the campaign cost. * - * @param currency the currency associated to the Campaign's cost + * @param currency the currency code */ public void setCurrency(String currency) { this.currency = currency; } /** - * Retrieves the identifier for this Campaign's primary {@link Goal}. + * Identifier of the primary goal tracked for this campaign. * - * @return the identifier for this Campaign's primary {@link Goal} + * @return the primary goal id */ public String getPrimaryGoal() { return primaryGoal; } /** - * Sets the identifier for this Campaign's primary {@link Goal}. + * Sets the primary goal id for this campaign. * - * @param primaryGoal the identifier for this Campaign's primary {@link Goal} + * @param primaryGoal the primary goal id */ public void setPrimaryGoal(String primaryGoal) { this.primaryGoal = primaryGoal; } /** - * Retrieves the timezone associated with this Campaign's start and end dates. + * Time zone used to interpret {@link #getStartDate()} and {@link #getEndDate()}. * - * @return the timezone associated with this Campaign's start and end dates + * @return the time zone id */ public String getTimezone() { return timezone; } /** - * Sets the timezone associated with this Campaign's start and end dates. + * Sets the time zone for campaign start and end dates. * - * @param timezone the timezone associated with this Campaign's start and end dates + * @param timezone the time zone id */ public void setTimezone(String timezone) { this.timezone = timezone; diff --git a/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java b/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java index adeb4c1f6..164fec4ef 100644 --- a/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java +++ b/api/src/main/java/org/apache/unomi/api/campaigns/CampaignDetail.java @@ -18,9 +18,10 @@ package org.apache.unomi.api.campaigns; /** - * Aggregated metrics for a {@link Campaign}. - * Summarizes participation and performance figures used in campaign reporting - * dashboards. + * Live campaign performance snapshot built from profiles, sessions, and goals. + * Counts engaged profiles, session views and successes, linked goals, and an + * overall conversion rate. {@link org.apache.unomi.api.services.GoalsService} and campaign REST endpoints + * return this object for campaign dashboards. */ public class CampaignDetail { private long engagedProfiles = 0; @@ -31,52 +32,52 @@ public class CampaignDetail { private Campaign campaign; /** - * Instantiates a new Campaign detail. + * Creates campaign performance details for the given campaign. * - * @param campaign the campaign + * @param campaign the campaign being reported on */ public CampaignDetail(Campaign campaign) { this.campaign = campaign; } /** - * Retrieves the number of engaged profiles. + * Number of profiles that engaged with the campaign. * - * @return the number of engaged profiles + * @return the engaged profile count */ public long getEngagedProfiles() { return engagedProfiles; } /** - * Sets the number of engaged profiles. + * Sets the engaged profile count. * - * @param engagedProfiles the number of engaged profiles + * @param engagedProfiles the engaged profile count */ public void setEngagedProfiles(long engagedProfiles) { this.engagedProfiles = engagedProfiles; } /** - * Retrieves the number of goals. + * Number of goals linked to the campaign. * - * @return the number of goals + * @return the goal count */ public long getNumberOfGoals() { return numberOfGoals; } /** - * Sets the number of goals. + * Sets the goal count. * - * @param numberOfGoals the number of goals + * @param numberOfGoals the goal count */ public void setNumberOfGoals(long numberOfGoals) { this.numberOfGoals = numberOfGoals; } /** - * Retrieves the conversion rate. + * Overall conversion rate for the campaign. * * @return the conversion rate */ @@ -94,16 +95,16 @@ public void setConversionRate(double conversionRate) { } /** - * Retrieves the associated campaign. + * Campaign these metrics describe. * - * @return the associated campaign + * @return the campaign */ public Campaign getCampaign() { return campaign; } /** - * Sets the associated campaign. + * Sets the campaign reference. * * @param campaign the campaign */ @@ -112,36 +113,36 @@ public void setCampaign(Campaign campaign) { } /** - * Retrieves the number of campaign session views. + * Number of campaign sessions that recorded a view. * - * @return the number of campaign session views + * @return the session view count */ public long getCampaignSessionViews() { return campaignSessionViews; } /** - * Sets the number of campaign session views. + * Sets the campaign session view count. * - * @param campaignSessionViews the number of campaign session views + * @param campaignSessionViews the session view count */ public void setCampaignSessionViews(long campaignSessionViews) { this.campaignSessionViews = campaignSessionViews; } /** - * Retrieves the number of campaign session successes. + * Number of campaign sessions that reached a success state. * - * @return the number of campaign session successes + * @return the session success count */ public long getCampaignSessionSuccess() { return campaignSessionSuccess; } /** - * Sets the number of campaign session successes. + * Sets the campaign session success count. * - * @param campaignSessionSuccess the number of campaign session successes + * @param campaignSessionSuccess the session success count */ public void setCampaignSessionSuccess(long campaignSessionSuccess) { this.campaignSessionSuccess = campaignSessionSuccess; diff --git a/api/src/main/java/org/apache/unomi/api/campaigns/events/CampaignEvent.java b/api/src/main/java/org/apache/unomi/api/campaigns/events/CampaignEvent.java index 3591cec45..b3a1d055b 100644 --- a/api/src/main/java/org/apache/unomi/api/campaigns/events/CampaignEvent.java +++ b/api/src/main/java/org/apache/unomi/api/campaigns/events/CampaignEvent.java @@ -25,9 +25,7 @@ import java.util.Date; /** - * A specific campaign event to help analyzing your key performance indicators by marking specific dates during your campaign. - * - * @author : rincevent Created : 17/03/15 + * A campaign milestone event used to mark dates and costs during a campaign for KPI analysis. */ public class CampaignEvent extends MetadataItem { /** @@ -44,13 +42,13 @@ public class CampaignEvent extends MetadataItem { private String timezone; /** - * Instantiates a new Campaign event. + * Default constructor. */ public CampaignEvent() { } /** - * Instantiates a new Campaign event with the specified metadata. + * Creates a campaign event with the given metadata. * * @param metadata the metadata */ @@ -59,9 +57,9 @@ public CampaignEvent(Metadata metadata) { } /** - * Retrieves the cost associated with this campaign event. + * Cost associated with this campaign event. * - * @return the cost associated with this campaign event + * @return the event cost */ public Double getCost() { return cost; @@ -77,9 +75,9 @@ public void setCost(Double cost) { } /** - * Retrieves the currency. + * Currency code for the event cost. * - * @return the currency + * @return the currency code */ public String getCurrency() { return currency; @@ -95,7 +93,7 @@ public void setCurrency(String currency) { } /** - * Retrieves the event date. + * Date when this campaign event occurred. * * @return the event date */ @@ -113,9 +111,9 @@ public void setEventDate(Date eventDate) { } /** - * Retrieves the identifier of the associated {@link Campaign}. + * Identifier of the associated {@link Campaign}. * - * @return the identifier of the associated {@link Campaign} + * @return the campaign identifier */ public String getCampaignId() { return campaignId; @@ -131,9 +129,9 @@ public void setCampaignId(String campaignId) { } /** - * Retrieves the timezone. + * Timezone for interpreting the event date. * - * @return the timezone + * @return the timezone identifier */ public String getTimezone() { return timezone; diff --git a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java index 5ef081d22..fd7655286 100644 --- a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java +++ b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java @@ -50,24 +50,24 @@ public class Condition implements Serializable, YamlConvertible { Map parameterValues = new HashMap<>(); /** - * Instantiates a new Condition. + * Default constructor. */ public Condition() { } /** - * Instantiates a new Condition with the specified {@link ConditionType}. + * Creates a condition with the given condition type. * - * @param conditionType the condition type + * @param conditionType the condition type definition */ public Condition(ConditionType conditionType) { setConditionType(conditionType); } /** - * Retrieves the associated condition type. + * Resolved condition type definition. * - * @return the condition type + * @return the condition type, or {@code null} if only the id is set */ @XmlTransient public ConditionType getConditionType() { @@ -87,9 +87,9 @@ public void setConditionType(ConditionType conditionType) { } /** - * Retrieves the identifier of the associated condition type. + * Identifier of the condition type backing this expression. * - * @return the identifier of the associated condition type + * @return the condition type id */ @XmlElement(name="type") public String getConditionTypeId() { @@ -106,19 +106,18 @@ public void setConditionTypeId(String conditionTypeId) { } /** - * Retrieves a Map of all parameter name - value pairs for this condition. - * - * @return a Map of all parameter name - value pairs for this condition. These depend on the condition type being used in the condition. + * Parameter values supplied for this condition instance. * + * @return the parameter map; contents depend on the condition type */ public Map getParameterValues() { return parameterValues; } /** - * Sets the parameter name - value pairs for this profile. + * Sets all parameter values for this condition. * - * @param parameterValues a Map containing the parameter name - value pairs for this profile + * @param parameterValues the parameter map */ public void setParameterValues(Map parameterValues) { this.parameterValues = parameterValues != null ? parameterValues : new HashMap<>(); @@ -135,10 +134,10 @@ public boolean containsParameter(String name) { } /** - * Retrieves the parameter identified by the specified name. + * Value of the named condition parameter. * - * @param name the name of the parameter to retrieve - * @return the value of the specified parameter or {@code null} if no such parameter exists + * @param name the parameter name + * @return the parameter value, or {@code null} if unset */ public Object getParameter(String name) { return parameterValues != null ? parameterValues.get(name) : null; 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 e1a00bda1..4adadf8e6 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 @@ -49,13 +49,14 @@ public class ConditionType extends MetadataItem implements PluginType, YamlConve private long pluginId; /** - * Instantiates a new Condition type. + * Default constructor. */ public ConditionType() { } /** - * Instantiates a new Condition type with the specified metadata + * Creates a condition type with the given metadata + * * @param metadata the metadata */ public ConditionType(Metadata metadata) { @@ -63,7 +64,7 @@ public ConditionType(Metadata metadata) { } /** - * Retrieves the condition evaluator. + * Condition evaluator implementation identifier. * * @return the condition evaluator */ @@ -81,7 +82,7 @@ public void setConditionEvaluator(String conditionEvaluator) { } /** - * Retrieves the query builder. + * Query builder implementation identifier. * * @return the query builder */ @@ -99,7 +100,7 @@ public void setQueryBuilder(String queryBuilder) { } /** - * Retrieves the parent condition. + * Parent condition for composite condition types. * * @return the parent condition */ @@ -117,7 +118,7 @@ public void setParentCondition(Condition parentCondition) { } /** - * Retrieves the defined {@link Parameter}s for this ConditionType. + * Parameters defined for this condition type. * * @return a List of the defined {@link Parameter}s for this ConditionType */ @@ -166,6 +167,7 @@ public void setPluginId(long pluginId) { * Implements YamlConvertible interface with circular reference detection. * * @param visited set of already visited objects to prevent infinite recursion (may be null) + * @param maxDepth maximum recursion depth to prevent stack overflow * @return a Map representation of this condition type */ @Override 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 4053c5ae5..2b449dd39 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 @@ -65,7 +65,7 @@ public enum Type { private transient Class customType; /** - * Instantiates a new ConditionValidation. + * Default constructor. */ public ConditionValidation() { } @@ -219,6 +219,7 @@ public void setCustomType(Class customType) { * Implements YamlConvertible interface. * * @param visited set of already visited objects to prevent infinite recursion (may be null) + * @param maxDepth maximum recursion depth to prevent stack overflow * @return a Map representation of this validation */ @Override 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 d8ee9ea93..fdca53bc7 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 @@ -25,14 +25,14 @@ public class BadSegmentConditionException extends RuntimeException { /** - * Instantiates a new BadSegmentConditionException. + * Default constructor. */ public BadSegmentConditionException() { super(); } /** - * Constructs with a message. + * Creates an exception with the given message. * * @param message the error message */ @@ -41,7 +41,7 @@ public BadSegmentConditionException(String message) { } /** - * Constructs with a message and cause. + * Creates an exception with the given message and cause. * * @param message the error message * @param cause the root cause diff --git a/api/src/main/java/org/apache/unomi/api/goals/Goal.java b/api/src/main/java/org/apache/unomi/api/goals/Goal.java index efca37cc8..673191015 100644 --- a/api/src/main/java/org/apache/unomi/api/goals/Goal.java +++ b/api/src/main/java/org/apache/unomi/api/goals/Goal.java @@ -52,77 +52,70 @@ public class Goal extends MetadataItem implements YamlConvertible { private String campaignId; /** - * Constructs an empty {@link Goal} instance. - * This constructor is typically used when initializing a goal object - * programmatically before setting its specific metadata and events. + * Default constructor. */ public Goal() { } /** - * Constructs a {@link Goal} instance initialized with - * the provided metadata. - * to be identified or configured within a system context. - * @param metadata The metadata to associate with this goal, allowing it + * Creates a goal with the given metadata. + * + * @param metadata the goal metadata */ public Goal(Metadata metadata) { super(metadata); } /** - * Retrieves the {@link Condition} determining the goal's start event if any, used for more complex goals where an action has to be accomplished first before evaluating the - * success of the final goal (funnel goal for example). - * @return the condition associated with the start event for this goal or {@code null} if no such event exists + * Optional start condition for funnel-style goals. + * When set, goal tracking begins only after this condition is met. + * + * @return the start condition, or {@code null} if none is configured */ public Condition getStartEvent() { return startEvent; } /** - * Sets the condition that must occur for the goal to begin tracking. - * This is used when the goal's success depends on an initial event - * occurring, such as in a funnel scenario. - * @param startEvent The {@link Condition} defining the starting event, or - * null if no specific start event is required. + * Sets the optional start condition for funnel-style goals. + * + * @param startEvent the start condition, or {@code null} to clear it */ public void setStartEvent(Condition startEvent) { this.startEvent = startEvent; } /** - * Retrieves the {@link Condition} determining the target event which needs to occur to consider the goal accomplished. - * @return the condition associated with the event determining if the goal is reached or not + * Condition that marks the goal as accomplished when it matches. + * + * @return the target condition */ public Condition getTargetEvent() { return targetEvent; } /** - * Sets the condition that must occur for the goal to be - * considered accomplished. - * This defines the target event that determines whether the - * goal has been reached. - * @param targetEvent The {@link Condition} defining the target event, or - * null if no specific target event is required. + * Sets the condition that marks the goal as accomplished. + * + * @param targetEvent the target condition, or {@code null} to clear it */ public void setTargetEvent(Condition targetEvent) { this.targetEvent = targetEvent; } /** - * Retrieves the identifier of the campaign this goal is part of, if any. - * @return the identifier of the campaign this goal is part of, or {@code null} if this goal is not part of any campaign + * Campaign id when this goal is scoped to a campaign. + * + * @return the campaign id, or {@code null} for scope-level goals */ public String getCampaignId() { return campaignId; } /** - * Sets the identifier of the campaign to which this goal belongs. - * This allows goals to be scoped and managed within a specific - * marketing campaign context. - * @param campaignId The unique ID of the associated campaign, or - * {@code null} if the goal is not tied to a campaign. + * Sets the campaign id for campaign-scoped goals. + * + * @param campaignId the campaign id, or {@code null} for scope-level goals */ public void setCampaignId(String campaignId) { this.campaignId = campaignId; @@ -131,6 +124,7 @@ public void setCampaignId(String campaignId) { /** * Converts this goal to a Map structure for YAML output. * Implements YamlConvertible interface with circular reference detection. + * * @param visited set of already visited objects to prevent infinite recursion (may be null) * @return a Map representation of this goal */ diff --git a/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java b/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java index 0211194ba..11a442325 100644 --- a/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java +++ b/api/src/main/java/org/apache/unomi/api/goals/GoalReport.java @@ -21,9 +21,10 @@ import java.util.List; /** - * Performance statistics for a {@link org.apache.unomi.api.goals.Goal}. - * Contains counts and rates (starts, conversions, etc.) used to measure - * whether visitors complete the tracked goal. + * Goal conversion report returned by {@link org.apache.unomi.api.services.GoalsService}. + * {@link #globalStats} aggregates all traffic; {@link #split} breaks the same + * metrics down per experiment or variant key so marketers can compare branches + * of a goal or campaign. */ public class GoalReport implements Serializable { private static final long serialVersionUID = -9150361970326342064L; @@ -31,47 +32,49 @@ public class GoalReport implements Serializable { private List split; /** - * Constructs a new GoalReport instance. + * Creates an empty goal report. */ public GoalReport() { } /** - * Retrieves the global statistics associated with this report. - * @return The {@link Stat} containing global statistics. + * Aggregated statistics across all traffic. + * + * @return global statistics */ public Stat getGlobalStats() { return globalStats; } /** - * Sets the global statistics for this report. - * @param globalStats The {@link Stat} object representing - * the global statistics. + * Sets the global statistics. + * + * @param globalStats global statistics */ public void setGlobalStats(Stat globalStats) { this.globalStats = globalStats; } /** - * Retrieves the list of split statistics associated with this report. - * @return A {@link List} of {@link Stat} objects representing the splits. + * Per-split statistics (for example A/B variants). + * + * @return split statistics */ public List getSplit() { return split; } /** - * Sets the list of split statistics for this report. - * @param split The {@link List} of {@link Stat} objects - * defining the splits. + * Sets the per-split statistics. + * + * @param split split statistics */ public void setSplit(List split) { this.split = split; } /** - * Statistics + * Counts and rates for one goal report bucket. */ public static class Stat implements Serializable { private static final long serialVersionUID = 4306277648074263098L; @@ -82,97 +85,100 @@ public static class Stat implements Serializable { private double percentage; /** - * Constructs a new {@code Stat} instance. + * Creates an empty stat bucket. */ public Stat() { } /** - * Retrieves the key associated with this statistic. - * @return The unique identifier (key) for the stat. + * Bucket key (for example a split name). + * + * @return stat key */ public String getKey() { return key; } /** - * Sets the key used to identify this statistic. - * @param key The unique string key. + * Sets the bucket key. + * + * @param key stat key */ public void setKey(String key) { this.key = key; } /** - * Retrieves the starting count of records analyzed. - * @return The total number of records at the start count. + * Number of goal starts. + * + * @return start count */ public long getStartCount() { return startCount; } /** - * Sets the initial count for this statistic. - * @param startCount The total number of records. + * Sets the start count. + * + * @param startCount start count */ public void setStartCount(long startCount) { this.startCount = startCount; } /** - * Retrieves the target count of successful records. - * @return The number of records that met the target criteria. + * Number of goal completions. + * + * @return target count */ public long getTargetCount() { return targetCount; } /** - * Sets the target count for this statistic. - * @param targetCount The desired or measured target count. + * Sets the target count. + * + * @param targetCount target count */ public void setTargetCount(long targetCount) { this.targetCount = targetCount; } /** - * Retrieves the calculated conversion rate (Target - * Count / Start Count). - * @return The conversion rate as a double value. + * Conversion rate ({@code targetCount / startCount}). + * + * @return conversion rate */ public double getConversionRate() { return conversionRate; } /** - * Sets the conversion rate statistic. - * This value is typically calculated as a ratio of successful - * targets to starting counts. - * @param conversionRate the calculated conversion rate (target - * count / start count). + * Sets the conversion rate. + * + * @param conversionRate conversion rate */ public void setConversionRate(double conversionRate) { this.conversionRate = conversionRate; } /** - * Retrieves the percentage value stored in this statistic. - * This value represents a proportion, often relative to another total. - * @return the percentage value. + * Share of the total as a percentage. + * + * @return percentage value */ public double getPercentage() { return percentage; } /** - * Sets the calculated percentage for this goal report statistic. - * This value is typically derived by dividing target counts by - * a larger total count. - * @param percentage the calculated percentage value. + * Sets the percentage value. + * + * @param percentage percentage value */ public void setPercentage(double percentage) { this.percentage = percentage; } } -} \ No newline at end of file +} diff --git a/api/src/main/java/org/apache/unomi/api/lists/UserList.java b/api/src/main/java/org/apache/unomi/api/lists/UserList.java index 35d99d446..e7f11cb16 100644 --- a/api/src/main/java/org/apache/unomi/api/lists/UserList.java +++ b/api/src/main/java/org/apache/unomi/api/lists/UserList.java @@ -38,12 +38,12 @@ public class UserList extends MetadataItem{ private static final long serialVersionUID = -1384533444875433496L; /** - * Instantiates a new UserList. + * Default constructor. */ public UserList() {} /** - * Instantiates a new UserList with the specified metadata. + * Creates a user list with the given metadata. * * @param metadata the metadata */ 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 9c2f3ffca..3dc959fc2 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 @@ -42,13 +42,13 @@ public class Aggregate implements Serializable { /** - * Instantiates a new Aggregate. + * Default constructor. */ public Aggregate() { } /** - * Retrieves the aggregation type. + * Aggregation type. *

* Supported values are {@code date}, {@code dateRange}, {@code numericRange}, and {@code ipRange}. * When {@code null}, a terms aggregation is used on distinct property values. @@ -72,7 +72,7 @@ public void setType(String type) { } /** - * Retrieves the aggregation parameters. + * Aggregation parameters. *

* For {@code date} aggregations, expected keys are {@code interval} and {@code format}. * For {@code dateRange} aggregations, the {@code format} key defines the date format. @@ -96,7 +96,7 @@ public void setParameters(Map parameters) { } /** - * Retrieves the property to aggregate on. + * Property to aggregate on. * * @return the property to aggregate on */ @@ -114,7 +114,7 @@ public void setProperty(String property) { } /** - * Retrieves the numeric ranges used by {@code numericRange} aggregations. + * Numeric ranges for {@code numericRange} aggregations. * * @return the numeric ranges */ @@ -123,7 +123,7 @@ public List getNumericRanges() { } /** - * Retrieves the date ranges used by {@code dateRange} aggregations. + * Date ranges for {@code dateRange} aggregations. * * @return the date ranges */ @@ -141,7 +141,7 @@ public void setDateRanges(List dateRanges) { } /** - * Retrieves the IP ranges used by {@code ipRange} aggregations. + * IP ranges for {@code ipRange} aggregations. * * @return the IP ranges */ diff --git a/api/src/main/java/org/apache/unomi/api/query/AggregateQuery.java b/api/src/main/java/org/apache/unomi/api/query/AggregateQuery.java index 0f44b3b33..96ce0db9c 100644 --- a/api/src/main/java/org/apache/unomi/api/query/AggregateQuery.java +++ b/api/src/main/java/org/apache/unomi/api/query/AggregateQuery.java @@ -30,13 +30,13 @@ public class AggregateQuery implements Serializable { private Condition condition; /** - * Instantiates a new Aggregate query. + * Default constructor. */ public AggregateQuery() { } /** - * Instantiates a new Aggregate query with the specified {@link Aggregate}. + * Creates an aggregate query with the given aggregate specification. * * @param aggregate the aggregate */ @@ -45,7 +45,7 @@ public AggregateQuery(Aggregate aggregate) { } /** - * Instantiates a new Aggregate query with the specified {@link Condition}. + * Creates an aggregate query with the given filter condition. * * @param condition the condition */ @@ -54,7 +54,7 @@ public AggregateQuery(Condition condition) { } /** - * Instantiates a new Aggregate query with the specified {@link Aggregate} and {@link Condition} + * Creates an aggregate query with the given aggregate and filter condition * * @param aggregate the aggregate * @param condition the condition @@ -65,7 +65,7 @@ public AggregateQuery(Aggregate aggregate, Condition condition) { } /** - * Retrieves the aggregate. + * Aggregate specification for this query. * * @return the aggregate */ @@ -83,7 +83,7 @@ public void setAggregate(Aggregate aggregate) { } /** - * Retrieves the condition. + * Filter condition for this query. * * @return the condition */ diff --git a/api/src/main/java/org/apache/unomi/api/query/DateRange.java b/api/src/main/java/org/apache/unomi/api/query/DateRange.java index 029073759..cfdfb01e5 100644 --- a/api/src/main/java/org/apache/unomi/api/query/DateRange.java +++ b/api/src/main/java/org/apache/unomi/api/query/DateRange.java @@ -20,9 +20,10 @@ import java.io.Serializable; /** - * Named inclusive date interval used in queries and aggregations. - * Pairs a label ({@code key}) with {@code from} and {@code to} bounds so - * conditions can match events or profiles inside a calendar window. + * Named inclusive date bucket for queries and aggregations. + * Combines a display {@code key} with {@code from} and {@code to} bounds so + * segment conditions and aggregate queries can group events or profiles inside + * a calendar window. */ public class DateRange implements Serializable { private String key; @@ -30,54 +31,60 @@ public class DateRange implements Serializable { private Object to; /** - * Constructs a new {@link DateRange} instance. + * Creates an empty date range. */ public DateRange() { } /** - * Retrieves the unique key associated with this date range. - * @return The string key of the range. + * Label for this range in query results. + * + * @return range key */ public String getKey() { return key; } /** - * Sets the unique identifier (key) for this date range. - * @param key the key to set + * Sets the range label. + * + * @param key range key */ public void setKey(String key) { this.key = key; } /** - * Retrieves the starting value (from) of the date range. - * @return The object representing the start time or value. + * Inclusive lower bound of the interval. + * + * @return start date or value */ public Object getFrom() { return from; } /** - * Sets the starting value (from) for this date range. - * @param from the starting value to set + * Sets the inclusive lower bound. + * + * @param from start date or value */ public void setFrom(Object from) { this.from = from; } /** - * Retrieves the ending value (to) of the date range. - * @return The object representing the end time or value. + * Inclusive upper bound of the interval. + * + * @return end date or value */ public Object getTo() { return to; } /** - * Sets the ending value (to) for this date range. - * @param to the ending value to set + * Sets the inclusive upper bound. + * + * @param to end date or value */ public void setTo(Object to) { this.to = to; diff --git a/api/src/main/java/org/apache/unomi/api/query/IpRange.java b/api/src/main/java/org/apache/unomi/api/query/IpRange.java index b33f16dcc..a2deb51ed 100644 --- a/api/src/main/java/org/apache/unomi/api/query/IpRange.java +++ b/api/src/main/java/org/apache/unomi/api/query/IpRange.java @@ -20,9 +20,9 @@ import java.io.Serializable; /** - * Named inclusive IP address interval for geo or network filtering. - * Used in segment conditions to match visitors whose IP falls between - * the configured lower and upper bounds. + * Named inclusive IP range used in geo or network segment conditions. + * The {@code from} and {@code to} strings define lower and upper bounds checked + * when evaluating whether a visitor's IP address belongs to the range. */ public class IpRange implements Serializable { private String key; @@ -30,54 +30,60 @@ public class IpRange implements Serializable { private String to; /** - * Constructs an empty {@link IpRange} instance. + * Creates an empty IP range. */ public IpRange() { } /** - * Retrieves the unique key associated with this IP range. - * @return The string key identifying the range. + * Label for this range in query results. + * + * @return range key */ public String getKey() { return key; } /** - * Sets the unique identifier (key) for this IP range. - * @param key The key to assign to the range. + * Sets the range label. + * + * @param key range key */ public void setKey(String key) { this.key = key; } /** - * Retrieves the starting IP address string of the range. - * @return The 'from' boundary of the IP range. + * Inclusive lower IP bound. + * + * @return start IP address */ public String getFrom() { return from; } /** - * Sets the starting IP address string for this range. - * @param from The start boundary of the IP range. + * Sets the inclusive lower IP bound. + * + * @param from start IP address */ public void setFrom(String from) { this.from = from; } /** - * Retrieves the ending IP address string of the range. - * @return The 'to' boundary of the IP range. + * Inclusive upper IP bound. + * + * @return end IP address */ public String getTo() { return to; } /** - * Sets the ending IP address string for this range. - * @param to The end boundary of the IP range. + * Sets the inclusive upper IP bound. + * + * @param to end IP address */ public void setTo(String to) { this.to = to; diff --git a/api/src/main/java/org/apache/unomi/api/query/NumericRange.java b/api/src/main/java/org/apache/unomi/api/query/NumericRange.java index bb8933785..9a493b79b 100644 --- a/api/src/main/java/org/apache/unomi/api/query/NumericRange.java +++ b/api/src/main/java/org/apache/unomi/api/query/NumericRange.java @@ -20,9 +20,9 @@ import java.io.Serializable; /** - * Named inclusive numeric interval for query filters. - * Stores a label plus lower and upper bounds so conditions can match - * numeric profile or event properties in a range. + * Named inclusive numeric bucket for query filters and aggregations. + * Stores a {@code key} plus {@code from}/{@code to} bounds so conditions can + * test profile or event properties against numeric intervals. */ public class NumericRange implements Serializable { private String key; @@ -30,55 +30,60 @@ public class NumericRange implements Serializable { private Double to; /** - * Constructs a new NumericRange object. + * Creates an empty numeric range. */ public NumericRange() { } /** - * Retrieves the unique key associated with this numeric range. - * @return The string key identifying the range. + * Label for this range in query results. + * + * @return range key */ public String getKey() { return key; } /** - * Sets the unique identifier (key) for this numeric range. - * @param key the key to set + * Sets the range label. + * + * @param key range key */ public void setKey(String key) { this.key = key; } /** - * Retrieves the starting value of the numeric range. - * @return The lower bound (inclusive start) of the - * range, or null if not set. + * Inclusive lower bound. + * + * @return lower bound, or {@code null} if unset */ public Double getFrom() { return from; } /** - * Sets the starting value for this numeric range. - * @param from the inclusive starting value of the range + * Sets the inclusive lower bound. + * + * @param from lower bound */ public void setFrom(Double from) { this.from = from; } /** - * Retrieves the ending value of the numeric range. - * @return The upper bound (inclusive end) of the range, or null if not set. + * Inclusive upper bound. + * + * @return upper bound, or {@code null} if unset */ public Double getTo() { return to; } /** - * Sets the ending value for this numeric range. - * @param to the inclusive ending value of the range + * Sets the inclusive upper bound. + * + * @param to upper bound */ public void setTo(Double to) { this.to = to; diff --git a/api/src/main/java/org/apache/unomi/api/query/Query.java b/api/src/main/java/org/apache/unomi/api/query/Query.java index bcfc9e3d0..9e3e0fbe2 100644 --- a/api/src/main/java/org/apache/unomi/api/query/Query.java +++ b/api/src/main/java/org/apache/unomi/api/query/Query.java @@ -22,9 +22,10 @@ import java.io.Serializable; /** - * Full specification for a context server search or aggregation. - * Combines a {@link Condition}, paging (offset/limit), sort order, and - * optional aggregate definitions in one request object. + * Search and paging request sent to {@link org.apache.unomi.api.services.QueryService} + * and profile/segment REST endpoints. + * Combines an optional full-text filter, a {@link Condition}, sort field, offset/limit, + * and a {@code forceRefresh} flag that controls whether indexes are refreshed first. */ public class Query implements Serializable { private String text; @@ -37,153 +38,152 @@ public class Query implements Serializable { private String scrollIdentifier; /** - * Instantiates a new Query. + * Default constructor. */ public Query() { } /** - * Retrieves the text to be used in full-text searches, if any. - * @return the text to be used in full-text searches or {@code null} if no full-text search is needed for this Query + * Optional full-text filter applied to the search. + * + * @return the search text, or {@code null} if none is set */ public String getText() { return text; } /** - * Sets to be used in full-text searches - * @param text the text to be used in full-text searches or {@code null} if no full-text search is needed for this Query + * Sets the full-text filter. + * + * @param text the search text, or {@code null} to disable full-text search */ public void setText(String text) { this.text = text; } - /** - * Retrieves the offset of the first element to be retrieved - * @return zero or a positive integer specifying the position of the first item to be retrieved in the total ordered collection of matching items + * Zero-based index of the first result to return. + * + * @return the result offset */ public int getOffset() { return offset; } /** - * Sets the offset of the first element to be retrieved - * @param offset zero or a positive integer specifying the position of the first item to be retrieved in the total ordered collection of matching items + * Sets the zero-based result offset. + * + * @param offset the first result index */ public void setOffset(int offset) { this.offset = offset; } /** - * Retrieves the number of elements to retrieve. - * @return a positive integer specifying how many matching items should be retrieved or {@code -1} if all of them should be retrieved + * Maximum number of results to return, or {@code -1} for all matches. + * + * @return the result limit */ public int getLimit() { return limit; } /** - * Sets the number of elements to retrieve. - * @param limit a positive integer specifying how many matching items should be retrieved or {@code -1} if all of them should be retrieved + * Sets the maximum number of results to return. + * + * @param limit the result limit, or {@code -1} for all matches */ public void setLimit(int limit) { this.limit = limit; } /** - * Retrieves the sorting specifications for this Query in String format, if any. - * @return an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements - * according to the property order in the String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is - * optionally followed by a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. + * Sort specification as a comma-separated property list. + * Each property may be followed by {@code :asc} or {@code :desc}. + * + * @return the sort specification, or {@code null} if unsorted */ public String getSortby() { return sortby; } /** - * Sets the String representation of the sorting specifications for this Query if any. See {@link #getSortby()} method documentation for format. - * @param sortby the String representation of the sorting specifications for this Query or {@code null} if no sorting is required + * Sets the sort specification. + * See {@link #getSortby()} for the expected format. + * + * @param sortby the sort specification, or {@code null} for no sorting */ public void setSortby(String sortby) { this.sortby = sortby; } /** - * Retrieves the {@link Condition} associated with this Query. - * @return the {@link Condition} associated with this Query + * Structured filter condition for the query. + * + * @return the condition, or {@code null} if none is set */ public Condition getCondition() { return condition; } /** - * Sets the {@link Condition} associated with this Query. - * @param condition the {@link Condition} associated with this Query + * Sets the structured filter condition. + * + * @param condition the condition */ public void setCondition(Condition condition) { this.condition = condition; } /** - * Determines whether or not an index refresh is needed before performing this Query - * @return {@code true} if an index refresh is needed before performing this Query, {@code false} otherwise + * Whether the search index should be refreshed before executing the query. + * + * @return {@code true} to force a refresh, {@code false} otherwise */ public boolean isForceRefresh() { return forceRefresh; } /** - * Specifies whether or not an index refresh is needed before performing this Query. - * @param forceRefresh {@code true} if an index refresh is needed before performing this Query, {@code false} otherwise + * Sets whether to refresh the index before executing the query. + * + * @param forceRefresh {@code true} to force a refresh */ public void setForceRefresh(boolean forceRefresh) { this.forceRefresh = forceRefresh; } /** - * Retrieves the unique identifier used to continue a - * scrolling search session. - * This identifier must be provided when performing subsequent searches - * that rely on the results of a previous query. - * @return the scroll identifier, or {@code null} if no - * scroll session is active. + * Scroll token for continuing a deep result set query. + * + * @return the scroll identifier, or {@code null} if scrolling is not active */ public String getScrollIdentifier() { return scrollIdentifier; } /** - * Sets the unique identifier required to continue a - * scrolling search session. - * This value should be obtained from the results of a previous query when - * performing subsequent searches that rely on the continuity of results. - * @param scrollIdentifier the scroll identifier used for continuation, or - * {@code null} if no scroll is needed. + * Sets the scroll token for continuing a scroll query. + * + * @param scrollIdentifier the scroll identifier, or {@code null} to clear it */ public void setScrollIdentifier(String scrollIdentifier) { this.scrollIdentifier = scrollIdentifier; } /** - * Retrieves the configured time validity period for the - * current scroll session. - * This value determines how long the search results remain valid for - * continuation queries. - * @return the string representation of the time validity (e.g., "10m"), or - * {@code null} if no specific validity is set. + * How long the scroll context remains valid (for example {@code 10m}). + * + * @return the scroll validity period, or {@code null} if not set */ public String getScrollTimeValidity() { return scrollTimeValidity; } /** - * Sets the time validity period for the current scroll session. - * This value specifies how long the search results remain available for - * continuation queries. - * @param scrollTimeValidity the string representation of the time validity - * (e.g., "10m"), or {@code null} if no specific - * validity is required. + * Sets how long the scroll context remains valid. + * + * @param scrollTimeValidity the validity period (for example {@code 10m}) */ public void setScrollTimeValidity(String scrollTimeValidity) { this.scrollTimeValidity = scrollTimeValidity; diff --git a/api/src/main/java/org/apache/unomi/api/rules/Rule.java b/api/src/main/java/org/apache/unomi/api/rules/Rule.java index cd6f751e5..1e2586f18 100644 --- a/api/src/main/java/org/apache/unomi/api/rules/Rule.java +++ b/api/src/main/java/org/apache/unomi/api/rules/Rule.java @@ -64,13 +64,13 @@ public class Rule extends MetadataItem implements YamlConvertible { private int priority; /** - * Instantiates a new Rule. + * Default constructor. */ public Rule() { } /** - * Instantiates a new Rule with the specified {@link Metadata}. + * Creates a rule with the given metadata. * * @param metadata the metadata */ @@ -79,7 +79,7 @@ public Rule(Metadata metadata) { } /** - * Retrieves the condition that, when satisfied, triggers the rule. + * Condition that triggers the rule when satisfied. * * @return the condition that, when satisfied, triggers the rule. */ @@ -97,7 +97,7 @@ public void setCondition(Condition condition) { } /** - * Retrieves the actions to be performed when this rule triggers. + * Actions performed when this rule triggers. * * @return the actions to be performed when this rule triggers */ @@ -115,7 +115,7 @@ public void setActions(List actions) { } /** - * Retrieves the linked items. + * Identifiers of items linked to this rule. * * @return the linked items */ @@ -142,9 +142,9 @@ public boolean isRaiseEventOnlyOnceForProfile() { } /** - * Determines whether the event raised when the rule is triggered should only be raised once + * Whether the rule-triggered event should be raised only once per incoming event. * - * @return {@code true} if the rule-triggered event should only be raised once per profile + * @return {@code true} if the rule-triggered event should only be raised once per event */ public boolean isRaiseEventOnlyOnce() { return raiseEventOnlyOnce; @@ -187,7 +187,7 @@ public void setRaiseEventOnlyOnce(boolean raiseEventOnlyOnce) { } /** - * Retrieves the priority in case this Rule needs to be executed before other ones when similar conditions match. + * Priority when this rule must run before others with similar conditions before other ones when similar conditions match. * * @return the priority */ @@ -209,6 +209,7 @@ public void setPriority(int priority) { * Implements YamlConvertible interface with circular reference detection. * * @param visited set of already visited objects to prevent infinite recursion (may be null) + * @param maxDepth maximum recursion depth to prevent stack overflow * @return a Map representation of this rule */ @Override diff --git a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java index 2cad6e77b..80cf18ac4 100644 --- a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java +++ b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java @@ -21,9 +21,13 @@ import java.util.Date; /** - * A separate item to track rule statistics, because we will manage the persistence and updating of - * these seperately from the rules themselves. This object contains all the relevant statistics - * concerning the execution of a rule, including accumulated execution times. + * Persisted statistics for a {@link org.apache.unomi.api.rules.Rule}, stored separately from the rule + * definition so counters can be updated without rewriting the rule itself. + *

+ * Cluster-wide fields ({@link #executionCount}, {@link #conditionsTime}, {@link #actionsTime}) hold + * aggregated totals synchronized across nodes. Matching {@code local*} fields track counts and + * timings on the current node since {@link #lastSyncDate}; they are merged into the cluster totals + * during synchronization. */ public class RuleStatistics extends Item { @@ -43,135 +47,141 @@ public class RuleStatistics extends Item { private Date lastSyncDate; /** - * Constructs a new RuleStatistics instance with default values. - * This constructor is used when initializing statistics objects without - * an associated item ID. + * Default constructor. */ public RuleStatistics() { } /** - * Constructs a new RuleStatistics instance, associating it with - * the given rule item ID. - * @param itemId The unique identifier of the rule item to which - * these statistics belong. + * Creates statistics for the rule with the given identifier. + * + * @param itemId the rule item identifier these statistics belong to */ public RuleStatistics(String itemId) { super(itemId); } /** - * Retrieve the execution count of the rule in the cluster - * @return a long that is the total number of executions of the rule without the local node execution - * count + * Cluster-wide execution count (excluding the current node's unsynchronized local count). + * + * @return the cluster execution count */ public long getExecutionCount() { return executionCount; } /** - * Set the execution count of the rule in the cluster - * @param executionCount a long that represents the number of execution of the rule in the cluster + * Sets the cluster-wide execution count. + * + * @param executionCount the cluster execution count */ public void setExecutionCount(long executionCount) { this.executionCount = executionCount; } /** - * Retrieve the execution count of the rule on this single node since the last sync with the cluster - * @return a long that is the total number of executions on this node since the last sync with the - * cluster + * Execution count on this node since the last cluster synchronization. + * + * @return the local execution count */ public long getLocalExecutionCount() { return localExecutionCount; } /** - * Sets the number of local execution counts for this node since the last sync with the cluster - * @param localExecutionCount a long that represents the number of execution of the rule since the - * last sync with the cluster + * Sets the local execution count since the last cluster synchronization. + * + * @param localExecutionCount the local execution count */ public void setLocalExecutionCount(long localExecutionCount) { this.localExecutionCount = localExecutionCount; } /** - * Retrieve the accumulated time evaluating the conditions of the rule in the cluster - * @return a long representing the accumulated time in milliseconds that represents the time spent - * evaluating the conditions of the rule for the whole cluster + * Cluster-wide accumulated time spent evaluating rule conditions, in milliseconds. + * + * @return the cluster conditions time in milliseconds */ public long getConditionsTime() { return conditionsTime; } /** - * Sets the execution time of the condition of the rule for the whole cluster - * @param conditionsTime a long representing a time in milliseconds + * Sets the cluster-wide accumulated conditions evaluation time. + * + * @param conditionsTime the cluster conditions time in milliseconds */ public void setConditionsTime(long conditionsTime) { this.conditionsTime = conditionsTime; } /** - * Retrieve the accumulated execution time of the rule's condition since the last sync with the cluster - * @return a long that represents the accumulated time in milliseconds + * Local accumulated time spent evaluating rule conditions since the last cluster sync, in milliseconds. + * + * @return the local conditions time in milliseconds */ public long getLocalConditionsTime() { return localConditionsTime; } /** - * Sets the accumulated execution time of the rule's condition since the last sync with the cluster - * @param localConditionsTime a long that represents the accumulated time in milliseconds + * Sets the local accumulated conditions evaluation time since the last cluster sync. + * + * @param localConditionsTime the local conditions time in milliseconds */ public void setLocalConditionsTime(long localConditionsTime) { this.localConditionsTime = localConditionsTime; } /** - * Retrieve the accumulated time of the rule's actions - * @return a long representing the accumulated time in milliseconds + * Cluster-wide accumulated time spent executing rule actions, in milliseconds. + * + * @return the cluster actions time in milliseconds */ public long getActionsTime() { return actionsTime; } /** - * Sets the accumulated time for the rule's actions - * @param actionsTime a long representing the accumulated time in milliseconds + * Sets the cluster-wide accumulated actions execution time. + * + * @param actionsTime the cluster actions time in milliseconds */ public void setActionsTime(long actionsTime) { this.actionsTime = actionsTime; } /** - * Retrieve the accumulated time spent executing the rule's actions since the last sync with the cluster - * @return a long representing the accumulated time in milliseconds + * Local accumulated time spent executing rule actions since the last cluster sync, in milliseconds. + * + * @return the local actions time in milliseconds */ public long getLocalActionsTime() { return localActionsTime; } /** - * Sets the accumulated time spend executing the rule's actions since the last sync with the cluster - * @param localActionsTime a long representing the accumulated time in milliseconds + * Sets the local accumulated actions execution time since the last cluster sync. + * + * @param localActionsTime the local actions time in milliseconds */ public void setLocalActionsTime(long localActionsTime) { this.localActionsTime = localActionsTime; } /** - * Retrieve the last sync date - * @return a date that was set the last time the statistics were synchronized with the cluster + * Date when local counters were last merged into the cluster totals. + * + * @return the last synchronization date */ public Date getLastSyncDate() { return lastSyncDate; } /** - * Sets the last sync date - * @param lastSyncDate a date that represents the last time the statistics were synchronized - * with the cluster + * Sets the last cluster 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/security/EncryptionService.java b/api/src/main/java/org/apache/unomi/api/security/EncryptionService.java index 5d23c84af..f3c1c2e8a 100644 --- a/api/src/main/java/org/apache/unomi/api/security/EncryptionService.java +++ b/api/src/main/java/org/apache/unomi/api/security/EncryptionService.java @@ -22,18 +22,18 @@ */ public interface EncryptionService { /** - * Get the encryption key for a specific tenant. + * Encryption key for the specified tenant. * * @param tenantId the tenant ID - * @return the encryption key as a byte array + * @return the encryption key bytes */ byte[] getTenantEncryptionKey(String tenantId); /** - * Generate a new encryption key for a tenant. + * Generates a new encryption key for the specified tenant. * * @param tenantId the tenant ID - * @return the newly generated encryption key + * @return the newly generated encryption key bytes */ byte[] generateTenantEncryptionKey(String tenantId); -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/security/SecurityService.java b/api/src/main/java/org/apache/unomi/api/security/SecurityService.java index 6b5f92cc3..656f88f88 100644 --- a/api/src/main/java/org/apache/unomi/api/security/SecurityService.java +++ b/api/src/main/java/org/apache/unomi/api/security/SecurityService.java @@ -35,7 +35,7 @@ public interface SecurityService { String SYSTEM_TENANT = "system"; /** - * Retrieves the current subject from the security context. The subject is determined in the following order: + * Current subject from the security context. The subject is determined in the following order: * 1. JAAS context - If a JAAS authentication is active * 2. Privileged subject - If a temporary privileged operation is in progress * 3. Current request subject - The subject associated with the current request @@ -45,7 +45,7 @@ public interface SecurityService { Subject getCurrentSubject(); /** - * Retrieves the current principal from the active subject. + * Current principal from the active subject. * The principal represents the primary identity of the authenticated entity. * * @return the current principal or null if no subject is set or the subject has no principals @@ -128,7 +128,7 @@ public interface SecurityService { void executeWithPrivilegedSubject(Subject privilegedSubject, Runnable operation); /** - * Retrieves the current tenant ID based on the active subject context. + * Current tenant identifier from the active subject context. * The tenant ID is determined from the subject's principal. * * @return the current tenant ID, or SYSTEM_TENANT if operating in system context @@ -144,7 +144,7 @@ public interface SecurityService { boolean isOperatingOnSystemTenant(); /** - * Retrieves the encryption key for a specific tenant. + * Encryption key for the specified tenant. * This key is used for encrypting sensitive data within the tenant's context. * * @param tenantId the ID of the tenant whose encryption key should be retrieved @@ -207,20 +207,23 @@ public interface SecurityService { boolean hasSystemAccess(); /** - * Get the system subject with administrative privileges + * System subject with administrative privileges. + * * @return the system subject */ Subject getSystemSubject(); /** - * Extract roles from a subject + * Role names granted to the given subject. + * * @param subject the subject to extract roles from * @return set of role names */ Set extractRolesFromSubject(Subject subject); /** - * Get the security service configuration + * Security service configuration. + * * @return the security configuration */ SecurityServiceConfiguration getConfiguration(); diff --git a/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java b/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java index 058b183fb..b3f4d0c52 100644 --- a/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java +++ b/api/src/main/java/org/apache/unomi/api/security/SecurityServiceConfiguration.java @@ -109,9 +109,7 @@ public class SecurityServiceConfiguration { private boolean enableEncryption = false; /** - * Constructs a default security service configuration, initializing system - * roles and mapping permissions for standard operations like query, save, - * update, etc., with default roles. + * Initializes default system roles and permission-to-role mappings. */ public SecurityServiceConfiguration() { // Initialize default system roles @@ -137,106 +135,100 @@ public SecurityServiceConfiguration() { } /** - * Retrieves the map containing defined permission keys and the array of - * roles authorized to use them. - * The returned map is a direct reference to the internal configuration. - * @return A {@link Map} where keys are permission strings and values are - * arrays of required role names. + * Permission keys mapped to the roles allowed to perform them. + * + * @return the permission-to-roles map */ public Map getPermissionRoles() { return permissionRoles; } /** - * Sets the complete mapping of permissions to roles for the security - * service. This allows external configuration of which roles can perform - * specific operations. - * @param permissionRoles The map containing all permission keys and their - * associated authorized roles. + * Replaces the full permission-to-role mapping. + * + * @param permissionRoles the permission-to-roles map */ public void setPermissionRoles(Map permissionRoles) { this.permissionRoles = permissionRoles; } /** - * Retrieves the default role assigned to users or - * entities within the system. - * @return The configured default role string. + * Role assigned when no explicit mapping exists. + * + * @return the default role name */ public String getDefaultRole() { return defaultRole; } /** - * Sets the default role that should be applied when a user - * or entity is created. - * @param defaultRole the role to set as the default for new entities. + * Sets the default role name. + * + * @param defaultRole the default role */ public void setDefaultRole(String defaultRole) { this.defaultRole = defaultRole; } /** - * Get required roles for an permission - * @param permission the permission to check - * @return array of required roles, or array containing default role if permission not mapped + * Roles required for the given permission. + * + * @param permission the permission key + * @return the required roles, or an array containing the default role if unmapped */ public String[] getRequiredRolesForPermission(String permission) { return permissionRoles.getOrDefault(permission, new String[]{defaultRole}); } /** - * Gets the immutable set of system roles recognized by the security - * service, such as administrative or core platform roles. - * @return A {@link Set} of strings representing the defined system roles. + * Platform roles recognized by the security service. + * + * @return the system role names */ public Set getSystemRoles() { return systemRoles; } /** - * Sets all system roles that the security service should - * recognize and manage. - * @param systemRoles a set containing all system role names. + * Replaces the set of recognized system roles. + * + * @param systemRoles the system role names */ public void setSystemRoles(Set systemRoles) { this.systemRoles = systemRoles; } /** - * Adds a single specified role name to the collection of - * recognized system roles. - * This allows dynamic configuration of core platform roles. - * @param role the role string to add to the system roles list. + * Adds a system role name. + * + * @param role the role to add */ public void addSystemRole(String role) { systemRoles.add(role); } /** - * Removes a specified role name from the set of recognized system roles. - * This should be used when a system role is deprecated or removed. - * @param role the role string to remove from the system roles list. + * Removes a system role name. + * + * @param role the role to remove */ public void removeSystemRole(String role) { systemRoles.remove(role); } /** - * Checks if profile data encryption has been enabled for - * the security service. - * @return {@code true} if encryption is active, {@code false} otherwise. + * Whether profile data encryption is enabled. + * + * @return {@code true} if encryption is enabled */ public boolean isEnableEncryption() { return enableEncryption; } /** - * Sets whether sensitive profile data should be encrypted when stored and - * retrieved by the security service. This controls the use of - * encryption mechanisms. - * @param enableEncryption {@code true} to enable encryption, - * {@code false} otherwise. + * Enables or disables profile data encryption. + * + * @param enableEncryption {@code true} to enable encryption */ public void setEnableEncryption(boolean enableEncryption) { this.enableEncryption = enableEncryption; diff --git a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java index 0eec441e4..30926539f 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java +++ b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java @@ -34,10 +34,10 @@ public class DependentMetadata implements Serializable { private List scorings; /** - * Constructs a {@link DependentMetadata} instance with specified - * segment and scoring lists. - * @param segments the list of metadata defining required segments. - * @param scorings the list of metadata defining associated scores. + * Creates dependent metadata from segment and scoring definitions. + * + * @param segments metadata for dependent segments + * @param scorings metadata for dependent scorings */ public DependentMetadata(List segments, List scorings) { this.segments = segments; @@ -45,32 +45,36 @@ public DependentMetadata(List segments, List scorings) { } /** - * Retrieves the list of metadata representing the dependent segments. - * @return a {@link List} containing the segment definitions. + * Metadata for segments that depend on the edited item. + * + * @return the dependent segment metadata */ public List getSegments() { return segments; } /** - * Sets the list of metadata defining the required segments for this object. - * @param segments the new list of segment metadata. + * Sets the dependent segment metadata. + * + * @param segments the segment metadata list */ public void setSegments(List segments) { this.segments = segments; } /** - * Retrieves the list of metadata representing the dependent scorings. - * @return a {@link List} containing the scoring definitions. + * Metadata for scorings that depend on the edited item. + * + * @return the dependent scoring metadata */ public List getScorings() { return scorings; } /** - * Sets the list of metadata defining the associated scores for this object. - * @param scorings the new list of score metadata. + * Sets the dependent scoring metadata. + * + * @param scorings the scoring metadata list */ public void setScorings(List scorings) { this.scorings = scorings; diff --git a/api/src/main/java/org/apache/unomi/api/segments/Scoring.java b/api/src/main/java/org/apache/unomi/api/segments/Scoring.java index b32aa8ab0..85e36ad1e 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/Scoring.java +++ b/api/src/main/java/org/apache/unomi/api/segments/Scoring.java @@ -45,13 +45,13 @@ public class Scoring extends MetadataItem implements YamlConvertible { private List elements; /** - * Instantiates a new Scoring. + * Default constructor. */ public Scoring() { } /** - * Instantiates a new Scoring with the specified metadata. + * Creates a scoring definition with the given metadata. * * @param metadata the metadata */ @@ -60,9 +60,9 @@ public Scoring(Metadata metadata) { } /** - * Retrieves the details of this Scoring. + * Scoring elements that define conditions and point values. * - * @return the elements + * @return the scoring elements */ public List getElements() { return elements; @@ -82,6 +82,7 @@ public void setElements(List elements) { * Implements YamlConvertible interface with circular reference detection. * * @param visited set of already visited objects to prevent infinite recursion (may be null) + * @param maxDepth maximum recursion depth to prevent stack overflow * @return a Map representation of this scoring */ @Override diff --git a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java index 801132c57..7467f5788 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java +++ b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java @@ -39,13 +39,13 @@ public class ScoringElement implements Serializable, YamlConvertible { private int value; /** - * Instantiates a new Scoring element. + * Default constructor. */ public ScoringElement() { } /** - * Retrieves the condition. + * Condition evaluated to decide whether this scoring element applies. * * @return the condition */ @@ -54,7 +54,7 @@ public Condition getCondition() { } /** - * Sets the condition. + * Sets the matching condition. * * @param condition the condition */ @@ -63,18 +63,18 @@ public void setCondition(Condition condition) { } /** - * Retrieves the value. + * Score contributed when the condition matches. * - * @return the value + * @return the score value */ public int getValue() { return value; } /** - * Sets the value. + * Sets the score value contributed when the condition matches. * - * @param value the value + * @param value the score value */ public void setValue(int value) { this.value = value; diff --git a/api/src/main/java/org/apache/unomi/api/segments/Segment.java b/api/src/main/java/org/apache/unomi/api/segments/Segment.java index 126279c15..b5a4758ab 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/Segment.java +++ b/api/src/main/java/org/apache/unomi/api/segments/Segment.java @@ -49,13 +49,13 @@ public class Segment extends MetadataItem implements YamlConvertible { private Condition condition; /** - * Instantiates a new Segment. + * Default constructor. */ public Segment() { } /** - * Instantiates a new Segment with the specified metadata. + * Creates a segment with the given metadata. * * @param metadata the metadata */ @@ -64,9 +64,9 @@ public Segment(Metadata metadata) { } /** - * Retrieves the condition that users' {@link Profile} must satisfy in order to be considered member of this Segment. + * Condition that profiles must satisfy to belong to this segment. * - * @return the condition that users must match + * @return the membership condition */ public Condition getCondition() { return condition; @@ -86,6 +86,7 @@ public void setCondition(Condition condition) { * Implements YamlConvertible interface with circular reference detection. * * @param visited set of already visited objects to prevent infinite recursion (may be null) + * @param maxDepth maximum recursion depth to prevent stack overflow * @return a Map representation of this segment */ @Override diff --git a/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java b/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java index da8fe4dec..0ba7dd634 100644 --- a/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java +++ b/api/src/main/java/org/apache/unomi/api/segments/SegmentsAndScores.java @@ -24,39 +24,39 @@ import java.util.Set; /** - * Snapshot of segment memberships and scoring values for a profile. - * Returned when clients need both which segments apply and the current - * score totals in one structure. + * Segment memberships and scoring totals for one {@link org.apache.unomi.api.Profile}. + * {@link org.apache.unomi.api.services.ProfileService} returns this snapshot when + * clients need both current segment ids and per-scoring-plan scores in a single + * response (for example after context resolution or profile reads). */ public class SegmentsAndScores implements Serializable { private Set segments; private Map scores; /** - * Instantiates a new SegmentsAndScores. + * Creates a snapshot with the given segment memberships and scores. * - * @param segments the set of segment identifiers - * @param scores the scores as a Map of scoring name - associated score pairs + * @param segments segment identifiers + * @param scores map of scoring name to score value */ public SegmentsAndScores(Set segments, Map scores) { this.segments = segments; this.scores = scores; } - /** - * Retrieves the segments identifiers. + * Segment ids the profile belongs to. * - * @return the segments identifiers + * @return segment identifiers */ public Set getSegments() { return segments; } /** - * Retrieves the scores as a Map of scoring name - associated score pairs. + * Scoring totals keyed by scoring plan name. * - * @return the scores as a Map of scoring name - associated score pairs + * @return map of scoring name to score value */ public Map getScores() { return scores; diff --git a/api/src/main/java/org/apache/unomi/api/services/ClusterService.java b/api/src/main/java/org/apache/unomi/api/services/ClusterService.java index 1b507959c..7d81d095a 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ClusterService.java +++ b/api/src/main/java/org/apache/unomi/api/services/ClusterService.java @@ -30,24 +30,24 @@ public interface ClusterService { /** - * Retrieves the list of available nodes for this context server instance. + * Returns cluster nodes known to this instance. * - * @return a list of {@link ClusterNode} + * @return cluster nodes in the current topology */ List getClusterNodes(); /** - * Removes all data before the specified date from the context server. + * Deletes all persisted data older than the given cutoff date. * - * @param date the Date before which all data needs to be removed + * @param date cutoff; data before this date is removed */ @Deprecated void purge(final Date date); /** - * Removes all data associated with the provided scope. + * Deletes all persisted data belonging to the given scope. * - * @param scope the scope for which we want to remove data + * @param scope scope whose data should be removed */ void purge(final String scope); 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 5757b3133..b25cdebef 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 @@ -38,8 +38,9 @@ public interface ConditionValidationService { *

* 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 when the condition is valid + * @return validation errors, or an empty list when valid */ List validate(Condition condition); @@ -56,7 +57,8 @@ class ValidationError { private final ValidationError parentError; /** - * Instantiates a validation error for a single parameter. + * Creates 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 @@ -66,7 +68,8 @@ public ValidationError(String parameterName, String message, ValidationErrorType } /** - * Instantiates a validation error with full condition context and optional parent error. + * Creates 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 @@ -88,40 +91,45 @@ public ValidationError(String parameterName, String message, ValidationErrorType } /** - * Retrieves the name of the parameter that caused the error. - * @return the parameter name, or {@code null} if not applicable + * Parameter that failed validation. + * + * @return parameter name, or {@code null} if not applicable */ public String getParameterName() { return parameterName; } /** - * Retrieves the error description message. - * @return the error message + * Human-readable error description. + * + * @return error message */ public String getMessage() { return message; } /** - * Retrieves the type of validation error. - * @return the error type + * Category of validation failure. + * + * @return 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 + * Condition instance that failed validation. + * + * @return 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 + * Condition type definition that was validated. + * + * @return condition type identifier, or {@code null} if not applicable */ public String getConditionTypeId() { return conditionTypeId; @@ -129,7 +137,8 @@ public String getConditionTypeId() { /** * @deprecated Use {@link #getConditionTypeId()} instead. - * @return the condition type identifier (same as {@link #getConditionTypeId()}) + * + * @return condition type identifier (same as {@link #getConditionTypeId()}) */ @Deprecated public String getConditionTypeName() { @@ -137,24 +146,27 @@ public String getConditionTypeName() { } /** - * Retrieves a copy of the additional context map for this error. - * @return the context map + * Defensive copy of supplemental error context. + * + * @return context map */ public Map getContext() { return new HashMap<>(context); } /** - * Retrieves the parent error that caused this error, if any. - * @return the parent error, or {@code null} if none + * Underlying error that caused this one, when chained. + * + * @return parent error, or {@code null} if none */ public ValidationError getParentError() { return parentError; } /** - * Retrieves a detailed error message including condition, parameter, context, and parent error information. - * @return the detailed error message + * Builds a message with condition, parameter, context, and parent-error details. + * + * @return detailed error message */ public String getDetailedMessage() { StringBuilder sb = new StringBuilder(); @@ -222,20 +234,18 @@ enum ValidationErrorType { private final String description; /** - * Constructs an error type, setting its descriptive label. - * This initializes the {@code ValidationErrorType} constant with a - * specific human-readable description used to identify - * validation failures. - * @param description the descriptive text associated - * with this error type. + * Associates a human-readable label with this error category. + * + * @param description descriptive text for this error type */ ValidationErrorType(String description) { this.description = description; } /** - * Retrieves the human-readable description of this error type. - * @return the error type description + * Human-readable label for this error category. + * + * @return 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 d847905e5..03309e309 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 @@ -34,7 +34,7 @@ public interface ConfigSharingService { /** - * Retrieves the value of the named property. + * Value of the named property. * * @param name the property name * @return the property value, or {@code null} if not set @@ -67,7 +67,7 @@ public interface ConfigSharingService { Object removeProperty(String name); /** - * Retrieves the names of all currently known properties. + * Names of all currently known properties. * * @return the property names */ @@ -94,7 +94,7 @@ public enum ConfigChangeEventType { private Object newValue; /** - * Instantiates a configuration change event. + * Creates a configuration change event. * * @param eventType the type of change * @param name the property name @@ -109,7 +109,7 @@ public ConfigChangeEvent(ConfigChangeEventType eventType, String name, Object ol } /** - * Retrieves the type of configuration change. + * Type of configuration change. * * @return the event type */ @@ -118,7 +118,7 @@ public ConfigChangeEventType getEventType() { } /** - * Retrieves the name of the changed property. + * Name of the changed property. * * @return the property name */ @@ -127,7 +127,7 @@ public String getName() { } /** - * Retrieves the previous value of the property. + * Previous value of the property. * * @return the old value, or {@code null} for {@link ConfigChangeEventType#ADDED} events */ @@ -136,7 +136,7 @@ public Object getOldValue() { } /** - * Retrieves the new value of the property. + * New value of the property. * * @return the new value, or {@code null} for {@link ConfigChangeEventType#REMOVED} events */ diff --git a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java index 96e00a065..ea0bd538f 100644 --- a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java +++ b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java @@ -37,199 +37,196 @@ */ public interface DefinitionsService { /** - * Retrieves all condition types. + * Returns every registered condition type. * - * @return a Collection of all collection types + * @return all condition types */ Collection getAllConditionTypes(); /** - * Retrieves the set of condition types with the specified tag. + * Returns condition types tagged with the given tag, including sub-tags. * - * @param tag the tag marking the condition types we want to retrieve - * @return the set of condition types with the specified tag (and its sub-tags, if specified) + * @param tag tag marking the condition types to include + * @return condition types with the specified tag */ Set getConditionTypesByTag(String tag); /** - * Retrieves the set of condition types with the specified system tag. + * Returns condition types with the given system tag, including sub-tags. * - * @param tag the system tag marking the condition types we want to retrieve - * @return the set of condition types with the specified tag (and its sub-tags, if specified) + * @param tag system tag marking the condition types to include + * @return condition types with the specified system tag */ Set getConditionTypesBySystemTag(String tag); /** - * Retrieves the condition type associated with the specified identifier. + * Looks up a condition type by id. * - * @param id the identifier of the condition type to retrieve - * @return the condition type associated with the specified identifier or {@code null} if no such condition type exists + * @param id condition type identifier + * @return matching condition type, or {@code null} if none exists */ ConditionType getConditionType(String id); /** - * Stores the condition type + * Registers or updates a condition type definition. * - * @param conditionType the condition type to store + * @param conditionType condition type to store */ void setConditionType(ConditionType conditionType); /** - * Remove the condition type + * Removes a condition type definition. * - * @param id the condition type to remove + * @param id identifier of the condition type to remove */ void removeConditionType(String id); /** - * Retrieves all known action types. + * Returns every registered action type. * - * @return all known action types + * @return all action types */ Collection getAllActionTypes(); /** - * Retrieves the set of action types with the specified tag. + * Returns action types tagged with the given tag. * - * @param tag the tag marking the action types we want to retrieve - * @return the set of action types with the specified tag + * @param tag tag marking the action types to include + * @return action types with the specified tag */ Set getActionTypeByTag(String tag); /** - * Retrieves the set of action types with the specified system tag. + * Returns action types with the given system tag. * - * @param tag the system tag marking the action types we want to retrieve - * @return the set of action types with the specified tag + * @param tag system tag marking the action types to include + * @return action types with the specified system tag */ Set getActionTypeBySystemTag(String tag); /** - * Retrieves the action type associated with the specified identifier. + * Looks up an action type by id. * - * @param id the identifier of the action type to retrieve - * @return the action type associated with the specified identifier or {@code null} if no such action type exists + * @param id action type identifier + * @return matching action type, or {@code null} if none exists */ ActionType getActionType(String id); /** - * Stores the action type + * Registers or updates an action type definition. * - * @param actionType the action type to store + * @param actionType action type to store */ void setActionType(ActionType actionType); /** - * Remove the action type + * Removes an action type definition. * - * @param id the action type to remove + * @param id identifier of the action type to remove */ void removeActionType(String id); /** - * Retrieves all known value types. + * Returns every registered value type. * - * @return all known value types + * @return all value types */ Collection getAllValueTypes(); /** - * Retrieves the set of value types with the specified tag. + * Returns value types tagged with the given tag. * - * @param tag the tag marking the value types we want to retrieve - * @return the set of value types with the specified tag + * @param tag tag marking the value types to include + * @return value types with the specified tag */ Set getValueTypeByTag(String tag); /** - * Retrieves the value type associated with the specified identifier. + * Looks up a value type by id. * - * @param id the identifier of the value type to retrieve - * @return the value type associated with the specified identifier or {@code null} if no such value type exists + * @param id value type identifier + * @return matching value type, or {@code null} if none exists */ ValueType getValueType(String id); /** - * Stores the value type + * Registers or updates a value type definition. * - * @param valueType the value type to store + * @param valueType value type to store */ void setValueType(ValueType valueType); /** - * Remove the value type + * Removes a value type definition. * - * @param id the value type to remove + * @param id identifier of the value type to remove */ void removeValueType(String id); /** - * Retrieves a Map of plugin identifier to a list of plugin types defined by that particular plugin. + * Groups registered plugin types by plugin id. * - * @return a Map of plugin identifier to a list of plugin types defined by that particular plugin + * @return map of plugin id to plugin types defined by that plugin */ Map> getTypesByPlugin(); /** - * Retrieves the property merge strategy type associated with the specified identifier. + * Looks up a property merge strategy type by id. * - * @param id the identifier of the property merge strategy type to retrieve - * @return the property merge strategy type associated with the specified identifier or {@code null} if no such property merge strategy type exists + * @param id property merge strategy type identifier + * @return matching type, or {@code null} if none exists */ PropertyMergeStrategyType getPropertyMergeStrategyType(String id); /** - * Stores the property merge strategy type + * Registers or updates a property merge strategy type. * - * @param propertyMergeStrategyType the property merge strategy type to store + * @param propertyMergeStrategyType property merge strategy type to store */ void setPropertyMergeStrategyType(PropertyMergeStrategyType propertyMergeStrategyType); /** - * Remove the property merge strategy type + * Removes a property merge strategy type definition. * - * @param id the property merge strategy type to remove + * @param id identifier of the property merge strategy type to remove */ void removePropertyMergeStrategyType(String id); /** - * Retrieves all known property merge strategy types. + * Returns every registered property merge strategy type. * - * @return all known property merge strategy types + * @return all property merge strategy types */ Collection getAllPropertyMergeStrategyTypes(); /** - * Retrieves all conditions of the specified type from the specified root condition. + * Collects nested conditions of the given type from a root condition tree. * - * TODO: remove? * - * @param rootCondition the condition from which we want to extract all conditions with the specified type - * @param typeId the identifier of the condition type we want conditions to extract to match - * @return a set of conditions contained in the specified root condition and matching the specified condition type or an empty set if no such condition exists + * @param rootCondition condition tree to walk + * @param typeId condition type id to match + * @return matching nested conditions, or an empty list if none */ List extractConditionsByType(Condition rootCondition, String typeId); /** - * Retrieves a condition matching the specified tag identifier from the specified root condition. + * Finds the first nested condition tagged with the given tag in a root condition tree. * - * TODO: remove from API and move to a different class? - * TODO: purpose and behavior not clear - * - * @param rootCondition the root condition where to start the extraction by class - * @param tag the tag to use to extract the condition - * @return Condition the condition that has been found matching the tag, or null if none matched + * Deprecated helper that may move out of this service in a future release. * + * @param rootCondition condition tree to walk + * @param tag tag used to select a condition + * @return first matching condition, or {@code null} if none * @deprecated As of 1.2.0-incubating, please use {@link #extractConditionBySystemTag(Condition, String)} instead */ @Deprecated Condition extractConditionByTag(Condition rootCondition, String tag); /** - * Retrieves a condition matching the specified system tag identifier from the specified root condition. + * Finds the first nested condition with the given system tag in a root condition tree. * - * @param rootCondition the root condition where to start the extraction by class - * @param systemTag the tag to use to extract the condition - * @return Condition the condition that has been found matching the tag, or null if none matched + * @param rootCondition condition tree to walk + * @param systemTag system tag used to select a condition + * @return first matching condition, or {@code null} if none */ Condition extractConditionBySystemTag(Condition rootCondition, String systemTag); @@ -263,26 +260,23 @@ public interface DefinitionsService { void refresh(); /** - * Gets the condition builder instance. + * Returns the shared condition builder for programmatic condition construction. * - * @return the condition builder instance + * @return condition builder instance */ ConditionBuilder getConditionBuilder(); /** - * Gets the TypeResolutionService instance. - * This service handles type resolution and invalid object tracking. + * Returns the type resolution service for condition and action type lookup. * - * @return the TypeResolutionService instance + * @return type resolution service instance */ TypeResolutionService getTypeResolutionService(); /** - * Gets the ConditionValidationService instance. - * This service validates conditions against their type definitions. - * The service automatically resolves condition types if needed before validation. + * Returns the condition validation service, which resolves types before validating parameters. * - * @return the ConditionValidationService instance + * @return condition validation service instance */ ConditionValidationService getConditionValidationService(); } diff --git a/api/src/main/java/org/apache/unomi/api/services/EventService.java b/api/src/main/java/org/apache/unomi/api/services/EventService.java index f251c1961..d6634bce2 100644 --- a/api/src/main/java/org/apache/unomi/api/services/EventService.java +++ b/api/src/main/java/org/apache/unomi/api/services/EventService.java @@ -53,112 +53,106 @@ public interface EventService { int PROFILE_UPDATED = 4; /** - * Propagates the specified event in the context server, notifying - * {@link EventListenerService} instances if needed. If the event is persistent ({@link Event#isPersistent()}, it will be persisted appropriately. Once the event is - * propagated, any {@link ActionPostExecutor} the event defined will be executed and the user profile updated if needed. + * Publishes an event, notifying listeners and persisting it when marked persistent. + * Runs post-actions and updates the profile or session when rules require it. * - * @param event the Event to be propagated - * @return the result of the event handling as combination of EventService flags, to be checked using bitwise AND (&) operator + * @param event event to propagate + * @return bitmask of {@link #NO_CHANGE}, {@link #ERROR}, {@link #SESSION_UPDATED}, and {@link #PROFILE_UPDATED} */ int send(Event event); /** - * Check if the tenant is allowed to send the specified event. Restricted events must be explicitly allowed for a tenant. + * Checks whether the tenant may send the given event. + * Restricted event types must be explicitly allowed per tenant. * - * @param event event to test - * @param tenantId the ID of the tenant - * @param sourceIP the IP address from which the event was sent (not persisted for privacy) - * @return true if the event is allowed for the tenant + * @param event event to test + * @param tenantId tenant identifier + * @param sourceIP client IP (not persisted for privacy) + * @return {@code true} if the event is allowed */ boolean isEventAllowedForTenant(Event event, String tenantId, String sourceIP); /** - * Retrieves the list of available event properties. + * Returns known event property definitions. * - * @return a list of available event properties + * @return available event properties * @deprecated use event types instead */ List getEventProperties(); /** - * Retrieves the set of known event type identifiers. + * Returns ids of all registered event types. * - * @return the set of known event type identifiers. + * @return known event type identifiers */ Set getEventTypeIds(); /** - * Retrieves {@link Event}s matching the specified {@link Condition}. Events are ordered according to their time stamp ({@link Event#getTimeStamp()} and paged: only - * {@code size} of them are retrieved, starting with the {@code offset}-th one. + * Searches events matching a condition, ordered by timestamp and paged. * - * @param condition the Condition we want the Events to match to be retrieved - * @param offset zero or a positive integer specifying the position of the first event in the total ordered collection of matching events - * @param size a positive integer specifying how many matching events should be retrieved or {@code -1} if all of them should be retrieved - * @return a {@link PartialList} of matching events + * @param condition filter condition + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @return matching events */ PartialList searchEvents(Condition condition, int offset, int size); /** - * Retrieves {@link Event}s for the {@link Session} identified by the provided session identifier, matching any of the provided event types, - * ordered according to the specified {@code sortBy} String and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. - * If a {@code query} is provided, a full text search is performed on the matching events to further filter them. + * Searches session events by type with optional full-text filtering, ordered and paged. * - * @param sessionId the identifier of the user session we're considering - * @param eventTypes an array of event type names; the events to retrieve should at least match one of these - * @param query a String to perform full text filtering on events matching the other conditions - * @param offset zero or a positive integer specifying the position of the first event in the total ordered collection of matching events - * @param size a positive integer specifying how many matching events should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in - * the String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of matching events + * @param sessionId session identifier + * @param eventTypes event types to include (any match) + * @param query optional full-text filter, or {@code null} + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return matching events */ PartialList searchEvents(String sessionId, String[] eventTypes, String query, int offset, int size, String sortBy); /** - * Retrieves {@link Event}s matching the specified {@link Query}. + * Searches events using a structured query. * - * @param query a {@link Query} specifying which Events to retrieve - * @return a {@link PartialList} of {@code Event} instances matching the specified query + * @param query query specifying which events to return + * @return matching events */ PartialList search(Query query); - /** - * Retrieves the {@link Event} by its identifier. + * Loads an event by id. * - * @param id the identifier of the {@link Event} to retrieve - * @return the {@link Event} identified by the specified identifier or {@code null} if no such profile exists + * @param id event identifier + * @return matching event, or {@code null} if none exists */ Event getEvent(final String id); /** - * Checks whether the specified event has already been raised either for the associated session or profile depending on the specified {@code session} parameter. + * Checks whether an equivalent event was already raised for the session or profile. * - * @param event the event we want to check - * @param session {@code true} if we want to check if the specified event has already been raised for the associated session, {@code false} if we want to check - * whether the event has already been raised for the associated profile - * @return {@code true} if the event has already been raised, {@code false} otherwise + * @param event event to check + * @param session {@code true} to check the session history, {@code false} for the profile history + * @return {@code true} if a matching event already exists */ boolean hasEventAlreadyBeenRaised(Event event, boolean session); /** - * Checks whether the specified event has already been raised with the same itemId. + * Checks whether an event with the same item id was already raised. * - * @param event the event we want to check - * @return {@code true} if the event has already been raised, {@code false} otherwise + * @param event event to check + * @return {@code true} if a matching event already exists */ boolean hasEventAlreadyBeenRaised(Event event); /** - * Removes all events of the specified profile + * Deletes all events belonging to the given profile. * - * @param profileId identifier of the profile that we want to remove it's events + * @param profileId profile whose events should be removed */ void removeProfileEvents(String profileId); /** - * Delete an event by specifying its event identifier - * @param eventIdentifier the unique identifier for the event + * Deletes a single event by id. + * + * @param eventIdentifier event identifier */ void deleteEvent(String eventIdentifier); } diff --git a/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java b/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java index a163edd4d..64011c5ac 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java +++ b/api/src/main/java/org/apache/unomi/api/services/ExecutionContextManager.java @@ -28,18 +28,21 @@ public interface ExecutionContextManager { /** * Gets the current execution context. + * * @return the current execution context */ ExecutionContext getCurrentContext(); /** * Sets the current execution context. + * * @param context the context to set as current */ void setCurrentContext(ExecutionContext context); /** * Executes an operation as the system user. + * * @param operation the operation to execute * @param the return type of the operation * @return the result of the operation @@ -48,6 +51,7 @@ public interface ExecutionContextManager { /** * Executes an operation as the system user without return value. + * * @param operation the operation to execute */ void executeAsSystem(Runnable operation); @@ -55,6 +59,7 @@ public interface ExecutionContextManager { /** * Executes an operation as a specific tenant. * This method creates a tenant context, executes the operation, and ensures proper cleanup. + * * @param tenantId the ID of the tenant to execute as * @param operation the operation to execute * @param the return type of the operation @@ -65,6 +70,7 @@ public interface ExecutionContextManager { /** * Executes an operation as a specific tenant without return value. * This method creates a tenant context, executes the operation, and ensures proper cleanup. + * * @param tenantId the ID of the tenant to execute as * @param operation the operation to execute */ @@ -72,6 +78,7 @@ public interface ExecutionContextManager { /** * Creates a new execution context for the given tenant. + * * @param tenantId the tenant ID * @return the created execution context */ diff --git a/api/src/main/java/org/apache/unomi/api/services/GoalsService.java b/api/src/main/java/org/apache/unomi/api/services/GoalsService.java index c04c3a06b..955619f2a 100644 --- a/api/src/main/java/org/apache/unomi/api/services/GoalsService.java +++ b/api/src/main/java/org/apache/unomi/api/services/GoalsService.java @@ -37,137 +37,134 @@ */ public interface GoalsService { /** - * Retrieves the set of Metadata associated with existing goals. + * Returns metadata for all goals. * - * @return the set of Metadata associated with existing goals + * @return goal metadata entries */ Set getGoalMetadatas(); /** - * Retrieves the set of Metadata associated with existing goals matching the specified {@link Query} + * Returns metadata for goals matching the given query. * - * @param query the Query used to filter the Goals which metadata we want to retrieve - * @return the set of Metadata associated with existing goals matching the specified {@link Query} + * @param query filter for goals whose metadata should be returned + * @return matching goal metadata entries */ Set getGoalMetadatas(Query query); /** - * Retrieves the goal associated with the specified identifier. + * Loads a goal by id. * - * @param goalId the identifier of the goal to retrieve - * @return the goal associated with the specified identifier or {@code null} if no such goal exists + * @param goalId goal identifier + * @return matching goal, or {@code null} if none exists */ Goal getGoal(String goalId); /** - * Saves the specified goal in the context server and creates associated {@link Rule}s if the goal is enabled. + * Saves a goal and creates associated rules when the goal is enabled. * - * TODO: rename to saveGoal - * - * @param goal the Goal to be saved + * The { setGoal} name is historical; a { saveGoal} alias may be added later. * + * @param goal goal to save */ void setGoal(Goal goal); /** - * Removes the goal associated with the specified identifier, also removing associated rules if needed. + * Deletes a goal and its associated rules when present. * - * @param goalId the identifier of the goal to be removed + * @param goalId goal identifier */ void removeGoal(String goalId); /** - * Retrieves the report for the goal identified with the specified identifier. + * Builds a performance report for the given goal. * - * @param goalId the identifier of the goal which report we want to retrieve - * @return the report for the specified goal + * @param goalId goal identifier + * @return goal report */ GoalReport getGoalReport(String goalId); /** - * Retrieves the report for the goal identified with the specified identifier, considering only elements determined by the specified {@link AggregateQuery}. + * Builds a performance report for the given goal, filtered by an aggregate query. * - * @param goalId the identifier of the goal which report we want to retrieve - * @param query an AggregateQuery to further specify which elements of the report we want - * @return the report for the specified goal and query + * @param goalId goal identifier + * @param query aggregate query limiting report elements + * @return goal report for the query scope */ GoalReport getGoalReport(String goalId, AggregateQuery query); /** - * Retrieves the set of Metadata associated with existing campaigns. + * Returns metadata for all campaigns. * - * @return the set of Metadata associated with existing campaigns + * @return campaign metadata entries */ Set getCampaignMetadatas(); /** - * Retrieves the set of Metadata associated with existing campaign matching the specified {@link Query} + * Returns metadata for campaigns matching the given query. * - * @param query the Query used to filter the campagins which metadata we want to retrieve - * @return the set of Metadata associated with existing campaigns matching the specified {@link Query} + * @param query filter for campaigns whose metadata should be returned + * @return matching campaign metadata entries */ Set getCampaignMetadatas(Query query); /** - * Retrieves campaign details for campaigns matching the specified query. + * Returns detailed campaign records matching the given query. * - * @param query the query specifying which campaigns to retrieve - * @return a {@link PartialList} of campaign details for the campaigns matching the specified query + * @param query filter for campaigns to return + * @return matching campaign details */ PartialList getCampaignDetails(Query query); /** - * Retrieves the {@link CampaignDetail} associated with the campaign identified with the specified identifier + * Loads detailed campaign information by id. * - * @param id the identifier of the campaign for which we want to retrieve the details - * @return the CampaignDetail for the campaign identified by the specified identifier or {@code null} if no such campaign exists + * @param id campaign identifier + * @return campaign details, or {@code null} if none exists */ CampaignDetail getCampaignDetail(String id); /** - * Retrieves the campaign identified by the specified identifier + * Loads a campaign by id. * - * @param campaignId the identifier of the campaign we want to retrieve - * @return the campaign associated with the specified identifier or {@code null} if no such campaign exists + * @param campaignId campaign identifier + * @return matching campaign, or {@code null} if none exists */ Campaign getCampaign(String campaignId); /** - * Saves the specified campaign in the context server and creates associated {@link Rule}s if the campaign is enabled. - * - * TODO: rename to saveCampaign + * Saves a campaign and creates associated rules when the campaign is enabled. * - * @param campaign the Campaign to be saved + * The { setCampaign} name is historical; a { saveCampaign} alias may be added later. * + * @param campaign campaign to save */ void setCampaign(Campaign campaign); /** - * Removes the campaign associated with the specified identifier, also removing associated rules if needed. + * Deletes a campaign and its associated rules when present. * - * @param campaignId the identifier of the campaign to be removed + * @param campaignId campaign identifier */ void removeCampaign(String campaignId); /** - * Retrieves {@link CampaignEvent}s matching the specified query. + * Searches campaign events matching the given query. * - * @param query the Query specifying which CampaignEvents to retrieve - * @return a {@link PartialList} of campaign events matching the specified query + * @param query filter for campaign events to return + * @return matching campaign events */ PartialList getEvents(Query query); /** - * Saves the specified campaign event in the context server. - * - * TODO: rename to saveCampaignEvent + * Saves a campaign event. * - * @param event the CampaignEvent to be saved + * The { setEvent} name is historical; a { saveCampaignEvent} alias may be added later. * + * @param event campaign event to save */ void setCampaignEvent(CampaignEvent event); /** - * Removes the campaign event associated with the specified identifier. + * Deletes a campaign event by id. * - * @param campaignEventId the identifier of the campaign event to be removed + * @param campaignEventId campaign event identifier */ void removeCampaignEvent(String campaignEventId); } diff --git a/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java b/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java index aa443af7b..131cb31ea 100644 --- a/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java +++ b/api/src/main/java/org/apache/unomi/api/services/InvalidObjectInfo.java @@ -23,9 +23,10 @@ import java.util.concurrent.atomic.AtomicLong; /** - * Diagnostic record for a configuration item that failed validation. - * Lists missing condition or action types and the contexts where the - * invalid object was seen so operators can fix definitions. + * Accumulated record for a definition that references missing condition or action types. + * {@link org.apache.unomi.api.services.TypeResolutionService} creates and updates + * these entries when JSON rules, segments, or other items fail type resolution. + * Operators use the missing-type lists and encounter counts to fix broken imports. */ public class InvalidObjectInfo { private final String objectType; @@ -39,36 +40,28 @@ public class InvalidObjectInfo { private final Set contextNames; /** - * Constructs an {@code InvalidObjectInfo} object using only the basic - * identifying information. - * This constructor delegates to the full constructor, initializing missing - * lists and context names as null or empty. - * @param objectType The type name of the invalid object. Must not be null. - * @param objectId The unique ID of the invalid object. Must not be null. - * @param reason A detailed explanation for why the object - * is considered invalid. + * Creates a record with type, id, and reason only. + * + * @param objectType invalid object type + * @param objectId invalid object id + * @param reason why the object is invalid */ public InvalidObjectInfo(String objectType, String objectId, String reason) { this(objectType, objectId, reason, null, null, null); } /** - * Constructs an {@code InvalidObjectInfo} object with full details, - * including initial tracking information. - * This initializes timestamps and encounter counts upon creation. - * @param objectType The type name of the invalid object. Must not be null. - * @param objectId The unique ID of the invalid object. Must not be null. - * @param reason A detailed explanation for why the object - * is considered invalid. - * @param missingConditionTypeIds Initial list of condition types that were - * missing during validation. - * @param missingActionTypeIds Initial list of action types that were - * missing during validation. - * @param contextName The specific context where this initial encounter - * occurred. Can be null. + * Creates a record with initial missing-type and context details. + * + * @param objectType invalid object type + * @param objectId invalid object id + * @param reason why the object is invalid + * @param missingConditionTypeIds missing condition types from the first encounter, or {@code null} + * @param missingActionTypeIds missing action types from the first encounter, or {@code null} + * @param contextName context where the object was first seen, or {@code null} */ - public InvalidObjectInfo(String objectType, String objectId, String reason, - List missingConditionTypeIds, + public InvalidObjectInfo(String objectType, String objectId, String reason, + List missingConditionTypeIds, List missingActionTypeIds, String contextName) { this.objectType = objectType; @@ -88,80 +81,81 @@ public InvalidObjectInfo(String objectType, String objectId, String reason, } /** - * Retrieves the type name associated with this invalid object record. - * @return The {@code String} representing the object's type name. + * Invalid object type. + * + * @return object type */ public String getObjectType() { return objectType; } /** - * Retrieves the unique ID of the invalid object record. - * @return The {@code String} representing the object's ID. + * Invalid object id. + * + * @return object id */ public String getObjectId() { return objectId; } /** - * Retrieves the reason why this object was flagged as invalid. - * @return The detailed explanation for the invalidation, or {@code null} - * if none is provided. + * Why the object failed validation. + * + * @return reason, or {@code null} if unset */ public String getReason() { return reason; } /** - * Gets the timestamp when this object record was first observed as invalid. - * @return The initial {@code long} timestamp (milliseconds since epoch). + * When this record was first created (milliseconds since epoch). + * + * @return first-seen timestamp */ public long getFirstSeenTimestamp() { return firstSeenTimestamp; } /** - * Gets the most recent timestamp when this object record was - * observed as invalid. - * This value is updated whenever an encounter occurs. - * @return The latest {@code long} timestamp (milliseconds since epoch). + * When this object was last seen as invalid (milliseconds since epoch). + * + * @return last-seen timestamp */ public long getLastSeenTimestamp() { return lastSeenTimestamp.get(); } /** - * Returns the total number of times this object has been - * encountered as invalid. - * @return An integer count representing the cumulative encounters. + * How many times this invalid object has been encountered. + * + * @return encounter count */ public int getEncounterCount() { return encounterCount.get(); } /** - * Retrieves an unmodifiable list of condition type IDs that were found to - * be missing or unresolved for this object instance. - * @return A {@link java.util.List} containing the - * missing condition type IDs. + * Condition types that could not be resolved for this object. + * + * @return unmodifiable list of missing condition type ids */ public List getMissingConditionTypeIds() { return Collections.unmodifiableList(new ArrayList<>(missingConditionTypeIds)); } /** - * Retrieves an unmodifiable list of action type IDs that were found to be - * missing or unresolved for this object instance. - * @return A {@link java.util.List} containing the missing action type IDs. + * Action types that could not be resolved for this object. + * + * @return unmodifiable list of missing action type ids */ public List getMissingActionTypeIds() { return Collections.unmodifiableList(new ArrayList<>(missingActionTypeIds)); } /** - * Returns an unmodifiable set of context names in which this invalid - * object was encountered. - * @return A {@link java.util.Set} containing all recorded context names. + * Contexts where this invalid object was seen. + * + * @return unmodifiable set of context names */ public Set getContextNames() { return Collections.unmodifiableSet(contextNames); @@ -172,9 +166,10 @@ public Set getContextNames() { * Thread-safe: backed by CopyOnWriteArrayList/CopyOnWriteArraySet, so reads via * {@code getMissingConditionTypeIds()}, {@code getMissingActionTypeIds()}, and * {@code getContextNames()} are safe during concurrent writes. - * @param missingConditionTypeIds additional missing condition type IDs found in this encounter - * @param missingActionTypeIds additional missing action type IDs found in this encounter - * @param contextName context where this encounter occurred + * + * @param missingConditionTypeIds additional missing condition type ids from this encounter + * @param missingActionTypeIds additional missing action type ids from this encounter + * @param contextName context where this encounter occurred */ public void updateEncounter(List missingConditionTypeIds, List missingActionTypeIds, @@ -230,4 +225,3 @@ public String toString() { return sb.toString(); } } - diff --git a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java index 4c0c7b9d9..bcd9a238c 100644 --- a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java +++ b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java @@ -33,29 +33,32 @@ public interface PersonalizationService { /** - * Check if an item is visible for the specified profile and session - * @param profile The profile - * @param session The session - * @param personalizedContent Personalized content, containing a list of filters - * @return If the content is visible or not + * Evaluates whether personalized content is visible for the given profile and session. + * + * @param profile visitor profile + * @param session visitor session + * @param personalizedContent content definition with filters to evaluate + * @return {@code true} if the content should be shown */ boolean filter(Profile profile, Session session, PersonalizedContent personalizedContent); /** - * Get the best match among a list of items, for the specified profile and session - * @param profile The profile - * @param session The session - * @param personalizationRequest Personalization request, containing the list of variants and the required strategy - * @return The id of the best-matching variant + * Selects the best-matching content variant for the given profile and session. + * + * @param profile visitor profile + * @param session visitor session + * @param personalizationRequest request with variants and selection strategy + * @return id of the best-matching variant */ String bestMatch(Profile profile, Session session, PersonalizationRequest personalizationRequest); /** - * Get a personalized list, filtered and sorted, based on the profile and session - * @param profile The profile - * @param session The session - * @param personalizationRequest Personalization request, containing the list of variants and the required strategy - * @return List of ids, based on user profile + * Filters and ranks content variants for the given profile and session. + * + * @param profile visitor profile + * @param session visitor session + * @param personalizationRequest request with variants and selection strategy + * @return ordered personalization result for the visitor */ PersonalizationResult personalizeList(Profile profile, Session session, PersonalizationRequest personalizationRequest); @@ -69,68 +72,72 @@ class PersonalizationRequest { private List contents; /** - * Gets the unique identifier of this personalization request. - * @return The ID string. + * Request identifier. + * + * @return request id */ public String getId() { return id; } /** - * Sets the unique identifier for this personalization request. - * @param id The unique ID to set. + * Sets the request identifier. + * + * @param id request id */ public void setId(String id) { this.id = id; } /** - * Retrieves the name of the personalization strategy to be used. - * @return The configured strategy name string. + * Personalization strategy name (for example {@code alwaysSet}). + * + * @return strategy name */ public String getStrategy() { return strategy; } /** - * Sets the personalization strategy that should process this request. - * @param strategy The name of the strategy (e.g., "alwaysSet"). + * Sets the personalization strategy name. + * + * @param strategy strategy name (for example {@code alwaysSet}) */ public void setStrategy(String strategy) { this.strategy = strategy; } /** - * Gets the list of personalized content items - * associated with this request. - * @return A list of {@link PersonalizedContent} objects. + * Content variants included in this request. + * + * @return personalized content items */ public List getContents() { return contents; } /** - * Sets the collection of personalized content that should be processed. - * @param contents The list of {@link PersonalizedContent} to set. + * Sets the content variants for this request. + * + * @param contents personalized content items */ public void setContents(List contents) { this.contents = contents; } /** - * Retrieves optional configuration parameters specific to the - * personalization strategy. - * @return A map containing strategy-specific options, or - * null if none are set. + * Strategy-specific options passed to the personalization engine. + * + * @return strategy options, or {@code null} if none */ public Map getStrategyOptions() { return strategyOptions; } /** - * Sets the map of optional configuration parameters for the - * personalization process. - * @param strategyOptions The map of options to apply. + * Sets strategy-specific options for this request. + * + * @param strategyOptions strategy options map */ public void setStrategyOptions(Map strategyOptions) { this.strategyOptions = strategyOptions; @@ -146,16 +153,18 @@ class PersonalizedContent { private Map properties; /** - * Retrieves the filter identifier associated with this content filtering definition. - * @return the filter identifier associated with this content filtering definition + * Content variant identifier. + * + * @return content id */ public String getId() { return id; } /** - * Sets the filter identifier associated with this content filtering definition. - * @param id the filter identifier associated with this content filtering definition + * Sets the content variant identifier. + * + * @param id content id */ public void setId(String id) { this.id = id; @@ -172,32 +181,36 @@ public void setFilterid(String filterid) { } /** - * Retrieves the filters. - * @return the filters + * Filters applied to this content variant. + * + * @return filter definitions */ public List getFilters() { return filters; } /** - * Sets the filters. - * @param filters the filters + * Sets the filters for this content variant. + * + * @param filters filter definitions */ public void setFilters(List filters) { this.filters = filters; } /** - * Retrieves the properties associated with this content definition. - * @return The map of properties stored in this object. + * Additional properties attached to this content variant. + * + * @return content properties */ public Map getProperties() { return properties; } /** - * Sets the properties associated with this content definition. - * @param properties The map containing the properties to set. + * Sets additional properties for this content variant. + * + * @param properties content properties */ public void setProperties(Map properties) { this.properties = properties; @@ -213,51 +226,54 @@ class Filter { private Map properties; /** - * Retrieves the list of targets this filter applies on. - * @return the applies on + * Targets this filter should be evaluated against. + * + * @return applicable targets */ public List getAppliesOn() { return appliesOn; } /** - * Specifies which targets this filter applies on. - * @param appliesOn the list of {@link Target} this filter should be applied on + * Sets the targets this filter applies to. + * + * @param appliesOn applicable targets */ public void setAppliesOn(List appliesOn) { this.appliesOn = appliesOn; } /** - * Retrieves the condition associated with this filter. - * @return the condition associated with this filter + * Condition evaluated when applying this filter. + * + * @return filter condition */ public Condition getCondition() { return condition; } /** - * Sets the condition associated with this filter. - * @param condition the condition associated with this filter + * Sets the condition for this filter. + * + * @param condition filter condition */ public void setCondition(Condition condition) { this.condition = condition; } /** - * Retrieves the map of properties associated with this filter. - * This method returns a reference to the internal - * {@code properties} map. - * @return the properties map + * Additional properties for this filter. + * + * @return filter properties map */ public Map getProperties() { return properties; } /** - * Sets the properties associated with this filter. - * The provided map replaces any existing properties. - * @param properties the map of properties to set + * Sets additional properties for this filter. + * + * @param properties filter properties map */ public void setProperties(Map properties) { this.properties = properties; @@ -272,32 +288,36 @@ class Target { private List values; /** - * Retrieves the target. - * @return the target + * Target dimension name (for example profile property or segment). + * + * @return target name */ public String getTarget() { return target; } /** - * Sets the target. - * @param target the target + * Sets the target dimension name. + * + * @param target target name */ public void setTarget(String target) { this.target = target; } /** - * Retrieves the values. - * @return the values + * Allowed values for this target dimension. + * + * @return target values */ public List getValues() { return values; } /** - * Sets the values. - * @param values the values + * Sets allowed values for this target dimension. + * + * @param values target values */ public void setValues(List values) { this.values = values; diff --git a/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java b/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java index fb3cf98a6..98fbeb074 100644 --- a/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java +++ b/api/src/main/java/org/apache/unomi/api/services/PrivacyService.java @@ -29,132 +29,128 @@ public interface PrivacyService { /** - * Retrieves the default base Apache Unomi server information, including the name and version of the server, build - * time information and the event types - * if recognizes as well as the capabilities supported by the system. For more detailed information about the system - * and extensions use the getServerInfos method. - * @return a ServerInfo object with all the server information + * Returns base server information (name, version, build time, event types, capabilities). + * For extension details, use {@link #getServerInfos()}. + * + * @return default server information */ ServerInfo getServerInfo(); /** - * Retrieves the list of the server information objects, that include extensions. Each object includes the - * name and version of the server, build time information and the event types - * if recognizes as well as the capabilities supported by the system. - * @return a list of ServerInfo objects with all the server information + * Returns server information for the core instance and all extensions. + * + * @return server information entries, including extensions */ List getServerInfos(); /** - * Deletes the current profile (but has no effect on sessions and events). This will delete the - * persisted profile and replace it with a new empty one with the same profileId. - * @param profileId the identifier of the profile to delete and replace - * @return true if the deletion was successful + * Replaces a profile with a new empty profile that keeps the same id. + * Sessions and events are not removed. + * + * @param profileId profile identifier + * @return {@code true} if deletion succeeded */ Boolean deleteProfile(String profileId); /** - * This method will "anonymize" a profile by removing from the associated profile all the properties - * that have been defined as "denied properties". - * @param profileId the identifier of the profile that needs to be anonymized. - * @param scope The scope will be used to send events, once for the anonymizeProfile event, the other for the profileUpdated event - * @return true if the profile had some properties purged, false otherwise + * Removes denied (personally identifying) properties from a profile. + * + * @param profileId profile identifier + * @param scope scope used when emitting anonymize and profile-updated events + * @return {@code true} if any properties were removed */ Boolean anonymizeProfile(String profileId, String scope); /** - * This method will anonymize browsing data by creating an anonymous profile for the current profile, - * and then re-associating all the profile's sessions and events with the new anonymous profile - * todo this method does not anonymize any session or event properties that may contain profile - * data (such as the login event) - * @param profileId the identifier of the profile on which to perform the anonymizations of the browsing - * data - * @return true if the operation was successful, false otherwise + * Moves a profile's sessions and events to a new anonymous profile. + * Does not anonymize session or event properties that may contain PII. + * + * @param profileId profile identifier + * @return {@code true} if the operation succeeded */ Boolean anonymizeBrowsingData(String profileId); /** - * This method will perform two operations, first it will call the anonymizeBrowsingData method on the - * specified profile, and then it will delete the profile from the persistence service. - * @param profileId the identifier of the profile - * @param purgeData flag that indicates whether to purge the profile's data - * @return true if the operation was successful, false otherwise + * Anonymizes browsing data, then optionally deletes the original profile. + * + * @param profileId profile identifier + * @param purgeData when {@code true}, deletes the profile after anonymization + * @return {@code true} if the operation succeeded */ Boolean deleteProfileData(String profileId,boolean purgeData); /** - * Controls the activation/deactivation of anonymous browsing. This method will simply set a system - * property called requireAnonymousProfile that will be then use to know if we should associate - * browsing data with the main profile or the associated anonymous profile. - * Note that changing this setting will also reset the goals and pastEvents system properties for the - * profile. - * @param profileId the identifier of the profile on which to set the anonymous browsing property flag - * @param anonymous the value of the anonymous browsing flag. - * @param scope a scope used to send a profileUpdated event internally - * @return true if successful, false otherwise + * Enables or disables anonymous browsing for a profile. + * Resets goals and past-events system properties when the flag changes. + * + * @param profileId profile identifier + * @param anonymous anonymous-browsing flag value + * @param scope scope used when emitting a profile-updated event + * @return {@code true} if the update succeeded */ Boolean setRequireAnonymousBrowsing(String profileId, boolean anonymous, String scope); /** - * Tests if the anonymous browsing flag is set of the specified profile. - * @param profileId the identifier of the profile on which we want to retrieve the anonymous browsing flag - * @return true if successful, false otherwise + * Checks whether anonymous browsing is required for the given profile id. + * + * @param profileId profile identifier + * @return {@code true} if anonymous browsing is required */ Boolean isRequireAnonymousBrowsing(String profileId); /** - * Tests if the anonymous browsing flag is set of the specified profile. - * @param profile the profile on which we want to retrieve the anonymous browsing flag - * @return true if successful, false otherwise + * Checks whether anonymous browsing is required for the given profile. + * + * @param profile profile to inspect + * @return {@code true} if anonymous browsing is required */ Boolean isRequireAnonymousBrowsing(Profile profile); /** - * Build a new anonymous profile (but doesn't persist it in the persistence service). This will also - * copy the profile properties from the passed profile that are not listed as denied properties. - * @param profile the profile for which to create the anonymous profile - * @return a newly created (but not persisted) profile for the passed profile. + * Builds an anonymous profile copy without persisting it. + * Copies non-denied properties from the source profile. + * + * @param profile source profile + * @return new anonymous profile (not persisted) */ Profile getAnonymousProfile(Profile profile); /** - * Retrieve the list of events that the profile has deactivated. For each profile a visitor may indicate - * that he doesn't want some events to be collected. This method retrieves this list from the specified - * profile - * @param profileId the identifier for the profile for which we want to retrieve the list of forbidden - * event types - * @return a list of event types + * Returns event types the visitor has opted out of collecting. + * + * @param profileId profile identifier + * @return blocked event type names */ List getFilteredEventTypes(String profileId); /** - * Retrieve the list of events that the profile has deactivated. For each profile a visitor may indicate - * that he doesn't want some events to be collected. This method retrieves this list from the specified - * profile - * @param profile the profile for which we want to retrieve the list of forbidden - * event types - * @return a list of event types + * Returns event types the visitor has opted out of collecting. + * + * @param profile profile to inspect + * @return blocked event type names */ List getFilteredEventTypes(Profile profile); /** - * Set the list of filtered event types for a profile. This is the list of event types that the visitor - * has specified he does not want the server to collect. - * @param profileId the identifier of the profile on which to filter the events - * @param eventTypes a list of event types that will be filter for the profile - * @return true if successfull, false otherwise. + * Sets event types the visitor has opted out of collecting. + * + * @param profileId profile identifier + * @param eventTypes blocked event type names + * @return {@code true} if the update succeeded */ Boolean setFilteredEventTypes(String profileId, List eventTypes); /** - * Gets the list of denied properties. These are properties marked with a personal identifier tag. - * @param profileId the identified of the profile - * @return a list of profile properties identifiers that are marked as personally identifying + * Returns property names tagged as personally identifying for the profile. + * + * @param profileId profile identifier + * @return denied property identifiers */ List getDeniedProperties(String profileId); /** * Sets the list of denied properties. + * * @param profileId the profile for which to see the denied properties * @param propertyNames the property names to be denied * @return null all the time, this method is not used and is marked as deprecated @@ -166,6 +162,7 @@ public interface PrivacyService { /** * This method doesn't do anything anymore please don't use it + * * @param profileId the identifier of the profile * @return do not use * @deprecated As of version 1.3.0-incubating, do not use this method @@ -175,6 +172,7 @@ public interface PrivacyService { /** * This method doesn't do anything anymore please don't use it + * * @param profileId the identifier of the profile * @param propertyNames do not use * @return do not use @@ -184,10 +182,11 @@ public interface PrivacyService { Boolean setDeniedPropertyDistribution(String profileId, List propertyNames); /** - * Removes a property from the specified profile. This change is persisted. - * @param profileId the identifier of the profile - * @param propertyName the name of the property to remove - * @return true if sucessfull, false otherwise + * Removes a property from a profile and persists the change. + * + * @param profileId profile identifier + * @param propertyName property to remove + * @return {@code true} if the removal succeeded */ Boolean removeProperty(String profileId, String propertyName); 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 b2493530f..5ca14f39e 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 @@ -38,374 +38,387 @@ public interface ProfileService { String PERSONAL_IDENTIFIER_TAG_NAME = "personalIdentifierProperties"; /** - * Retrieves the number of unique profiles. - * @return the number of unique profiles. + * Returns the total number of profiles. + * + * @return profile count */ long getAllProfilesCount(); /** - * Retrieves profiles or personas matching the specified query. - * @param the specific sub-type of {@link Profile} to retrieve - * @param query a {@link Query} specifying which elements to retrieve - * @param clazz the class of elements to retrieve - * @return a {@link PartialList} of {@code T} instances matching the specified query + * Searches profiles or personas using a structured query. + * + * @param profile subtype to return + * @param query search query + * @param clazz profile class to load + * @return matching profiles or personas */ PartialList search(Query query, Class clazz); /** - * Retrieves sessions matching the specified query. - * @param query a {@link Query} specifying which elements to retrieve - * @return a {@link PartialList} of sessions matching the specified query + * Searches sessions using a structured query. + * + * @param query search query + * @return matching sessions */ PartialList searchSessions(Query query); /** - * Creates a String containing comma-separated values (CSV) formatted version of profiles matching the specified query. - * @param query the query specifying which profiles to export - * @return a CSV-formatted String version of the profiles matching the specified query + * Exports matching profile properties as a CSV string. + * + * @param query search query selecting profiles to export + * @return CSV representation of matching profile properties */ String exportProfilesPropertiesToCsv(Query query); /** - * Find profiles which have the specified property with the specified value, ordered according to the specified {@code sortBy} String and paged: only - * {@code size} of them are retrieved, starting with the {@code offset}-th one. - * TODO: replace with version using a query instead of separate parameters - * TODO: remove as it's unused? - * @param propertyName the name of the property we're interested in - * @param propertyValue the value of the property we want profiles to have - * @param offset zero or a positive integer specifying the position of the first profile in the total ordered collection of matching profiles - * @param size a positive integer specifying how many matching profiles should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in - * the String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally - * followed by a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of matching profiles + * Finds profiles with a given property value, ordered and paged. + * + * Prefer { #search(Query)} for new code; this overload remains for backward compatibility. * + * @param propertyName property name to match + * @param propertyValue required property value + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return matching profiles */ PartialList findProfilesByPropertyValue(String propertyName, String propertyValue, int offset, int size, String sortBy); /** - * Merges the specified profiles into the provided so-called master profile, merging properties according to the {@link PropertyMergeStrategyType} specified on their {@link - * PropertyType}. - * @param masterProfile the profile into which the specified profiles will be merged - * @param profilesToMerge the list of profiles to merge into the specified master profile - * @return the merged profile + * Merges profiles into a master profile using each property's merge strategy. + * + * @param masterProfile profile that receives merged data + * @param profilesToMerge profiles to merge into the master + * @return merged master profile */ Profile mergeProfiles(Profile masterProfile, List profilesToMerge); /** - * Retrieves the profile identified by the specified identifier. - * @param profileId the identifier of the profile to retrieve - * @return the profile identified by the specified identifier or {@code null} if no such profile exists + * Loads a profile by id. + * + * @param profileId profile identifier + * @return matching profile, or {@code null} if none exists */ Profile load(String profileId); /** - * Saves the specified profile in the context server. - * @param profile the profile to be saved - * @return the newly saved profile + * Persists a profile. + * + * @param profile profile to save + * @return saved profile */ Profile save(Profile profile); /** - * Adds the alias to the profile. - * @param profileID the identifier of the profile - * @param alias the alias which should be linked with of the profile - * @param clientID the identifier of the client + * Links an alias to a profile for the given client. + * + * @param profileID profile identifier + * @param alias alias to link + * @param clientID client identifier */ void addAliasToProfile(String profileID, String alias, String clientID); /** - * Removes the alias from the profile. - * @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 + * Unlinks an alias from a profile for the given client. + * + * @param profileID profile identifier + * @param alias alias to unlink + * @param clientID client identifier + * @return removed alias, or {@code null} if not found */ ProfileAlias removeAliasFromProfile(String profileID, String alias, String clientID); /** - * Find profile aliases which have the specified property with the specified value, ordered according to the specified {@code sortBy} String and paged: only - * {@code size} of them are retrieved, starting with the {@code offset}-th one. - * @param profileId the identifier of the profile - * @param offset zero or a positive integer specifying the position of the first profile in the total ordered collection of matching profiles - * @param size a positive integer specifying how many matching profiles should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in - * the String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally - * followed by a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of matching profiles + * Lists aliases linked to a profile, ordered and paged. + * + * @param profileId profile identifier + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return matching profile aliases */ PartialList findProfileAliases(String profileId, int offset, int size, String sortBy); /** - * Merge the specified profile properties in an existing profile,or save new profile if it does not exist yet - * @param profile the profile to be saved - * @return the newly saved or merged profile or null if the save or merge operation failed. + * Saves a new profile or merges properties into an existing one. + * + * @param profile profile to save or merge + * @return saved or merged profile, or {@code null} on failure */ Profile saveOrMerge(Profile profile); /** - * Removes the profile (or persona if the {@code persona} parameter is set to {@code true}) identified by the specified identifier. - * @param profileId the identifier of the profile or persona to delete - * @param persona {@code true} if the specified identifier is supposed to refer to a persona, {@code false} if it is supposed to refer to a profile + * Deletes a profile or persona by id. + * + * @param profileId profile or persona identifier + * @param persona {@code true} when deleting a persona, {@code false} for a profile */ void delete(String profileId, boolean persona); /** - * Retrieves the sessions associated with the profile identified by the specified identifier that match the specified query (if specified), ordered according to the specified - * {@code sortBy} String and and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. - * TODO: use a Query object instead of distinct parameter - * @param profileId the identifier of the profile we want to retrieve sessions from - * @param query a String of text used for fulltext filtering which sessions we are interested in or {@code null} (or an empty String) if we want to retrieve all sessions - * @param offset zero or a positive integer specifying the position of the first session in the total ordered collection of matching sessions - * @param size a positive integer specifying how many matching sessions should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of matching sessions + * Lists a profile's sessions with optional full-text filtering, ordered and paged. + * + * @param profileId profile identifier + * @param query optional full-text filter, or {@code null} for all sessions + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return matching sessions */ PartialList getProfileSessions(String profileId, String query, int offset, int size, String sortBy); /** - * Retrieves the session identified by the specified identifier. - * @deprecated {@code dateHint} is not supported anymore, please use {@link #loadSession(String)} - * @param sessionId the identifier of the session to be retrieved - * @param dateHint a Date helping in identifying where the item is located - * @return the session identified by the specified identifier + * Loads a session by id. + * + * @deprecated {@code dateHint} is not supported anymore; use {@link #loadSession(String)} + * @param sessionId session identifier + * @param dateHint unused date hint + * @return matching session */ @Deprecated Session loadSession(String sessionId, Date dateHint); /** - * Retrieves the session identified by the specified identifier. - * @param sessionId the identifier of the session to be retrieved - * @return the session identified by the specified identifier + * Loads a session by id. + * + * @param sessionId session identifier + * @return matching session */ default Session loadSession(String sessionId) { return loadSession(sessionId, null); }; /** - * Saves the specified session. - * @param session the session to be saved - * @return the newly saved session + * Persists a session. + * + * @param session session to save + * @return saved session */ Session saveSession(Session session); /** - * Retrieves sessions associated with the profile identified by the specified identifier. - * @param profileId the profile id for which we want to retrieve the sessions - * @return a {@link PartialList} of the profile's sessions + * Returns all sessions linked to a profile. + * + * @param profileId profile identifier + * @return profile sessions */ PartialList findProfileSessions(String profileId); /** - * Removes all sessions of the specified profile - * @param profileId identifier of the profile that we want to remove it's sessions + * Deletes all sessions belonging to a profile. + * + * @param profileId profile identifier */ void removeProfileSessions(String profileId); /** - * Deletes the session identified by the given identifier from persistence. - * Note: events belonging to this session are NOT removed; they remain in persistence - * with a dangling sessionId reference. - * @param sessionIdentifier the unique identifier for the session + * Deletes a session by id. + * Events for the session remain in persistence with a dangling session id. + * + * @param sessionIdentifier session identifier */ void deleteSession(String sessionIdentifier); /** - * Checks whether the specified profile and/or session satisfy the specified condition. - * @param condition the condition we're testing against which might or might not have profile- or session-specific sub-conditions - * @param profile the profile we're testing - * @param session the session we're testing - * @return {@code true} if the profile and/or sessions match the specified condition, {@code false} otherwise + * Evaluates whether a profile and/or session satisfy a condition. + * + * @param condition condition to test (may include profile- or session-specific parts) + * @param profile profile to evaluate + * @param session session to evaluate + * @return {@code true} when the condition matches */ boolean matchCondition(Condition condition, Profile profile, Session session); /** - * Update all profiles in batch according to the specified {@link BatchUpdate} - * @param update the batch update specification + * Applies a batch update to matching profiles. + * + * @param update batch update specification */ void batchProfilesUpdate(BatchUpdate update); /** - * Retrieves the persona identified by the specified identifier. - * @param personaId the identifier of the persona to retrieve - * @return the persona associated with the specified identifier or {@code null} if no such persona exists. + * Loads a persona by id. + * + * @param personaId persona identifier + * @return matching persona, or {@code null} if none exists */ Persona loadPersona(String personaId); /** - * Persists the specified {@link Persona} in the context server. - * @param persona the persona to persist - * @return the newly persisted persona + * Persists a persona. + * + * @param persona persona to save + * @return saved persona */ Persona savePersona(Persona persona); /** - * Retrieves the persona identified by the specified identifier and all its associated sessions - * @param personaId the identifier of the persona to retrieve - * @return a {@link PersonaWithSessions} instance with the persona identified by the specified identifier and all its associated sessions + * Loads a persona and all of its sessions. + * + * @param personaId persona identifier + * @return persona with associated sessions */ PersonaWithSessions loadPersonaWithSessions(String personaId); /** - * Creates a persona with the specified identifier and automatically creates an associated session with it. - * @param personaId the identifier to use for the new persona - * @return the newly created persona + * Creates a persona and an initial session for it. + * + * @param personaId identifier for the new persona + * @return newly created persona */ Persona createPersona(String personaId); /** - * Retrieves the sessions associated with the persona identified by the specified identifier, ordered according to the specified {@code sortBy} String and and paged: only - * {@code size} of them are retrieved, starting with the {@code offset}-th one. - * @param personaId the persona id - * @param offset zero or a positive integer specifying the position of the first session in the total ordered collection of matching sessions - * @param size a positive integer specifying how many matching sessions should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of sessions for the persona identified by the specified identifier + * Lists persona sessions, ordered and paged. + * + * @param personaId persona identifier + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return matching persona sessions */ PartialList getPersonaSessions(String personaId, int offset, int size, String sortBy); /** - * Save a persona with its sessions. - * @param personaToSave the persona object containing all the persona information and sessions - * @return the persona with sessions + * Persists a persona together with its sessions. + * + * @param personaToSave persona and session data to save + * @return saved persona with sessions */ PersonaWithSessions savePersonaWithSessions(PersonaWithSessions personaToSave); - /** - * Retrieves all the property types associated with the specified target. - * TODO: move to a different class - * @param target the target for which we want to retrieve the associated property types - * @return a collection of all the property types associated with the specified target + * Returns property types registered for the given target. + * + * @param target target name (for example profiles or events) + * @return property types for the target */ Collection getTargetPropertyTypes(String target); /** - * Retrieves all known property types. - * TODO: move to a different class - * TODO: use Map instead of HashMap - * @return a Map associating targets as keys to related {@link PropertyType}s + * Returns all property types grouped by target. + * + * @return map of target name to property types */ Map> getTargetPropertyTypes(); /** - * Retrieves all property types with the specified tag - * TODO: move to a different class - * @param tag the tag name marking property types we want to retrieve - * @return a Set of the property types with the specified tag + * Returns property types tagged with the given tag. + * + * @param tag tag name + * @return matching property types */ Set getPropertyTypeByTag(String tag); /** - * Retrieves all property types with the specified system tag - * TODO: move to a different class - * @param tag the system tag name marking property types we want to retrieve - * @return a Set of the property types with the specified system tag + * Returns property types with the given system tag. + * + * @param tag system tag name + * @return matching property types */ Set getPropertyTypeBySystemTag(String tag); /** - * TODO - * @param fromPropertyTypeId fromPropertyTypeId - * @return property type mapping + * Returns the persistence mapping for a property type. + * + * @param fromPropertyTypeId source property type id + * @return mapped property type id */ String getPropertyTypeMapping(String fromPropertyTypeId); /** - * TODO - * @param propertyName the property name - * @return list of property types + * Returns property types mapped to the given property name. + * + * @param propertyName property name to look up + * @return matching property types */ Collection getPropertyTypeByMapping(String propertyName); /** - * Retrieves the property type identified by the specified identifier. - * TODO: move to a different class - * @param id the identifier of the property type to retrieve - * @return the property type identified by the specified identifier or {@code null} if no such property type exists + * Looks up a property type by id. + * + * @param id property type identifier + * @return matching property type, or {@code null} if none exists */ PropertyType getPropertyType(String id); /** - * Persists the specified property type in the context server. - * TODO: move to a different class - * @param property the property type to persist - * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed + * Registers or updates a property type definition. + * + * @param property property type to persist + * @return {@code true} when created, {@code false} when it already existed */ boolean setPropertyType(PropertyType property); /** - * This function will try to set the target on the property type if not set already, based on the file URL - * @param predefinedPropertyTypeURL the URL to extract the target from if the target is not yet. By default it will - * use the 5's part after a "/" character - * @param propertyType the property type to register + * Infers and sets the property type target from a definition URL when missing. + * By default uses the fifth path segment after {@code /}. + * + * @param predefinedPropertyTypeURL URL of the property type definition + * @param propertyType property type to update */ void setPropertyTypeTarget(URL predefinedPropertyTypeURL, PropertyType propertyType); /** - * Deletes the property type identified by the specified identifier. - * TODO: move to a different class - * @param propertyId the identifier of the property type to delete - * @return {@code true} if the property type was properly deleted, {@code false} otherwise + * Deletes a property type by id. + * + * @param propertyId property type identifier + * @return {@code true} when deleted successfully */ boolean deletePropertyType(String propertyId); /** - * Retrieves the existing property types for the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and with the specified tag. - * TODO: move to a different class - * @param tag the tag we're interested in - * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @return all property types defined for the specified item type and with the specified tag + * Returns property types defined for an item type that carry the given tag. + * + * @param tag tag to match + * @param itemType item type name from the item class {@code ITEM_TYPE} field + * @return matching property types */ Set getExistingProperties(String tag, String itemType); /** - * Retrieves the existing property types for the specified type as defined by the Item subclass public - * field {@code ITEM_TYPE} and with the specified tag (system or regular) - * TODO: move to a different class - * @param tag the tag we're interested in - * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param systemTag whether the specified is a system tag or a regular one - * @return all property types defined for the specified item type and with the specified tag + * Returns property types defined for an item type that carry the given tag or system tag. + * + * @param tag tag to match + * @param itemType item type name from the item class {@code ITEM_TYPE} field + * @param systemTag {@code true} when {@code tag} is a system tag + * @return matching property types */ Set getExistingProperties(String tag, String itemType, boolean systemTag); /** - * Forces a refresh of the profile service, to load data from persistence immediately instead of waiting for - * scheduled tasks to execute. Warning : this may have serious impacts on performance so it should only be used - * in specific scenarios such as integration tests. + * Reloads profile service state from persistence immediately. + * Expensive; intended for integration tests and similar scenarios. */ void refresh(); /** - * Purge (delete) profiles - * example: Purge profile inactive since 10 days only: - * purgeProfiles(10, 0); - * example: Purge profile created since 30 days only: - * purgeProfiles(0, 30); - * @param inactiveNumberOfDays will purge profiles with no visits since this number of days (0 or negative value, will have no effect) - * @param existsNumberOfDays will purge profiles created since this number of days (0 or negative value, will have no effect) + * Deletes profiles by inactivity and/or age criteria. + * Example: purge profiles inactive for 10 days only: {@code purgeProfiles(10, 0)}. + * Example: purge profiles created within the last 30 days: {@code purgeProfiles(0, 30)}. + * + * @param inactiveNumberOfDays purge profiles with no visits since this many days (ignored when {@code <= 0}) + * @param existsNumberOfDays purge profiles created within this many days (ignored when {@code <= 0}) */ void purgeProfiles(int inactiveNumberOfDays, int existsNumberOfDays); /** - * Purge (delete) session items - * @param existsNumberOfDays will purge sessions created since this number of days (0 or negative value, will have no effect) + * Deletes session items older than the given age threshold. + * + * @param existsNumberOfDays purge sessions created within this many days (ignored when {@code <= 0}) */ void purgeSessionItems(int existsNumberOfDays); /** - * Purge (delete) event items - * @param existsNumberOfDays will purge events created since this number of days (0 or negative value, will have no effect) + * Deletes event items older than the given age threshold. + * + * @param existsNumberOfDays purge events created within this many days (ignored when {@code <= 0}) */ void purgeEventItems(int existsNumberOfDays); /** - * Use purgeSessionItems and purgeEventItems to remove rollover items instead - * @param existsNumberOfMonths used to remove monthly indices older than this number of months + * @deprecated Use {@link #purgeSessionItems(int)} and {@link #purgeEventItems(int)} for rollover cleanup. + * + * @param existsNumberOfMonths remove monthly indices older than this many months */ @Deprecated void purgeMonthlyItems(int existsNumberOfMonths); diff --git a/api/src/main/java/org/apache/unomi/api/services/QueryService.java b/api/src/main/java/org/apache/unomi/api/services/QueryService.java index 657c8ab4e..b4b657825 100644 --- a/api/src/main/java/org/apache/unomi/api/services/QueryService.java +++ b/api/src/main/java/org/apache/unomi/api/services/QueryService.java @@ -30,27 +30,23 @@ public interface QueryService { /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and aggregated by possible values of the specified - * property. + * Counts items of the given type grouped by a property's distinct values. * - * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param property the property we're aggregating on, i.e. for each possible value of this property, we are counting how many items of the specified type have that value - * @return a Map associating a specific value of the property to the cardinality of items with that value + * @param itemType item type name from the item class {@code ITEM_TYPE} field + * @param property property to aggregate on + * @return map of property value to item count * @see Item Item for a discussion of {@code ITEM_TYPE} */ Map getAggregate(String itemType, String property); /** - * TODO: rework, this method is confusing since it either behaves like {@link #getAggregate(String, String)} if query is null but completely differently if it isn't + * Counts items of the given type grouped by property values, optionally filtered by an aggregate query. + * Also returns the global document count when {@code query} is {@code null}. * - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and aggregated by possible values of the specified - * property or, if the specified query is not {@code null}, perform that aggregate query. - * Also return the global count of document matching the {@code ITEM_TYPE} - * - * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param property the property we're aggregating on, i.e. for each possible value of this property, we are counting how many items of the specified type have that value - * @param query the {@link AggregateQuery} specifying the aggregation that should be perfomed - * @return a Map associating a specific value of the property to the cardinality of items with that value + * @param itemType item type name from the item class {@code ITEM_TYPE} field + * @param property property to aggregate on + * @param query optional aggregate query, or {@code null} for simple aggregation + * @return map of property value to item count * @see Item Item for a discussion of {@code ITEM_TYPE} * @deprecated As of 1.3.0-incubating, please use {@link #getAggregateWithOptimizedQuery(String, String, AggregateQuery)} instead */ @@ -58,38 +54,35 @@ public interface QueryService { Map getAggregate(String itemType, String property, AggregateQuery query); /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and aggregated by possible values of the specified - * property or, if the specified query is not {@code null}, perform that aggregate query. - * This aggregate won't return the global count and should therefore be much faster than {@link #getAggregate(String, String, AggregateQuery)} + * Counts items of the given type grouped by property values using an optimized aggregate query. + * Does not return a global document count. * - * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param property the property we're aggregating on, i.e. for each possible value of this property, we are counting how many items of the specified type have that value - * @param query the {@link AggregateQuery} specifying the aggregation that should be perfomed - * @return a Map associating a specific value of the property to the cardinality of items with that value + * @param itemType item type name from the item class {@code ITEM_TYPE} field + * @param property property to aggregate on + * @param query aggregate query defining the aggregation + * @return map of property value to item count * @see Item Item for a discussion of {@code ITEM_TYPE} */ Map getAggregateWithOptimizedQuery(String itemType, String property, AggregateQuery query); /** - * Retrieves the number of items of the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and matching the specified {@link Condition}. + * Counts items of the given type that match a condition. * - * @param condition the condition the items must satisfy - * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @return the number of items of the specified type + * @param condition filter condition + * @param itemType item type name from the item class {@code ITEM_TYPE} field + * @return matching item count * @see Item Item for a discussion of {@code ITEM_TYPE} */ long getQueryCount(String itemType, Condition condition); /** - * Retrieves the specified metrics for the specified field of items of the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and matching the - * specified {@link Condition}. + * Computes numeric metrics (sum, avg, min, max) for a field on items matching a condition. * - * @param condition the condition the items must satisfy - * @param slashConcatenatedMetrics a String specifying which metrics should be computed, separated by a slash ({@code /}) (possible values: {@code sum} for the sum of the - * values, {@code avg} for the average of the values, {@code min} for the minimum value and {@code max} for the maximum value) - * @param property the name of the field for which the metrics should be computed - * @param type the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @return a Map associating computed metric name as key to its associated value + * @param condition filter condition + * @param slashConcatenatedMetrics metrics to compute, separated by {@code /} ({@code sum}, {@code avg}, {@code min}, {@code max}) + * @param property field name to aggregate + * @param type item type name from the item class {@code ITEM_TYPE} field + * @return map of metric name to computed value * @see Item Item for a discussion of {@code ITEM_TYPE} */ Map getMetric(String type, String property, String slashConcatenatedMetrics, Condition condition); diff --git a/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java b/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java index e6555a2ed..0d948a1f2 100644 --- a/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java +++ b/api/src/main/java/org/apache/unomi/api/services/RuleListenerService.java @@ -43,6 +43,7 @@ enum AlreadyRaisedFor { * Called before a rule's conditions are evaluated. Be careful when implemented this listener because rule's condition * are called in very high frequencies and the performance of this listener might have a huge impact on rule's * performance + * * @param rule the rule that is being evaluated * @param event the event we are processing and evaluating against the rule */ @@ -50,6 +51,7 @@ enum AlreadyRaisedFor { /** * Called when a rule has already been raised either for a session or a profile. + * * @param alreadyRaisedFor an enum that indicates if the rule was already raised once for the session or for the * profile * @param rule the rule that has already been raised @@ -59,6 +61,7 @@ enum AlreadyRaisedFor { /** * Called just before a matching rule's actions are about to be executed. + * * @param rule the matching rule for the current event * @param event the event we are processing that matched the current rule. */ diff --git a/api/src/main/java/org/apache/unomi/api/services/RulesService.java b/api/src/main/java/org/apache/unomi/api/services/RulesService.java index b55e09115..7f3ad7260 100644 --- a/api/src/main/java/org/apache/unomi/api/services/RulesService.java +++ b/api/src/main/java/org/apache/unomi/api/services/RulesService.java @@ -38,95 +38,98 @@ public interface RulesService { /** - * Retrieves the metadata for all known rules. - * Note that it only includes the rules in memory, not those persisted in storage. - * @return the Set of known metadata + * Returns metadata for all in-memory rules. + * Does not query persistence directly. + * + * @return rule metadata from the in-memory cache */ Set getRuleMetadatas(); /** - * Retrieves rule metadatas for rules matching the specified {@link Query}. + * Returns metadata for rules matching the given query. * - * @param query the query the rules which metadata we want to retrieve must match - * @return a {@link PartialList} of rules metadata for the rules matching the specified query + * @param query filter for rules whose metadata should be returned + * @return matching rule metadata */ PartialList getRuleMetadatas(Query query); /** - * Retrieves rule details for rules matching the specified query. + * Returns full rule definitions matching the given query. * - * @param query the query specifying which rules to retrieve - * @return a {@link PartialList} of rule details for the rules matching the specified query + * @param query filter for rules to return + * @return matching rules */ PartialList getRuleDetails(Query query); /** - * Get all rules available in the system. - * (This is not doing a persistence query to retrieve the rules, it's using the internal in memory cache - * that is refreshed every second by default but can vary depending on your own configuration) + * Returns all rules from the in-memory cache. + * The cache refreshes on a configurable interval (default one second). * - * @return all rules available. + * @return all cached rules */ List getAllRules(); /** - * Retrieves the rule identified by the specified identifier. + * Loads a rule by id. * - * @param ruleId the identifier of the rule we want to retrieve - * @return the rule identified by the specified identifier or {@code null} if no such rule exists. + * @param ruleId rule identifier + * @return matching rule, or {@code null} if none exists */ Rule getRule(String ruleId); /** - * Retrieves the statistics for a rule - * @param ruleId the identifier of the rule - * @return a long representing the number of times the rule was matched and executed. + * Returns execution statistics for a rule. + * + * @param ruleId rule identifier + * @return rule match and execution counts */ RuleStatistics getRuleStatistics(String ruleId); /** - * Retrieves the statistics for all the rules - * @return a map containing rule IDs as key, and the RuleStatistics object as a value + * Returns execution statistics for all rules. + * + * @return map of rule id to statistics */ Map getAllRuleStatistics(); /** - * Resets all the rule statistics to zero, useful when testing or if you want to set a point in time. + * Resets match and execution counters for every rule. */ void resetAllRuleStatistics(); /** - * Persists the specified rule to the context server. + * Persists a rule definition. * - * @param rule the rule to be persisted + * @param rule rule to save */ void setRule(Rule rule); /** - * Deletes the rule identified by the specified identifier. + * Deletes a rule by id. * - * @param ruleId the identifier of the rule we want to delete + * @param ruleId rule identifier */ void removeRule(String ruleId); /** - * Retrieves tracked conditions (rules with a condition marked with the {@code trackedCondition} tag and which {@code sourceEventCondition} matches the specified item) for the - * specified item. + * Returns tracked conditions whose source-event condition matches the given item. + * Tracked conditions are rules tagged with {@code trackedCondition}. * - * @param item the item which tracked conditions we want to retrieve - * @return the Set of tracked conditions for the specified item + * @param item item to match against source-event conditions + * @return matching tracked conditions */ Set getTrackedConditions(Item item); /** - * Retrieves all the matching rules for a specific event - * @param event the event we want to retrieve all the matching rules for - * @return a set of rules that match the event passed in the parameters + * Returns rules whose conditions match the given event. + * + * @param event event to evaluate + * @return matching rules */ public Set getMatchingRules(Event event); /** - * Refresh the rules for this instance by reloading them from the persistence backend + * Reloads rules from persistence into the in-memory cache. */ public void refreshRules(); 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 fe97d3dfc..0b770b91f 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 @@ -264,6 +264,7 @@ ScheduledTask createTask(String taskType, /** * Gets the value of a specific metric. + * * @param metric The metric name * @return The current value of the metric */ @@ -276,6 +277,7 @@ ScheduledTask createTask(String taskType, /** * Gets all metrics as a map. + * * @return Map of metric names to their current values */ Map getAllMetrics(); @@ -284,6 +286,7 @@ ScheduledTask createTask(String taskType, * Gets a list of scheduled tasks matching the specified status. * This method allows filtering all managed tasks (both persistent and * in-memory) by their current operational state, such as RUNNING or FAILED. + * * @param taskStatus the desired {@link ScheduledTask.TaskStatus} to filter by * @return tasks that currently match the given status */ @@ -297,6 +300,7 @@ ScheduledTask createTask(String taskType, interface TaskBuilder { /** * Sets task parameters. + * * @param parameters task-specific parameters * @return this builder for method chaining */ @@ -304,6 +308,7 @@ interface TaskBuilder { /** * Sets initial execution delay. + * * @param initialDelay delay before first execution * @param timeUnit time unit for delay * @return this builder for method chaining @@ -312,6 +317,7 @@ interface TaskBuilder { /** * Sets execution period. + * * @param period time between executions * @param timeUnit time unit for period * @return this builder for method chaining @@ -321,6 +327,7 @@ interface TaskBuilder { /** * Uses fixed delay scheduling. * Period is measured from completion of one execution to start of next. + * * @return this builder for method chaining */ TaskBuilder withFixedDelay(); @@ -328,6 +335,7 @@ interface TaskBuilder { /** * Uses fixed rate scheduling. * Period is measured from start of one execution to start of next. + * * @return this builder for method chaining */ TaskBuilder withFixedRate(); @@ -335,6 +343,7 @@ interface TaskBuilder { /** * Makes this a one-shot task. * Task will execute once and then be disabled. + * * @return this builder for method chaining */ TaskBuilder asOneShot(); @@ -342,12 +351,14 @@ interface TaskBuilder { /** * Disallows parallel execution. * Task will use locking to ensure only one instance runs at a time. + * * @return this builder for method chaining */ TaskBuilder disallowParallelExecution(); /** * Sets the task executor. + * * @param executor the executor to handle this task * @return this builder for method chaining */ @@ -355,6 +366,7 @@ interface TaskBuilder { /** * Sets a simple runnable as the executor. + * * @param runnable the code to execute * @return this builder for method chaining */ @@ -363,6 +375,7 @@ interface TaskBuilder { /** * Makes this a non-persistent task. * Task will only exist in memory on this node. + * * @return this builder for method chaining */ TaskBuilder nonPersistent(); @@ -370,6 +383,7 @@ interface TaskBuilder { /** * Runs the task on all nodes in the cluster rather than just executor nodes. * This is helpful for distributed cache refreshes or local data maintenance. + * * @return this builder for method chaining */ TaskBuilder runOnAllNodes(); @@ -423,6 +437,7 @@ interface TaskBuilder { /** * Sets the task dependencies. * The task will not execute until all dependencies have completed. + * * @param taskIds IDs of tasks this task depends on * @return this builder for method chaining */ @@ -430,6 +445,7 @@ interface TaskBuilder { /** * Creates and schedules the task with current configuration. + * * @return the created and scheduled task */ ScheduledTask schedule(); diff --git a/api/src/main/java/org/apache/unomi/api/services/SegmentService.java b/api/src/main/java/org/apache/unomi/api/services/SegmentService.java index 935dfa1ce..26eeb11a4 100644 --- a/api/src/main/java/org/apache/unomi/api/services/SegmentService.java +++ b/api/src/main/java/org/apache/unomi/api/services/SegmentService.java @@ -38,46 +38,39 @@ public interface SegmentService { /** - * Retrieves segment metadatas, ordered according to the specified {@code sortBy} String and and paged: only {@code size} of them are retrieved, starting with the {@code - * offset}-th one. + * Returns segment metadata, ordered and paged. * - * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements - * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of segment metadata + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return segment metadata */ PartialList getSegmentMetadatas(int offset, int size, String sortBy); /** - * Retrieves segment metadatas for segments in the specified scope, ordered according to the specified {@code sortBy} String and and paged: only {@code size} of them are - * retrieved, starting with the {@code offset}-th one. - * TODO: remove? + * Returns segment metadata for a scope, ordered and paged. * - * @param scope the scope for which we want to retrieve segment metadata - * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements - * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of segment metadata + * @param scope scope filter + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return segment metadata for the scope */ PartialList getSegmentMetadatas(String scope, int offset, int size, String sortBy); /** - * Retrieves the metadata for segments matching the specified {@link Query}. + * Returns segment metadata matching the given query. * - * @param query the query that the segments must match for their metadata to be retrieved - * @return a {@link PartialList} of segment metadata + * @param query filter for segments whose metadata should be returned + * @return matching segment metadata */ PartialList getSegmentMetadatas(Query query); /** - * Retrieves the segment identified by the specified identifier. + * Loads a segment definition by id. * - * @param segmentId the identifier of the segment to be retrieved - * @return the segment identified by the specified identifier or {@code null} if no such segment exists + * @param segmentId segment identifier + * @return matching segment, or {@code null} if none exists */ Segment getSegmentDefinition(String segmentId); @@ -101,33 +94,30 @@ public interface SegmentService { DependentMetadata removeSegmentDefinition(String segmentId, boolean validate); /** - * Retrieves the list of Segment and Scoring metadata depending on the specified segment. - * A segment or scoring is depending on a segment if it includes a profileSegmentCondition with a test on this segment. + * Returns segment and scoring metadata that depend on the given segment. + * A dependent definition references the segment in a profile-segment condition. * - * @param segmentId the segment identifier - * @return a list of Segment/Scoring Metadata depending on the specified segment + * @param segmentId segment identifier + * @return dependent segment and scoring metadata */ DependentMetadata getSegmentDependentMetadata(String segmentId); /** - * Retrieves a list of profiles matching the conditions defined by the segment identified by the specified identifier, ordered according to the specified {@code sortBy} - * String and and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. + * Lists profiles that match a segment's conditions, ordered and paged. * - * @param segmentID the identifier of the segment for which we want to retrieve matching profiles - * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements - * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of profiles matching the specified segment + * @param segmentID segment identifier + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return matching profiles */ PartialList getMatchingIndividuals(String segmentID, int offset, int size, String sortBy); /** - * Retrieves the number of profiles matching the conditions defined by the segment identified by the specified identifier. + * Counts profiles that match a segment's conditions. * - * @param segmentID the identifier of the segment for which we want to retrieve matching profiles - * @return the number of profiles matching the conditions defined by the segment identified by the specified identifier + * @param segmentID segment identifier + * @return matching profile count */ long getMatchingIndividualsCount(String segmentID); @@ -141,44 +131,44 @@ public interface SegmentService { Boolean isProfileInSegment(Profile profile, String segmentId); /** - * Retrieves the segments and scores for the specified profile. + * Returns current segment memberships and scores for a profile. * - * @param profile the profile for which we want to retrieve segments and scores - * @return a {@link SegmentsAndScores} instance encapsulating the segments and scores for the specified profile + * @param profile profile to evaluate + * @return segments and scores for the profile */ SegmentsAndScores getSegmentsAndScoresForProfile(Profile profile); /** - * Retrieves the list of segment metadata for the segments the specified profile is a member of. + * Returns metadata for segments the profile belongs to. * - * @param profile the profile for which we want to retrieve the segment metadata - * @return the (possibly empty) list of segment metadata for the segments the specified profile is a member of + * @param profile profile to inspect + * @return segment metadata for the profile's memberships */ List getSegmentMetadatasForProfile(Profile profile); /** - * Retrieves the set of all scoring metadata. + * Returns scoring metadata, ordered and paged. * - * @param offset the offset - * @param size the size - * @param sortBy sort by - * @return the set of all scoring metadata + * @param offset zero-based index of the first result + * @param size maximum results to return, or {@code -1} for all + * @param sortBy optional comma-separated property list with optional {@code :asc}/{@code :desc} suffixes + * @return scoring metadata */ PartialList getScoringMetadatas(int offset, int size, String sortBy); /** - * Retrieves the set of scoring metadata for scorings matching the specified query. + * Returns scoring metadata matching the given query. * - * @param query the query the scorings must match for their metadata to be retrieved - * @return the set of scoring metadata for scorings matching the specified query + * @param query filter for scorings whose metadata should be returned + * @return matching scoring metadata */ PartialList getScoringMetadatas(Query query); /** - * Retrieves the scoring identified by the specified identifier. + * Loads a scoring definition by id. * - * @param scoringId the identifier of the scoring to be retrieved - * @return the scoring identified by the specified identifier or {@code null} if no such scoring exists + * @param scoringId scoring identifier + * @return matching scoring, or {@code null} if none exists */ Scoring getScoringDefinition(String scoringId); @@ -213,19 +203,20 @@ public interface SegmentService { DependentMetadata removeScoringDefinition(String scoringId, boolean validate); /** - * Retrieves the list of Segment and Scoring metadata depending on the specified scoring. - * A segment or scoring is depending on a segment if it includes a scoringCondition with a test on this scoring. + * Returns segment and scoring metadata that depend on the given scoring. + * A dependent definition references the scoring in a scoring condition. * - * @param scoringId the segment identifier - * @return a list of Segment/Scoring Metadata depending on the specified scoring + * @param scoringId scoring identifier + * @return dependent segment and scoring metadata */ DependentMetadata getScoringDependentMetadata(String scoringId); /** - * Get generated property key for past event condition - * @param condition The event condition - * @param parentCondition The past event condition - * @return a String representing the condition and parent condition uniquelly + * Builds a stable property key for a past-event condition pair. + * + * @param condition nested event condition + * @param parentCondition parent past-event condition + * @return generated property key */ String getGeneratedPropertyKey(Condition condition, Condition parentCondition); diff --git a/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java b/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java index d5653d9ab..1d1b9ccf7 100644 --- a/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java +++ b/api/src/main/java/org/apache/unomi/api/services/TenantLifecycleListener.java @@ -23,7 +23,8 @@ public interface TenantLifecycleListener { /** * Called when a tenant is removed from the system. + * * @param tenantId the ID of the tenant that was removed */ void onTenantRemoved(String tenantId); -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/services/TopicService.java b/api/src/main/java/org/apache/unomi/api/services/TopicService.java index f8344ae39..6a843c5db 100644 --- a/api/src/main/java/org/apache/unomi/api/services/TopicService.java +++ b/api/src/main/java/org/apache/unomi/api/services/TopicService.java @@ -28,30 +28,34 @@ public interface TopicService { /** - * Retrieves the topic identified by the specified identifier. - * @param topicId the identifier of the topic to retrieve - * @return the topic identified by the specified identifier or {@code null} if no such topic exists + * Loads a topic by id. + * + * @param topicId topic identifier + * @return matching topic, or {@code null} if none exists */ Topic load(final String topicId); /** - * Saves the specified topic in the context server. - * @param topic the topic to be saved - * @return the newly saved topic if the creation or update was successful, {@code null} otherwise + * Persists a topic. + * + * @param topic topic to save + * @return saved topic, or {@code null} if the operation failed */ Topic save(final Topic topic); /** - * Retrieves topic matching the specified query. - * @param query a {@link Query} specifying which elements to retrieve - * @return a {@link PartialList} of {@link Topic} metadata + * Searches topics using a structured query. + * + * @param query search query + * @return matching topics */ PartialList search(final Query query); /** - * Removes the topic identified by the specified identifier. - * @param topicId the identifier of the profile or persona to delete - * @return {@code true} if the deletion was successful, {@code false} otherwise + * Deletes a topic by id. + * + * @param topicId topic identifier + * @return {@code true} if deletion succeeded */ boolean delete(final String topicId); diff --git a/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java b/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java index 37c92930a..b330286b5 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java +++ b/api/src/main/java/org/apache/unomi/api/services/ValueTypeValidator.java @@ -29,6 +29,7 @@ public interface ValueTypeValidator { /** * Validates if a value matches the expected type + * * @param value The value to validate * @return true if the value is valid for this type, false otherwise */ 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 e8afce678..c5d698e77 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 @@ -105,7 +105,7 @@ public enum TaskStatus { private Set waitingOnTasks = new HashSet<>(); /** - * Instantiates a new scheduled task with default status {@link TaskStatus#SCHEDULED}, + * Creates a scheduled task with default status {@link TaskStatus#SCHEDULED}, * {@code maxRetries} of 3, and a default {@code retryDelay} of 60 seconds. */ public ScheduledTask() { @@ -117,7 +117,7 @@ public ScheduledTask() { } /** - * Retrieves the task type identifier. + * Task type identifier that selects the executor. * The task type determines which executor will handle this task. * * @return the task type identifier @@ -136,7 +136,7 @@ public void setTaskType(String taskType) { } /** - * Retrieves the task parameters. + * Parameters passed to the task executor. * These parameters are passed to the task executor during execution. * * @return map of task parameters @@ -155,7 +155,7 @@ public void setParameters(Map parameters) { } /** - * Retrieves the initial delay before the first execution, expressed in {@link #getTimeUnit()}. + * Initial delay before the first execution, expressed in {@link #getTimeUnit()}. * * @return the initial delay in the configured time unit */ @@ -173,7 +173,7 @@ public void setInitialDelay(long initialDelay) { } /** - * Retrieves the period between successive task executions. + * 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 @@ -204,7 +204,7 @@ public void setPeriod(long period) { } /** - * Retrieves the time unit for delay and period values. + * Time unit for delay and period values. * * @return the time unit used for scheduling */ @@ -242,7 +242,7 @@ public void setFixedRate(boolean fixedRate) { } /** - * Retrieves the date of the last execution attempt. + * Date of the last execution attempt. * * @return the last execution date or {@code null} if never executed */ @@ -260,7 +260,7 @@ public void setLastExecutionDate(Date lastExecutionDate) { } /** - * Retrieves the node ID that last executed this task. + * Node identifier that last executed this task. * * @return the ID of the last executing node */ @@ -278,7 +278,7 @@ public void setLastExecutedBy(String lastExecutedBy) { } /** - * Retrieves the error message from the last failed execution. + * Error message from the last failed execution. * * @return the last error message or {@code null} if no error */ @@ -315,7 +315,7 @@ public void setEnabled(boolean enabled) { } /** - * Retrieves the ID of the node that currently holds the execution lock. + * Node identifier that currently holds the execution lock. * * @return the current lock owner's node ID or {@code null} if unlocked */ @@ -333,7 +333,7 @@ public void setLockOwner(String lockOwner) { } /** - * Retrieves the date when the current lock was acquired. + * Date when the current lock was acquired. * * @return the lock acquisition date or {@code null} if unlocked */ @@ -395,7 +395,7 @@ public void setAllowParallelExecution(boolean allowParallelExecution) { } /** - * Retrieves the current task status. + * Current task lifecycle status. * * @return the current status */ @@ -433,7 +433,7 @@ public void setStatusDetails(Map statusDetails) { } /** - * Retrieves the next scheduled execution date for periodic tasks. + * Next scheduled execution date for periodic tasks. * * @return the next scheduled execution date or {@code null} if not scheduled */ @@ -451,7 +451,7 @@ public void setNextScheduledExecution(Date nextScheduledExecution) { } /** - * Retrieves the number of consecutive execution failures. + * Number of consecutive execution failures. * * @return the failure count */ @@ -469,7 +469,7 @@ public void setFailureCount(int failureCount) { } /** - * Retrieves the number of successful executions. + * Number of successful executions. * * @return the success count */ @@ -487,7 +487,7 @@ public void setSuccessCount(int successCount) { } /** - * Retrieves the maximum number of retry attempts after failures. + * 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 @@ -516,7 +516,7 @@ public void setMaxRetries(int maxRetries) { } /** - * Retrieves the delay between retry attempts. + * 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 @@ -541,7 +541,7 @@ public void setRetryDelay(long retryDelay) { } /** - * Retrieves the name of the current execution step. + * Name of the current execution step. * This is used to track progress through multi-step tasks. * * @return the current step name or {@code null} if not set @@ -560,7 +560,7 @@ public void setCurrentStep(String currentStep) { } /** - * Retrieves the checkpoint data for task resumption. + * 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 {@code null} if no checkpoint @@ -642,7 +642,7 @@ public void setSystemTask(boolean systemTask) { } /** - * Retrieves the task type that this task is waiting for a lock on. + * Task type 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 {@code null} if not waiting @@ -661,7 +661,7 @@ public void setWaitingForTaskType(String waitingForTaskType) { } /** - * Retrieves the set of task IDs that this task depends on. + * Task identifiers this task depends on. * The task will not execute until all dependencies have completed. * * @return set of dependency task IDs @@ -680,7 +680,7 @@ public void setDependsOn(Set dependsOn) { } /** - * Retrieves the set of task IDs that this task is currently waiting on. + * Task identifiers 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 @@ -746,7 +746,7 @@ public void removeWaitingOnTask(String taskId) { } /** - * Retrieves the ID of the node currently executing this task. + * Node identifier 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. * diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java index 93f57f2a3..b6121792f 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java @@ -22,9 +22,9 @@ import java.util.Date; /** - * Represents an API key for tenant authentication and authorization. - * This class extends the base Item class and provides functionality for managing - * API keys including their lifecycle (creation, expiration, revocation) and metadata. + * Persisted credential used to authenticate REST calls for a {@link Tenant}. + * Stores hashed key material, scope, expiration, and revocation state. Plaintext + * keys are only returned once at creation time via {@link ApiKeyCreationResult}. */ public class ApiKey extends Item { /** @@ -142,6 +142,7 @@ public static String maskPlainTextKey(String plainTextKey) { /** * Gets the SHA-256 digest of the API key. + * * @return the key hash as lowercase hex */ public String getKeyHash() { @@ -150,6 +151,7 @@ public String getKeyHash() { /** * Sets the SHA-256 digest of the API key. + * * @param keyHash the key hash to set */ public void setKeyHash(String keyHash) { @@ -158,6 +160,7 @@ public void setKeyHash(String keyHash) { /** * Gets the display-safe masked representation of the key. + * * @return the masked key (e.g. "unomi_v1_****ab12") */ public String getMaskedKey() { @@ -166,6 +169,7 @@ public String getMaskedKey() { /** * Sets the display-safe masked representation of the key. + * * @param maskedKey the masked key to set */ public void setMaskedKey(String maskedKey) { @@ -174,6 +178,7 @@ public void setMaskedKey(String maskedKey) { /** * Gets the name or identifier of the API key. + * * @return the API key name */ public String getName() { @@ -182,6 +187,7 @@ public String getName() { /** * Sets the name or identifier of the API key. + * * @param name the API key name to set */ public void setName(String name) { @@ -190,6 +196,7 @@ public void setName(String name) { /** * Gets the description of the API key's purpose or usage. + * * @return the API key description */ public String getDescription() { @@ -198,6 +205,7 @@ public String getDescription() { /** * Sets the description of the API key's purpose or usage. + * * @param description the API key description to set */ public void setDescription(String description) { @@ -206,6 +214,7 @@ public void setDescription(String description) { /** * Gets the creation date of the API key. + * * @return the creation date */ @Override @@ -215,6 +224,7 @@ public Date getCreationDate() { /** * Sets the creation date of the API key. + * * @param creationDate the creation date to set */ public void setCreationDate(Date creationDate) { @@ -223,6 +233,7 @@ public void setCreationDate(Date creationDate) { /** * Gets the expiration date of the API key. + * * @return the expiration date */ public Date getExpirationDate() { @@ -231,6 +242,7 @@ public Date getExpirationDate() { /** * Sets the expiration date of the API key. + * * @param expirationDate the expiration date to set */ public void setExpirationDate(Date expirationDate) { @@ -239,6 +251,7 @@ public void setExpirationDate(Date expirationDate) { /** * Checks if the API key has been revoked. + * * @return true if the API key is revoked, false otherwise */ public boolean isRevoked() { @@ -247,6 +260,7 @@ public boolean isRevoked() { /** * Sets the revocation status of the API key. + * * @param revoked true to revoke the API key, false to reinstate */ public void setRevoked(boolean revoked) { @@ -255,6 +269,7 @@ public void setRevoked(boolean revoked) { /** * Gets the type of the API key. + * * @return the API key type */ public ApiKeyType getKeyType() { @@ -263,6 +278,7 @@ public ApiKeyType getKeyType() { /** * Sets the type of the API key. + * * @param keyType the API key type to set */ public void setKeyType(ApiKeyType keyType) { diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyConfig.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyConfig.java index aada3c21b..14e94cd5a 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyConfig.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyConfig.java @@ -20,9 +20,9 @@ import java.util.Map; /** - * Configuration settings for API keys. - * This class defines the configuration parameters for API key management, - * including validation rules and usage limits. + * Policy constraints applied when creating or validating {@link ApiKey} instances. + * Defines length and pattern rules, expiration defaults, allowed scopes, active-key + * limits, and optional per-scope rate limits consumed by tenant security services. */ public class ApiKeyConfig { private int minLength; @@ -36,6 +36,7 @@ public class ApiKeyConfig { /** * Gets the minimum length required for API keys. + * * @return the minimum length */ public int getMinLength() { @@ -44,6 +45,7 @@ public int getMinLength() { /** * Sets the minimum length required for API keys. + * * @param minLength the minimum length to set */ public void setMinLength(int minLength) { @@ -52,6 +54,7 @@ public void setMinLength(int minLength) { /** * Gets the maximum length allowed for API keys. + * * @return the maximum length */ public int getMaxLength() { @@ -60,6 +63,7 @@ public int getMaxLength() { /** * Sets the maximum length allowed for API keys. + * * @param maxLength the maximum length to set */ public void setMaxLength(int maxLength) { @@ -68,6 +72,7 @@ public void setMaxLength(int maxLength) { /** * Gets the regex pattern for API key validation. + * * @return the validation pattern */ public String getPattern() { @@ -76,6 +81,7 @@ public String getPattern() { /** * Sets the regex pattern for API key validation. + * * @param pattern the validation pattern to set */ public void setPattern(String pattern) { @@ -84,6 +90,7 @@ public void setPattern(String pattern) { /** * Gets the maximum number of active API keys allowed. + * * @return the maximum number of active keys */ public int getMaxActiveKeys() { @@ -92,6 +99,7 @@ public int getMaxActiveKeys() { /** * Sets the maximum number of active API keys allowed. + * * @param maxActiveKeys the maximum number to set */ public void setMaxActiveKeys(int maxActiveKeys) { @@ -100,6 +108,7 @@ public void setMaxActiveKeys(int maxActiveKeys) { /** * Gets the default expiration period in days for new API keys. + * * @return the default expiration period in days */ public int getDefaultExpirationDays() { @@ -108,6 +117,7 @@ public int getDefaultExpirationDays() { /** * Sets the default expiration period in days for new API keys. + * * @param defaultExpirationDays the default expiration period to set */ public void setDefaultExpirationDays(int defaultExpirationDays) { @@ -116,6 +126,7 @@ public void setDefaultExpirationDays(int defaultExpirationDays) { /** * Gets the list of allowed scopes for API keys. + * * @return list of allowed scopes */ public List getAllowedScopes() { @@ -124,6 +135,7 @@ public List getAllowedScopes() { /** * Sets the list of allowed scopes for API keys. + * * @param allowedScopes list of allowed scopes to set */ public void setAllowedScopes(List allowedScopes) { @@ -132,6 +144,7 @@ public void setAllowedScopes(List allowedScopes) { /** * Gets the rate limits for different operations. + * * @return map of operation names to their rate limits */ public Map getRateLimits() { @@ -140,6 +153,7 @@ public Map getRateLimits() { /** * Sets the rate limits for different operations. + * * @param rateLimits map of operation names to their rate limits */ public void setRateLimits(Map rateLimits) { @@ -148,6 +162,7 @@ public void setRateLimits(Map rateLimits) { /** * Gets additional configuration settings. + * * @return map of additional settings */ public Map getAdditionalSettings() { @@ -156,6 +171,7 @@ public Map getAdditionalSettings() { /** * Sets additional configuration settings. + * * @param additionalSettings map of additional settings to set */ public void setAdditionalSettings(Map additionalSettings) { diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java index 0419bbe59..8bbea6daa 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java @@ -41,6 +41,7 @@ public ApiKeyCreationResult() { * and the plaintext key value. * The plaintext key is only available at creation time, as * it is not persisted. + * * @param apiKey the API key metadata that was successfully persisted. * @param plainTextKey the one-time plaintext key value. */ @@ -51,6 +52,7 @@ public ApiKeyCreationResult(ApiKey apiKey, String plainTextKey) { /** * Gets the persisted API key metadata (type, masked key, dates, etc.), without the secret. + * * @return the API key metadata */ public ApiKey getApiKey() { @@ -59,6 +61,7 @@ public ApiKey getApiKey() { /** * Sets the persisted API key metadata. + * * @param apiKey the API key metadata to set */ public void setApiKey(ApiKey apiKey) { @@ -68,6 +71,7 @@ public void setApiKey(ApiKey apiKey) { /** * Gets the one-time plaintext key value. This is only available right after creation; * it is never persisted and cannot be retrieved again afterwards. + * * @return the plaintext API key */ public String getPlainTextKey() { @@ -76,6 +80,7 @@ public String getPlainTextKey() { /** * Sets the one-time plaintext key value. + * * @param plainTextKey the plaintext API key to set */ public void setPlainTextKey(String plainTextKey) { diff --git a/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java b/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java index 758777d8b..1c9fde333 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/AuditService.java @@ -51,20 +51,20 @@ public interface AuditService extends ItemAuditService, TenantAuditService { void auditDelete(Item item, String userId); /** - * Retrieves items modified since a specific date. + * Returns configuration items modified after the given date. * - * @param tenantId the tenant ID to filter by - * @param since the date to check modifications from - * @return a list of modified items + * @param tenantId tenant identifier + * @param since earliest modification timestamp (exclusive) + * @return modified items */ List getModifiedItems(String tenantId, Date since); /** - * Retrieves items modified since the last synchronization. + * Returns items modified since the last sync with a source instance. * - * @param tenantId the tenant ID to filter by - * @param sourceInstanceId the source instance ID - * @return a list of modified items + * @param tenantId tenant identifier + * @param sourceInstanceId source cluster node identifier + * @return modified items since last sync */ List getModifiedItemsSinceLastSync(String tenantId, String sourceInstanceId); @@ -78,11 +78,11 @@ public interface AuditService extends ItemAuditService, TenantAuditService { void updateLastSyncDate(String tenantId, String sourceInstanceId, Date syncDate); /** - * Retrieves the last synchronization date. + * Returns the last sync timestamp for a tenant and source instance pair. * - * @param tenantId the tenant ID - * @param sourceInstanceId the source instance ID - * @return the last synchronization date + * @param tenantId tenant identifier + * @param sourceInstanceId source cluster node identifier + * @return last synchronization date */ Date getLastSyncDate(String tenantId, String sourceInstanceId); diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java b/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java index bccaf5a13..ff168c4cf 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/ItemAuditService.java @@ -51,20 +51,20 @@ public interface ItemAuditService { void auditDelete(Item item, String userId); /** - * Retrieves items modified since a specific date. + * Returns configuration items modified after the given date. * - * @param tenantId the tenant ID to filter by - * @param since the date to check modifications from - * @return a list of modified items + * @param tenantId tenant identifier + * @param since earliest modification timestamp (exclusive) + * @return modified items */ List getModifiedItems(String tenantId, Date since); /** - * Retrieves items modified since the last synchronization. + * Returns items modified since the last sync with a source instance. * - * @param tenantId the tenant ID to filter by - * @param sourceInstanceId the source instance ID - * @return a list of modified items + * @param tenantId tenant identifier + * @param sourceInstanceId source cluster node identifier + * @return modified items since last sync */ List getModifiedItemsSinceLastSync(String tenantId, String sourceInstanceId); @@ -78,11 +78,11 @@ public interface ItemAuditService { void updateLastSyncDate(String tenantId, String sourceInstanceId, Date syncDate); /** - * Retrieves the last synchronization date. + * Returns the last sync timestamp for a tenant and source instance pair. * - * @param tenantId the tenant ID - * @param sourceInstanceId the source instance ID - * @return the last synchronization date + * @param tenantId tenant identifier + * @param sourceInstanceId source cluster node identifier + * @return last synchronization date */ Date getLastSyncDate(String tenantId, String sourceInstanceId); @@ -96,4 +96,4 @@ default void updateModificationMetadata(Item item, String userId) { item.setLastModifiedBy(userId); item.setLastModificationDate(new Date()); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java b/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java index 6de2f11fe..c9c8abad9 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java @@ -25,12 +25,8 @@ import javax.xml.bind.annotation.XmlTransient; /** - * Represents a tenant in the system. - * A tenant is an isolated entity within the system with its own users, data, and configuration. - * Each tenant has its own set of API keys (public and private) for authentication and authorization, - * and event permissions to control access to specific event types. - * This class extends the base Item class and provides functionality for managing tenant - * settings and lifecycle. + * A tenant isolates data, API keys, and event permissions within a single Unomi deployment. + * Each tenant has its own users, configuration, and lifecycle settings. */ public class Tenant extends Item { /** @@ -101,6 +97,7 @@ public Tenant() { /** * Gets the tenant's display name. + * * @return the tenant name */ public String getName() { @@ -109,6 +106,7 @@ public String getName() { /** * Sets the tenant's display name. + * * @param name the tenant name to set */ public void setName(String name) { @@ -117,6 +115,7 @@ public void setName(String name) { /** * Gets the tenant's description. + * * @return the tenant description */ public String getDescription() { @@ -125,6 +124,7 @@ public String getDescription() { /** * Sets the tenant's description. + * * @param description the tenant description to set */ public void setDescription(String description) { @@ -133,6 +133,7 @@ public void setDescription(String description) { /** * Gets the tenant's current status. + * * @return the tenant status */ public TenantStatus getStatus() { @@ -141,6 +142,7 @@ public TenantStatus getStatus() { /** * Sets the tenant's status. + * * @param status the tenant status to set */ public void setStatus(TenantStatus status) { @@ -149,6 +151,7 @@ public void setStatus(TenantStatus status) { /** * Gets the tenant's creation date. + * * @return the creation date */ @Override @@ -158,6 +161,7 @@ public Date getCreationDate() { /** * Sets the tenant's creation date. + * * @param creationDate the creation date to set */ public void setCreationDate(Date creationDate) { @@ -166,6 +170,7 @@ public void setCreationDate(Date creationDate) { /** * Gets the tenant's last modification date. + * * @return the last modification date */ @Override @@ -175,6 +180,7 @@ public Date getLastModificationDate() { /** * Sets the tenant's last modification date. + * * @param lastModificationDate the last modification date to set */ public void setLastModificationDate(Date lastModificationDate) { @@ -184,6 +190,7 @@ public void setLastModificationDate(Date lastModificationDate) { /** * Gets the list of all API keys associated with the tenant. * This includes both active and historical keys for auditing purposes. + * * @return the list of API keys */ public List getApiKeys() { @@ -192,6 +199,7 @@ public List getApiKeys() { /** * Sets the list of API keys associated with the tenant. + * * @param apiKeys the list of API keys to set */ public void setApiKeys(List apiKeys) { @@ -200,6 +208,7 @@ public void setApiKeys(List apiKeys) { /** * Gets additional tenant properties as key-value pairs. + * * @return map of additional properties */ public Map getProperties() { @@ -208,6 +217,7 @@ public Map getProperties() { /** * Sets additional tenant properties as key-value pairs. + * * @param properties map of additional properties to set */ public void setProperties(Map properties) { @@ -217,6 +227,7 @@ public void setProperties(Map properties) { /** * Gets the set of event types that are restricted for this tenant. * Events of these types will require IP validation before being processed. + * * @return the set of restricted event types */ public Set getRestrictedEventTypes() { @@ -226,6 +237,7 @@ public Set getRestrictedEventTypes() { /** * Sets the event types that are restricted for this tenant. * Events of these types will require IP validation before being processed. + * * @param restrictedEventTypes the set of restricted event types to set */ public void setRestrictedEventTypes(Set restrictedEventTypes) { @@ -234,6 +246,7 @@ public void setRestrictedEventTypes(Set restrictedEventTypes) { /** * Gets the set of authorized IP addresses or CIDR ranges for this tenant. + * * @return the set of authorized IP addresses/ranges */ public Set getAuthorizedIPs() { @@ -242,6 +255,7 @@ public Set getAuthorizedIPs() { /** * Sets the authorized IP addresses or CIDR ranges for this tenant. + * * @param authorizedIPs the set of authorized IP addresses/ranges to set */ public void setAuthorizedIPs(Set authorizedIPs) { @@ -254,6 +268,7 @@ public void setAuthorizedIPs(Set authorizedIPs) { * non-revoked, non-expired private key, but since the plaintext key is never persisted (see * UNOMI-938), this cannot be used to authenticate as the tenant; the real plaintext key is * only available once, at creation time, via {@link ApiKeyCreationResult}. + * * @return the masked private API key, or null if no valid private key exists */ @XmlTransient @@ -267,6 +282,7 @@ public String getPrivateApiKey() { * non-revoked, non-expired public key, but since the plaintext key is never persisted (see * UNOMI-938), this cannot be used in client-side applications; the real plaintext key is * only available once, at creation time, via {@link ApiKeyCreationResult}. + * * @return the masked public API key, or null if no valid public key exists */ @XmlTransient @@ -277,6 +293,7 @@ public String getPublicApiKey() { /** * Gets all active private API keys for the tenant. * This method returns all non-revoked, non-expired private keys. + * * @return list of active private API keys, or empty list if none exist */ @XmlTransient @@ -287,6 +304,7 @@ public List getActivePrivateApiKeys() { /** * Gets all active public API keys for the tenant. * This method returns all non-revoked, non-expired public keys. + * * @return list of active public API keys, or empty list if none exist */ @XmlTransient @@ -297,6 +315,7 @@ public List getActivePublicApiKeys() { /** * Gets all active API keys for the tenant. * This method returns all non-revoked, non-expired keys regardless of type. + * * @return list of all active API keys, or empty list if none exist */ @XmlTransient diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java index ab3105a0f..4c6fac1e1 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantAuditService.java @@ -28,4 +28,4 @@ public interface TenantAuditService { * @param operation the operation being performed */ void logTenantOperation(String tenantId, String operation); -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java index fe76b6e57..0c9740e8a 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantBackupMetadata.java @@ -17,48 +17,50 @@ package org.apache.unomi.api.tenants; /** - * Represents metadata used to track the state or existence of a backup - * for a specific tenant. - * This class encapsulates essential information linking a backup record - * to its owner and time. - * The {@link TenantBackupMetadata} holds two primary pieces of data: the unique - * identifier of the tenant, stored in {@code tenantId}, and a timestamp - * indicating when this metadata was generated or pertains to, stored - * in {@code timestamp}. + * Bookmark for a tenant backup operation. + * Associates a {@code tenantId} with the {@code timestamp} when backup metadata + * was recorded so restore and housekeeping jobs can tell which backup generation + * applies to which tenant. */ public class TenantBackupMetadata { + /** Tenant that owns this backup record. */ private String tenantId; + /** When this backup metadata was recorded (milliseconds since epoch). */ private long timestamp; /** - * Retrieves the tenant ID associated with this metadata object. - * @return The stored {@link String} tenant ID. + * Tenant that owns this backup. + * + * @return tenant id */ public String getTenantId() { return tenantId; } /** - * Sets the tenant ID for this metadata object. - * @param tenantId The new {@link String} tenant ID to set. + * Sets the tenant id. + * + * @param tenantId tenant id */ public void setTenantId(String tenantId) { this.tenantId = tenantId; } /** - * Retrieves the timestamp associated with this metadata object. - * @return The stored {@link Long} timestamp. + * When this backup metadata was recorded. + * + * @return timestamp in milliseconds since epoch */ public long getTimestamp() { return timestamp; } /** - * Sets the timestamp for this metadata object. - * @param timestamp The new {@link Long} timestamp to set. + * Sets the backup metadata timestamp. + * + * @param timestamp timestamp in milliseconds since epoch */ public void setTimestamp(long timestamp) { this.timestamp = timestamp; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java index 1c070aa76..eed555a7b 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java @@ -37,9 +37,9 @@ public class TenantEventPurgeResult { private long requestedAt; /** - * Retrieves the unique identifier of the tenant - * associated with this result. - * @return The tenant ID string. + * Tenant this purge result applies to. + * + * @return tenant id */ public String getTenantId() { return tenantId; @@ -47,6 +47,7 @@ public String getTenantId() { /** * Sets the unique identifier of the tenant associated with this result. + * * @param tenantId The tenant ID to set. */ public void setTenantId(String tenantId) { @@ -54,9 +55,9 @@ public void setTenantId(String tenantId) { } /** - * Retrieves the number of days events must be retained before - * purge consideration. - * @return The retention period in days. + * Retention window in days before events become eligible for purge. + * + * @return retention period in days */ public int getRetentionDays() { return retentionDays; @@ -65,6 +66,7 @@ public int getRetentionDays() { /** * Sets the number of days events must be retained before * purge consideration. + * * @param retentionDays The retention period in days. */ public void setRetentionDays(int retentionDays) { @@ -72,9 +74,10 @@ public void setRetentionDays(int retentionDays) { } /** - * Retrieves the estimated number of events that matched the retention - * cutoff before the delete-by-query was submitted; not a post-delete count. - * @return The count of matching events. + * Estimated event count matching the retention cutoff before delete-by-query ran. + * This is a pre-delete estimate, not a post-delete count. + * + * @return estimated matching event count */ public long getEventsMatched() { return eventsMatched; @@ -83,6 +86,7 @@ public long getEventsMatched() { /** * Sets the estimated number of events that matched the retention cutoff * before the delete-by-query was submitted; not a post-delete count. + * * @param eventsMatched The estimated count of matching events. */ public void setEventsMatched(long eventsMatched) { @@ -114,6 +118,7 @@ public void setPurgeRequested(boolean purgeRequested) { /** * Gets the timestamp (in milliseconds) when this event retention purge * request was initiated. + * * @return The requested time in milliseconds since the epoch. */ public long getRequestedAt() { @@ -122,6 +127,7 @@ public long getRequestedAt() { /** * Sets the timestamp when the event retention purge request was initiated. + * * @param requestedAt the time in milliseconds since the epoch */ public void setRequestedAt(long requestedAt) { diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java index 775b7be28..057240412 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java @@ -17,9 +17,10 @@ package org.apache.unomi.api.tenants; /** - * Usage counters for a single scope inside a tenant usage snapshot. - * Tracks how many segments and rules exist in that scope when - * {@link TenantUsageService} collects statistics. + * Per-scope slice of a {@link TenantUsage} snapshot. + * When usage collection runs, each scope contributes segment and rule counts so + * operators can see which sites or applications consume the most configuration + * inside a tenant. */ public class TenantScopeUsage { @@ -28,48 +29,54 @@ public class TenantScopeUsage { private long ruleCount; /** - * Retrieves the unique identifier of the scope. - * @return The scope ID as a {@link String}. + * Scope identifier. + * + * @return scope id */ public String getScopeId() { return scopeId; } /** - * Sets the unique identifier of the scope. - * @param scopeId The new scope ID to set. + * Sets the scope identifier. + * + * @param scopeId scope id */ public void setScopeId(String scopeId) { this.scopeId = scopeId; } /** - * Retrieves the count of segments associated with this scope usage. - * @return The segment count as a {@link Long}. + * Number of segments in this scope. + * + * @return segment count */ public long getSegmentCount() { return segmentCount; } /** - * Sets the count of segments associated with this scope usage. - * @param segmentCount The new segment count to set. + * Sets the segment count. + * + * @param segmentCount segment count */ public void setSegmentCount(long segmentCount) { this.segmentCount = segmentCount; } /** - * Retrieves the count of rules associated with this scope usage. - * @return The rule count as a {@link Long}. + * Number of rules in this scope. + * + * @return rule count */ public long getRuleCount() { return ruleCount; } /** - * Sets the count of rules associated with this scope usage. - * @param ruleCount The new rule count to set. + * Sets the rule count. + * + * @param ruleCount rule count */ public void setRuleCount(long ruleCount) { this.ruleCount = ruleCount; diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java index 91414ce90..9313ecd8e 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java @@ -53,9 +53,9 @@ public class TenantUsage { private long collectedAt; /** - * Retrieves the unique identifier for the tenant associated with - * this usage snapshot. - * @return The tenant ID string. + * Tenant this usage snapshot describes. + * + * @return tenant id */ public String getTenantId() { return tenantId; @@ -64,6 +64,7 @@ public String getTenantId() { /** * Sets the unique identifier for the tenant associated with * this usage snapshot. + * * @param tenantId The ID of the tenant. */ public void setTenantId(String tenantId) { @@ -73,6 +74,7 @@ public void setTenantId(String tenantId) { /** * Returns the normalized period label, e.g., {@code 2026-07} * for a calendar month. + * * @return The period label string. */ public String getPeriod() { @@ -82,6 +84,7 @@ public String getPeriod() { /** * Sets the normalized period label, e.g., {@code 2026-07} * for a calendar month. + * * @param period The desired period label. */ public void setPeriod(String period) { @@ -90,6 +93,7 @@ public void setPeriod(String period) { /** * Returns the inclusive start of the reporting period (epoch millis, UTC). + * * @return The starting epoch millisecond value. */ public long getPeriodStart() { @@ -98,6 +102,7 @@ public long getPeriodStart() { /** * Sets the inclusive start of the reporting period (epoch millis, UTC). + * * @param periodStart The starting epoch millisecond value. */ public void setPeriodStart(long periodStart) { @@ -106,6 +111,7 @@ public void setPeriodStart(long periodStart) { /** * Returns the exclusive end of the reporting period (epoch millis, UTC). + * * @return The ending epoch millisecond value. */ public long getPeriodEnd() { @@ -114,6 +120,7 @@ public long getPeriodEnd() { /** * Sets the exclusive end of the reporting period (epoch millis, UTC). + * * @param periodEnd The ending epoch millisecond value. */ public void setPeriodEnd(long periodEnd) { @@ -121,9 +128,9 @@ public void setPeriodEnd(long periodEnd) { } /** - * Retrieves the number of profiles associated with this - * tenant usage snapshot. - * @return The profile count. + * Total profile count at collection time. + * + * @return profile count */ public long getProfileCount() { return profileCount; @@ -131,6 +138,7 @@ public long getProfileCount() { /** * Sets the number of profiles for this tenant usage snapshot. + * * @param profileCount The total count of profiles. */ public void setProfileCount(long profileCount) { @@ -151,6 +159,7 @@ public long getEventCount() { /** * Sets the count of events for this tenant usage snapshot. + * * @param eventCount The total number of events. */ public void setEventCount(long eventCount) { @@ -158,9 +167,9 @@ public void setEventCount(long eventCount) { } /** - * Retrieves the count of tenant scopes excluding - * {@code systemscope} for this period. - * @return The scope count. + * Tenant scope count excluding {@code systemscope}. + * + * @return scope count */ public long getScopeCount() { return scopeCount; @@ -168,6 +177,7 @@ public long getScopeCount() { /** * Sets the count of tenant scopes excluding {@code systemscope}. + * * @param scopeCount The total number of scopes. */ public void setScopeCount(long scopeCount) { @@ -175,9 +185,9 @@ public void setScopeCount(long scopeCount) { } /** - * Retrieves the count of segments associated with this - * tenant usage snapshot. - * @return The segment count. + * Segment count at collection time. + * + * @return segment count */ public long getSegmentCount() { return segmentCount; @@ -185,6 +195,7 @@ public long getSegmentCount() { /** * Sets the count of segments for this tenant usage snapshot. + * * @param segmentCount The total number of segments. */ public void setSegmentCount(long segmentCount) { @@ -192,8 +203,9 @@ public void setSegmentCount(long segmentCount) { } /** - * Retrieves the count of rules associated with this tenant usage snapshot. - * @return The number of rules. + * Rule count at collection time. + * + * @return rule count */ public long getRuleCount() { return ruleCount; @@ -201,6 +213,7 @@ public long getRuleCount() { /** * Sets the count of rules associated with this tenant usage snapshot. + * * @param ruleCount The total number of rules. */ public void setRuleCount(long ruleCount) { @@ -208,9 +221,9 @@ public void setRuleCount(long ruleCount) { } /** - * Retrieves the document count across all tenant indices - * recorded in this snapshot. - * @return The total document count. + * Document count across all tenant indices at collection time. + * + * @return total document count */ public long getStorageDocumentCount() { return storageDocumentCount; @@ -219,6 +232,7 @@ public long getStorageDocumentCount() { /** * Sets the document count across all tenant indices * recorded in this snapshot. + * * @param storageDocumentCount The total number of documents. */ public void setStorageDocumentCount(long storageDocumentCount) { @@ -226,9 +240,9 @@ public void setStorageDocumentCount(long storageDocumentCount) { } /** - * Retrieves the count of active API keys on the tenant record - * for this usage period. - * @return The number of active API keys. + * Active API key count on the tenant record. + * + * @return active API key count */ public long getActiveApiKeyCount() { return activeApiKeyCount; @@ -237,6 +251,7 @@ public long getActiveApiKeyCount() { /** * Sets the count of active API keys on the tenant record * for this usage period. + * * @param activeApiKeyCount The total number of active API keys. */ public void setActiveApiKeyCount(long activeApiKeyCount) { @@ -244,9 +259,9 @@ public void setActiveApiKeyCount(long activeApiKeyCount) { } /** - * Retrieves the in-memory REST request counter for this tenant since the - * Unomi process started. - * @return The count of REST requests. + * In-memory REST request counter for this tenant since process start. + * + * @return REST request count */ public long getRestRequestCount() { return restRequestCount; @@ -255,6 +270,7 @@ public long getRestRequestCount() { /** * Sets the in-memory REST request counter for this tenant since the * Unomi process started. + * * @param restRequestCount The total number of REST requests recorded. */ public void setRestRequestCount(long restRequestCount) { @@ -262,9 +278,9 @@ public void setRestRequestCount(long restRequestCount) { } /** - * Retrieves an unmodifiable list of usage details for all tenant scopes. - * @return An unmodifiable {@link List} containing - * scope usage snapshots. + * Per-scope usage breakdown for this snapshot. + * + * @return unmodifiable scope usage list */ public List getScopeUsages() { return Collections.unmodifiableList(scopeUsages); @@ -274,6 +290,7 @@ public List getScopeUsages() { * Sets the list of usage details for all tenant scopes. This method * performs a defensive copy of the provided list to ensure * internal state immutability. + * * @param scopeUsages The list of {@link TenantScopeUsage} objects. */ public void setScopeUsages(List scopeUsages) { @@ -284,6 +301,7 @@ public void setScopeUsages(List scopeUsages) { /** * Gets the timestamp (epoch millis) when this usage snapshot was collected. + * * @return The collection time in epoch milliseconds. */ public long getCollectedAt() { @@ -293,6 +311,7 @@ public long getCollectedAt() { /** * Sets the timestamp (epoch millis) indicating when this usage * snapshot was collected. + * * @param collectedAt The collection time in epoch milliseconds. */ public void setCollectedAt(long collectedAt) { diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java index 5ca4850a9..df44ed56b 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsageService.java @@ -43,6 +43,7 @@ public interface TenantUsageService { /** * Records one authenticated REST request for the tenant (in-memory counter). + * * @param tenantId tenant identifier */ void recordRestRequest(String tenantId); diff --git a/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java b/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java index 3c4166531..8fba8c63a 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityAuditReport.java @@ -21,9 +21,10 @@ import java.util.Map; /** - * Represents a security audit report for a tenant. - * This class contains information about security-related events and statistics - * within a specified time period. + * Security activity summary for one tenant over a time window. + * {@link TenantSecurityService} fills this report with authentication events, + * per-type counts, and aggregate statistics so operators can review access + * patterns and failed attempts for a given period. */ public class SecurityAuditReport { private String tenantId; @@ -34,96 +35,108 @@ public class SecurityAuditReport { private Map statistics; /** - * Gets the tenant ID associated with this report. - * @return the tenant ID + * Tenant covered by this audit report. + * + * @return tenant id */ public String getTenantId() { return tenantId; } /** - * Sets the tenant ID associated with this report. - * @param tenantId the tenant ID to set + * Sets the tenant id for this audit report. + * + * @param tenantId tenant id */ public void setTenantId(String tenantId) { this.tenantId = tenantId; } /** - * Gets the start date of the audit period. - * @return the start date + * Inclusive start of the audit window. + * + * @return start date */ public Date getStartDate() { return startDate; } /** - * Sets the start date of the audit period. - * @param startDate the start date to set + * Sets the inclusive start of the audit window. + * + * @param startDate start date */ public void setStartDate(Date startDate) { this.startDate = startDate; } /** - * Gets the end date of the audit period. - * @return the end date + * Exclusive end of the audit window. + * + * @return end date */ public Date getEndDate() { return endDate; } /** - * Sets the end date of the audit period. - * @param endDate the end date to set + * Sets the exclusive end of the audit window. + * + * @param endDate end date */ public void setEndDate(Date endDate) { this.endDate = endDate; } /** - * Gets the list of security events. - * @return list of security events + * Security events recorded in the audit window. + * + * @return security events */ public List getEvents() { return events; } /** - * Sets the list of security events. - * @param events list of security events to set + * Sets the security events for this report. + * + * @param events security events */ public void setEvents(List events) { this.events = events; } /** - * Gets the count of events by type. - * @return map of event types to their counts + * Event counts grouped by event type. + * + * @return map of event type to count */ public Map getEventCounts() { return eventCounts; } /** - * Sets the count of events by type. - * @param eventCounts map of event types to their counts + * Sets event counts grouped by event type. + * + * @param eventCounts map of event type to count */ public void setEventCounts(Map eventCounts) { this.eventCounts = eventCounts; } /** - * Gets additional statistics about the audit period. - * @return map of statistics + * Aggregate statistics for the audit window. + * + * @return statistics map */ public Map getStatistics() { return statistics; } /** - * Sets additional statistics about the audit period. - * @param statistics map of statistics to set + * Sets aggregate statistics for the audit window. + * + * @param statistics statistics map */ public void setStatistics(Map statistics) { this.statistics = statistics; @@ -141,8 +154,9 @@ public static class SecurityEvent { private Map details; /** - * Retrieves the type identifier of this security event. - * @return The string representing the event type. + * Security event type identifier. + * + * @return event type */ public String getType() { return type; @@ -157,8 +171,9 @@ public void setType(String type) { } /** - * Retrieves the timestamp when the security event occurred. - * @return The {@link java.util.Date} representing the event time. + * When the security event occurred. + * + * @return event timestamp */ public Date getTimestamp() { return timestamp; @@ -174,8 +189,9 @@ public void setTimestamp(Date timestamp) { } /** - * Retrieves a detailed description of the security event. - * @return A string containing the event's description. + * Human-readable description of the security event. + * + * @return event description */ public String getDescription() { return description; @@ -190,8 +206,9 @@ public void setDescription(String description) { } /** - * Retrieves the user ID associated with this security event. - * @return The unique identifier of the user involved in the event. + * User associated with the security event. + * + * @return user id */ public String getUserId() { return userId; @@ -237,4 +254,4 @@ public void setDetails(Map details) { this.details = details; } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java b/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java index 0667d2de5..118172bb4 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/security/SecuritySettings.java @@ -20,9 +20,9 @@ import java.util.Map; /** - * Represents security settings for a tenant. - * This class contains configuration for various security aspects including - * authentication, authorization, and API access. + * Tenant-level security policy loaded and enforced by {@link TenantSecurityService}. + * Groups authentication rules (token/session settings), authorization mappings, + * rate limits, and API access constraints that apply to REST calls for one tenant. */ public class SecuritySettings { private boolean enabled; @@ -31,64 +31,72 @@ public class SecuritySettings { private Map additionalSettings; /** - * Gets whether security is enabled for the tenant. - * @return true if security is enabled, false otherwise + * Whether tenant security enforcement is enabled. + * + * @return {@code true} when security is enabled */ public boolean isEnabled() { return enabled; } /** - * Sets whether security is enabled for the tenant. - * @param enabled true to enable security, false to disable + * Enables or disables tenant security enforcement. + * + * @param enabled {@code true} to enable security */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** - * Gets the authentication configuration. - * @return the authentication configuration + * Authentication policy for this tenant. + * + * @return authentication configuration */ public AuthenticationConfig getAuthentication() { return authentication; } /** - * Sets the authentication configuration. - * @param authentication the authentication configuration to set + * Sets the authentication policy for this tenant. + * + * @param authentication authentication configuration */ public void setAuthentication(AuthenticationConfig authentication) { this.authentication = authentication; } /** - * Gets the authorization configuration. - * @return the authorization configuration + * Authorization policy for this tenant. + * + * @return authorization configuration */ public AuthorizationConfig getAuthorization() { return authorization; } /** - * Sets the authorization configuration. - * @param authorization the authorization configuration to set + * Sets the authorization policy for this tenant. + * + * @param authorization authorization configuration */ public void setAuthorization(AuthorizationConfig authorization) { this.authorization = authorization; } /** - * Gets additional security settings as key-value pairs. - * @return map of additional settings + * Extra security settings not covered by authentication or authorization. + * + * @return additional settings map */ public Map getAdditionalSettings() { return additionalSettings; } /** - * Sets additional security settings as key-value pairs. - * @param additionalSettings map of additional settings to set + * Sets extra security settings not covered by authentication or authorization. + * + * @param additionalSettings additional settings map */ public void setAdditionalSettings(Map additionalSettings) { this.additionalSettings = additionalSettings; @@ -121,9 +129,9 @@ public void setAllowedAuthMethods(List allowedAuthMethods) { } /** - * Retrieves the maximum number of login attempts - * permitted before lockout. - * @return The maximum number of login attempts. + * Maximum failed login attempts before account lockout. + * + * @return max login attempts */ public int getMaxLoginAttempts() { return maxLoginAttempts; @@ -183,9 +191,9 @@ public static class AuthorizationConfig { private Map> rolePermissions; /** - * Retrieves the list of roles configured for this tenant. - * @return A {@link java.util.List} of strings - * representing the assigned roles. + * Roles defined for this tenant. + * + * @return role names */ public List getRoles() { return roles; @@ -200,9 +208,9 @@ public void setRoles(List roles) { } /** - * Retrieves the list of permissions configured for this tenant. - * @return A {@link java.util.List} of strings representing the - * assigned permissions. + * Permissions defined for this tenant. + * + * @return permission names */ public List getPermissions() { return permissions; @@ -217,9 +225,9 @@ public void setPermissions(List permissions) { } /** - * Retrieves the mapping of roles to their associated permissions. - * @return A {@link java.util.Map} where keys are role names and values - * are lists of permissions. + * Maps each role to the permissions it grants. + * + * @return role-to-permissions map */ public Map> getRolePermissions() { return rolePermissions; @@ -234,4 +242,4 @@ public void setRolePermissions(Map> rolePermissions) { this.rolePermissions = rolePermissions; } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityValidationResult.java b/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityValidationResult.java index 88ed71ca1..427bb0f18 100644 --- a/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityValidationResult.java +++ b/api/src/main/java/org/apache/unomi/api/tenants/security/SecurityValidationResult.java @@ -20,9 +20,10 @@ import java.util.List; /** - * Represents the result of a security validation operation. - * This class contains information about whether the validation was successful, - * and if not, what errors were encountered. + * Outcome of a tenant security validation step. + * Carries a success flag plus structured error messages when authentication, + * authorization, or rate-limit checks fail so REST layers can return actionable + * feedback without re-running the validation logic. */ public class SecurityValidationResult { private boolean valid; @@ -39,6 +40,7 @@ public SecurityValidationResult() { /** * Gets whether the validation was successful. + * * @return true if validation passed, false otherwise */ public boolean isValid() { @@ -47,6 +49,7 @@ public boolean isValid() { /** * Sets the validation status. + * * @param valid true if validation passed, false otherwise */ public void setValid(boolean valid) { @@ -55,6 +58,7 @@ public void setValid(boolean valid) { /** * Gets the list of validation errors. + * * @return list of error messages */ public List getErrors() { @@ -63,6 +67,7 @@ public List getErrors() { /** * Sets the list of validation errors. + * * @param errors list of error messages */ public void setErrors(List errors) { @@ -71,6 +76,7 @@ public void setErrors(List errors) { /** * Adds an error message to the result. + * * @param error the error message to add */ public void addError(String error) { @@ -80,6 +86,7 @@ public void addError(String error) { /** * Gets the general message associated with the validation result. + * * @return the message */ public String getMessage() { @@ -88,6 +95,7 @@ public String getMessage() { /** * Sets the general message associated with the validation result. + * * @param message the message to set */ public void setMessage(String message) { 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 e9116d3a0..d2b051279 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 @@ -19,9 +19,10 @@ import javax.servlet.http.HttpServletRequest; /** - * Service interface for managing tenants-level security operations and validations. - * This service provides comprehensive security features including authentication, - * authorization, rate limiting, and security auditing for tenants-specific operations. + * Entry point for tenant-scoped REST security checks. + * Validates incoming HTTP requests against {@link SecuritySettings}, enforces + * rate limits, applies authorization rules, and can produce + * {@link SecurityAuditReport} data for operator review. */ public interface TenantSecurityService { diff --git a/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java b/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java index f827402bc..152dc4b74 100644 --- a/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java +++ b/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java @@ -67,6 +67,7 @@ public interface ConditionVisitor { /** * Visits a given {@link Condition} object, executing necessary logic * for that specific condition during traversal. + * * @param condition The condition being visited. */ void visit(Condition condition); @@ -74,6 +75,7 @@ public interface ConditionVisitor { * Executes post-visit logic for the specified {@link Condition} object. * This method is called after all descendants of the given condition * have been processed. + * * @param condition The condition whose children have * finished processing. */ @@ -94,10 +96,13 @@ public interface ValueExtractor { * provided event context. * This method is abstract and must be implemented by * concrete subclasses. + * * @param valueAsString The string value that needs to be * extracted or parsed. + * * @param event The {@link Event} providing context for * the extraction process. + * * @return An object representing the extracted value. * @throws IllegalAccessException if access to a required member fails. * @throws NoSuchMethodException if a necessary method cannot be found. @@ -128,13 +133,14 @@ public interface ValueExtractor { * {@code rootCondition} to ensure all nested conditions have their types * resolved against the provided definitions service. It updates the * condition type on the condition object if successful. - * {@code false} otherwise. + * * @param definitionsService service used to resolve condition types. * @param rootCondition the starting condition for resolution. * @param contextObjectName name of the object providing context * (for error messages). - * @return {@code true} if all reachable conditions were - * successfully resolved, + * + * @return {@code true} if all reachable conditions were successfully resolved, + * {@code false} otherwise */ public static boolean resolveConditionType(final DefinitionsService definitionsService, Condition rootCondition, String contextObjectName) { return resolveConditionType(definitionsService, rootCondition, contextObjectName, @@ -227,6 +233,7 @@ private static boolean resolveConditionType(final DefinitionsService definitions * This method traverses the entire structure of the condition, including * nested parameters and collections, and gathers all associated * condition type ids. + * * @param rootCondition the condition whose types are to be collected. * @return a list of unique strings representing the condition type IDs * found in the condition tree. @@ -257,6 +264,7 @@ public void postVisit(Condition condition) { * the provided visitor for each one. This method handles recursive * traversal through parameters and collections that may * contain nested conditions. + * * @param rootCondition the starting condition to visit. * @param visitor the {@link ConditionVisitor} implementation to execute on * every visited condition. @@ -289,10 +297,12 @@ public static void visitConditions(Condition rootCondition, ConditionVisitor vis * {@link DefinitionsService}. It logs warnings if resolution fails and * returns {@code false} if any structural error (null/empty actions) or * resolution failure occurs. + * * @param definitionsService service used to resolve action types. * @param rule the rule containing actions whose types need resolving. * @param ignoreErrors if {@code true}, warnings about missing or empty * actions are suppressed. + * * @return {@code true} if all actions were successfully resolved, * {@code false} otherwise. */ @@ -334,11 +344,11 @@ public static boolean resolveActionTypes(DefinitionsService definitionsService, * If the action's type is not already set, this method queries the * definitions service to find and set the correct {@link ActionType}. It * tracks unresolved types internally, logging warnings if resolution fails. - * {@code false} otherwise. + * * @param definitionsService service used to resolve action types. * @param action the action whose type needs resolving. - * @return {@code true} if the action's type was successfully - * resolved or already set, + * @return {@code true} if the action's type was successfully resolved or already set, + * {@code false} otherwise */ public static boolean resolveActionType(DefinitionsService definitionsService, Action action) { if (definitionsService == null) { @@ -367,6 +377,7 @@ public static boolean resolveActionType(DefinitionsService definitionsService, A * it falls back to resolving the value type directly using the definitions * service's lookup mechanism. * The provided {@code PropertyType} object will be modified if successful. + * * @param definitionsService service used to resolve property types. * @param propertyType the property type whose value needs resolution. */ @@ -388,9 +399,9 @@ public static void resolveValueType(DefinitionsService definitionsService, Prope } } - /** * @deprecated Use {@link #resolveConditionEventTypes(Condition, DefinitionsService)} instead. + * * @param rootCondition the condition whose event types are to be resolved * @return a set of unique event type identifiers */ @@ -404,6 +415,7 @@ public static Set resolveConditionEventTypes(Condition rootCondition) { * This method traverses the entire condition tree starting from * {@code rootCondition} and collects every distinct event type ID found in * any nested condition or parameter. + * * @param rootCondition the condition whose event types are to be resolved. * @param definitionsService service used for resolving event types. * @return a set of unique strings representing all event types @@ -435,6 +447,7 @@ public static class EventTypeConditionVisitor implements ConditionVisitor { /** * Constructs an {@code EventTypeConditionVisitor} and initializes it * with a service used to resolve condition types. + * * @param definitionsService The service providing definitions necessary * for resolving type information. */ @@ -506,9 +519,9 @@ public void postVisit(Condition condition) { } /** - * Retrieves the set of event type IDs that have been collected by - * this visitor instance. - * @return A {@link Set} containing all resolved event type identifiers. + * Event type ids collected during condition-tree traversal. + * + * @return collected event type ids */ public Set getEventTypeIds() { return eventTypeIds; @@ -523,12 +536,16 @@ public Set getEventTypeIds() { * these placeholders by calling {@link #extractValue(String, Event, Map)}. * The resulting map contains the processed values with resolved objects * instead of placeholder strings. + * * @param event The current event used for value * extraction during resolution. + * * @param map The input map containing potentially * placeholder-filled values. + * * @param valueExtractors A map linking value type names to their * respective extractors. + * * @return A new {@link Map} with the string placeholders * resolved into actual object values. */ @@ -577,17 +594,23 @@ public static Map parseMap(Event event, Map map, * value type from the raw value content. It then uses the provided * {@link ValueExtractor} to perform the actual extraction based * on the event context. + * * @param s The input string containing the type and * value separated by "::". + * * @param event The current event used during value extraction. * @param valueExtractors A map of available extractors * keyed by value type name. + * * @return The extracted object value, or null if no extractor is found * or extraction fails. + * * @throws IllegalAccessException If the underlying extractor * throws this exception. + * * @throws NoSuchMethodException If the underlying extractor * throws this exception. + * * @throws InvocationTargetException If the underlying extractor * throws this exception. */ @@ -616,9 +639,11 @@ public static Object extractValue(String s, Event event, Map values, Map getParentChain( /** * Deep copies a parameter value, handling Condition objects and collections containing Conditions. * This is a helper method to avoid code duplication when merging parameters. + * * @param value the parameter value to deep copy * @return a deep copy of the value, or the original value if it's not a Condition or collection */ @@ -748,6 +775,7 @@ private static Object deepCopyParameterValue(Object value) { * This method traverses the parent condition chain and returns the condition * at the end of the chain (the root parent). It merges parameters from all * levels in the chain into the context and the effective condition. + * * @param condition the condition to resolve * @param definitionsService service to resolve condition types * @param context context map for parameter merging (will be modified) @@ -765,6 +793,7 @@ public static Condition resolveEffectiveCondition( /** * Resolves the effective condition to use, following the parent chain. + * * @param condition the condition to resolve * @param definitionsService service to resolve condition types * @param context context map for parameter merging (will be modified) diff --git a/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java b/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java index 07d3c4cba..86cfc325c 100644 --- a/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java +++ b/api/src/main/java/org/apache/unomi/api/utils/YamlUtils.java @@ -178,7 +178,7 @@ public YamlMapBuilder putIfNotEmpty(String key, java.util.Collection collecti /** * Merges all fields from a Map into this builder. * This is useful for inheritance where subclasses want to include parent class fields. - * + * * Usage in subclasses: *

          * return YamlMapBuilder.create()
@@ -295,7 +295,7 @@ public static Object toYamlValue(Object value, Set visited, int maxDepth
         if (value instanceof Map) {
             Map inputMap = (Map) value;
             Map result = new LinkedHashMap<>();
-            
+
             if (!inputMap.isEmpty()) {
                 // Sort entries alphabetically by key string representation
                 inputMap.entrySet().stream()
@@ -308,7 +308,6 @@ public static Object toYamlValue(Object value, Set visited, int maxDepth
         return value;
     }
 
-
     /**
      * Formats a value as YAML using SnakeYaml.
      * This is a convenience method that delegates to SnakeYaml.
diff --git a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportConfiguration.java b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportConfiguration.java
index 36f09e394..02cef325e 100644
--- a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportConfiguration.java
+++ b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/ImportConfiguration.java
@@ -23,12 +23,15 @@
 import java.util.*;
 
 /**
- * Created by amidani on 28/04/2017.
+ * Router-side import job definition for bulk loading Unomi items.
+ * Extends {@link ImportExportConfiguration} with import-specific column mappings,
+ * type mappings, and merge behavior. Camel routes in the router extension read
+ * this configuration to stream external files into the persistence layer.
  */
 public class ImportConfiguration extends ImportExportConfiguration {
 
     /**
-     * The ImportConfiguration ITEM_TYPE
+     * Persistence item type for router import configurations.
      *
      * @see Item for a discussion of ITEM_TYPE
      */
@@ -39,7 +42,6 @@ public class ImportConfiguration extends ImportExportConfiguration {
     private boolean hasHeader = false;
     private boolean hasDeleteColumn = false;
 
-
     /**
      * Returns the property used to match existing profiles during import.
      *
@@ -50,35 +52,36 @@ public String getMergingProperty() {
     }
 
     /**
-     * Sets the merging property.
-     * @param mergingProperty property used to check if the profile exist when merging
+     * Sets the property used to match existing profiles during import.
+     *
+     * @param mergingProperty property used to check whether the profile exists when merging
      */
     public void setMergingProperty(String mergingProperty) {
         this.mergingProperty = mergingProperty;
     }
 
     /**
-     * Retrieves the import configuration overwriteExistingProfiles flag.
+     * Returns whether existing profiles are overwritten during import.
      *
-     * @return true if during the import existing profiles must be overwritten
+     * @return {@code true} when existing profiles must be overwritten
      */
     public boolean isOverwriteExistingProfiles() {
         return this.overwriteExistingProfiles;
     }
 
     /**
-     * Sets the overwriteExistingProfiles flag true/false.
+     * Sets whether existing profiles are overwritten during import.
      *
-     * @param overwriteExistingProfiles a boolean to set overwriteExistingProfiles in the import configuration
+     * @param overwriteExistingProfiles {@code true} to overwrite existing profiles
      */
     public void setOverwriteExistingProfiles(boolean overwriteExistingProfiles) {
         this.overwriteExistingProfiles = overwriteExistingProfiles;
     }
 
     /**
-     * Retrieves the import configuration propertiesToOverwrite field.
+     * Returns profile property names that may be overwritten during import.
      *
-     * @return propertiesToOverwrite list.
+     * @return the property names to overwrite, or {@code null} when not restricted
      */
     public List getPropertiesToOverwrite() {
         return propertiesToOverwrite;
@@ -94,36 +97,36 @@ public void setPropertiesToOverwrite(List propertiesToOverwrite) {
     }
 
     /**
-     * Retrieves the hasHeader flag.
+     * Returns whether the import file includes a header row.
      *
-     * @return true if the file imported by the current config has a header line.
+     * @return {@code true} when the first row is a header
      */
     public boolean isHasHeader() {
         return this.hasHeader;
     }
 
     /**
-     * Sets the hasHeader flag.
+     * Sets whether the import file includes a header row.
      *
-     * @param hasHeader new value for hasHeader
+     * @param hasHeader {@code true} when the first row is a header
      */
     public void setHasHeader(boolean hasHeader) {
         this.hasHeader = hasHeader;
     }
 
     /**
-     * Retrieves the hasDeleteColumn flag.
+     * Returns whether the import file includes a delete column.
      *
-     * @return true if the file imported by the current config has a delete column.
+     * @return {@code true} when a delete column is present
      */
     public boolean isHasDeleteColumn() {
         return this.hasDeleteColumn;
     }
 
     /**
-     * Sets the hasDeleteColumn flag.
+     * Sets whether the import file includes a delete column.
      *
-     * @param hasDeleteColumn new value for hasDeleteColumn
+     * @param hasDeleteColumn {@code true} when a delete column is present
      */
     public void setHasDeleteColumn(boolean hasDeleteColumn) {
         this.hasDeleteColumn = hasDeleteColumn;
diff --git a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterConstants.java b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterConstants.java
index 0f7a7fcf2..373b8e41f 100644
--- a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterConstants.java
+++ b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterConstants.java
@@ -17,7 +17,9 @@
 package org.apache.unomi.router.api;
 
 /**
- * Constants used by the Unomi router import/export integration.
+ * Shared constant names for the Unomi router import/export Camel integration.
+ * Defines configuration types, route identifiers, and event names used when
+ * import or export jobs are registered, refreshed, or torn down.
  */
 public interface RouterConstants {
     /**
diff --git a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterUtils.java b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterUtils.java
index a5cee7c63..7109b468d 100644
--- a/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterUtils.java
+++ b/extensions/router/router-api/src/main/java/org/apache/unomi/router/api/RouterUtils.java
@@ -23,7 +23,9 @@
 import java.util.Map;
 
 /**
- * Utility methods for router import/export configuration handling.
+ * Helper routines for building router import/export column mappings.
+ * Converts Unomi {@link PropertyType} metadata into header lists and default
+ * values used when CSV or similar files are parsed by router Camel processors.
  */
 public class RouterUtils {
 
diff --git a/manual/src/main/asciidoc/building-and-deploying.adoc b/manual/src/main/asciidoc/building-and-deploying.adoc
index a5a0e0c1f..6736fc335 100644
--- a/manual/src/main/asciidoc/building-and-deploying.adoc
+++ b/manual/src/main/asciidoc/building-and-deploying.adoc
@@ -367,7 +367,7 @@ This is only needed if you didn't use the generated package. Also, this is the p
 environment if you intend to re-deploy the context server KAR iteratively.
 
 Additional requirements:
-* Apache Karaf 4.2.x, http://karaf.apache.org[http://karaf.apache.org]
+* Apache Karaf 4.4.x (Unomi 3.1 ships with 4.4.11), Java 17+ — http://karaf.apache.org[http://karaf.apache.org]
 
 Before deploying, make sure that you have Apache Karaf properly installed. Depending of your usage, you may also have to increase the
  memory size by adjusting the following environment values in the bin/setenv(.bat)
@@ -384,7 +384,7 @@ Install the WAR support and CXF into Karaf by doing the following in the Karaf c
 
 [source]
 ----
-   feature:repo-add cxf-jaxrs 3.3.4
+   feature:repo-add cxf-jaxrs 3.6.x (match the CXF version pinned in the Unomi Karaf features)
    feature:repo-add mvn:org.apache.unomi/unomi-kar/VERSION/xml/features
    feature:install unomi-kar
 ----
diff --git a/manual/src/main/asciidoc/clustering.adoc b/manual/src/main/asciidoc/clustering.adoc
index 9d31aa6a2..e7b556da8 100644
--- a/manual/src/main/asciidoc/clustering.adoc
+++ b/manual/src/main/asciidoc/clustering.adoc
@@ -11,19 +11,84 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
+[[_clustering]]
 === Cluster setup
 
-Apache Karaf relies on Persistence to register nodes and manage cluster.
+Apache Unomi runs as one or more Karaf nodes that share the same Elasticsearch or OpenSearch backend.
+Since Unomi 3.0, cluster coordination uses the **persistence layer** (UNOMI-877): each node registers a
+`ClusterNode` record, publishes periodic heartbeats, and reads peer state from the search index.
+Karaf Cellar / Hazelcast are no longer required.
 
-You can control most of the important clustering settings through the centralized configuration file at
+This design also supports the <<_scheduler,task scheduler>> (cluster-wide locks, executor nodes) and the
+health-check **cluster** provider.
 
-    etc/unomi.custom.system.properties
+==== Required settings
 
-And notably using the following properties:
+Configure each node in `etc/unomi.custom.system.properties` (or via environment variables):
 
-    org.apache.unomi.cluster.public.address=${env:UNOMI_CLUSTER_PUBLIC_ADDRESS:-http://localhost:8181}
-    org.apache.unomi.cluster.internal.address=${env:UNOMI_CLUSTER_INTERNAL_ADDRESS:-https://localhost:9443}
-    org.apache.unomi.cluster.nodeId=${env:UNOMI_CLUSTER_NODEID:-unomi-node-1}
-    org.apache.unomi.cluster.nodeStatisticsUpdateFrequency=${env:UNOMI_CLUSTER_NODESTATISTICS_UPDATEFREQUENCY:-10000}
+[source,properties]
+----
+org.apache.unomi.cluster.public.address=${env:UNOMI_CLUSTER_PUBLIC_ADDRESS:-http://localhost:8181}
+org.apache.unomi.cluster.internal.address=${env:UNOMI_CLUSTER_INTERNAL_ADDRESS:-https://localhost:9443}
+org.apache.unomi.cluster.nodeId=${env:UNOMI_CLUSTER_NODEID:-unomi-node-1}
+org.apache.unomi.cluster.nodeStatisticsUpdateFrequency=${env:UNOMI_CLUSTER_NODESTATISTICS_UPDATEFREQUENCY:-10000}
+----
 
-Note that it is mandatory to set a different `org.apache.unomi.cluster.nodeId` for each node in the cluster.
+* **public.address** — URL other nodes and operators use to reach this node (HTTP, port 8181 by default).
+* **internal.address** — HTTPS URL for the private REST API (port 9443 by default).
+* **nodeId** — **Must be unique** on every node in the cluster (for example `unomi-node-1`, `unomi-node-2`).
+* **nodeStatisticsUpdateFrequency** — Interval in milliseconds between heartbeat / statistics updates (default 10 seconds).
+
+If `nodeId` is missing or duplicate, cluster registration fails at startup.
+
+==== How it works
+
+On startup the cluster service:
+
+1. Registers this node in persistence (`ClusterNode` item).
+2. Schedules a recurring task to update CPU, load, and uptime statistics.
+3. Schedules stale-node cleanup (nodes with no heartbeat for roughly three times the update interval are removed).
+
+Other nodes see the same registry through the shared search backend. The health-check extension reports
+cluster size and per-node status when you call `/health/check` (see <<_health_check,Health Check extension>>).
+
+[plantuml]
+----
+@startuml
+skinparam componentStyle rectangle
+
+database "Elasticsearch / OpenSearch" as IDX
+node "Unomi node A\n(nodeId=node-1)" as A
+node "Unomi node B\n(nodeId=node-2)" as B
+
+A --> IDX : register ClusterNode,\nheartbeat stats
+B --> IDX : register ClusterNode,\nheartbeat stats
+A ..> B : optional HTTP\n(public address)
+@enduml
+----
+
+==== Scheduler and executor nodes
+
+Background tasks can be marked **persistent** (stored in the index, visible on all nodes) or **local**
+(single node only). Only nodes that run the task executor process scheduled work.
+
+* Use the Karaf command `unomi:task-executor` to see or toggle executor mode on a node.
+* See <<_scheduler,Task scheduler>> for REST and shell operations on tasks.
+
+In a typical cluster you run multiple Unomi nodes with **distinct `nodeId` values**, all pointing at the
+same search backend, and enable the executor on every node that should run background jobs (or on a subset
+for dedicated worker nodes).
+
+==== Operations
+
+* **Inspect nodes** — `GET /cxs/cluster` (private API, system administrator credentials). Listed in
+  <<_useful_apache_unomi_urls,Useful Apache Unomi URLs>>.
+* **Health** — `GET /health/check` includes a `cluster` provider when the health-check feature is installed.
+* **Stale nodes** — Removed automatically when heartbeats stop; adjust `nodeStatisticsUpdateFrequency` if
+  your network is slow or nodes are under heavy load.
+
+==== Migration from Unomi 2.x / Cellar
+
+If you previously used Karaf Cellar for clustering, plan a full migration to persistence-based clustering
+as part of your Unomi 3 upgrade. See <<_upgrade_jira_reference,JIRA reference>> (UNOMI-877) and
+<<_upgrade_karaf,Karaf upgrade>> for platform constraints.
diff --git a/manual/src/main/asciidoc/configuration.adoc b/manual/src/main/asciidoc/configuration.adoc
index e9da98be8..96a265f43 100644
--- a/manual/src/main/asciidoc/configuration.adoc
+++ b/manual/src/main/asciidoc/configuration.adoc
@@ -733,7 +733,7 @@ org.apache.unomi.security.personalization.sanitizeConditions=${env:UNOMI_SECURIT
 
 Groovy actions offer the ability to define a set of actions and action types (aka action descriptors) purely from Groovy scripts defined at runtime.
 
-Initially submitted to Unomi through a purpose-built REST API endpoint, Groovy actions are then stored in Elasticsearch. When an event matches a rule configured to execute an action, the corresponding action is fetched from Elasticsearch and executed.
+Initially submitted to Unomi through a purpose-built REST API endpoint, Groovy actions are then stored in the persistence layer (Elasticsearch or OpenSearch). When an event matches a rule configured to execute an action, the corresponding action is fetched from persistence and executed.
 
 ===== Anatomy of a Groovy Action
 
@@ -1743,6 +1743,7 @@ cd apache-unomi-custom-*
 unomi:start elasticsearch-prod
 ----
 
+[[_health_check]]
 === Health Check Extension
 
 The Health Check extension provides a way to check is required Unomi components are 'live'.
diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc
index e222a5302..a464d89bc 100644
--- a/manual/src/main/asciidoc/index.adoc
+++ b/manual/src/main/asciidoc/index.adoc
@@ -50,6 +50,11 @@ include::request-examples.adoc[]
 
 include::configuration.adoc[]
 
+[[_multitenancy]]
+== Multi-tenancy
+
+include::multitenancy.adoc[]
+
 [[_json_schemas]]
 == JSON schemas
 
diff --git a/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc b/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc
index 92961678c..8a1f82c35 100644
--- a/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc
+++ b/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc
@@ -12,6 +12,7 @@
 // limitations under the License.
 //
 
+[[_migrate_from_3_0_to_3_1]]
 === Migration Overview
 
 Apache Unomi 3.1 introduces comprehensive multi-tenancy support, enabling complete data isolation between different tenants. This fundamental architectural change requires a new tenant-based authentication model while keeping all API endpoints unchanged.
diff --git a/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc b/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc
index a1cc67efc..b87bed040 100644
--- a/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc
+++ b/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc
@@ -12,7 +12,8 @@
 // limitations under the License.
 //
 
-= Apache Unomi V2 Compatibility Mode
+[[_v2_compatibility_mode]]
+=== V2 compatibility mode
 
 This document explains how to use the V2 compatibility mode in Apache Unomi V3, which allows V2 client applications to work with Unomi V3 without requiring API keys.
 
diff --git a/manual/src/main/asciidoc/multitenancy.adoc b/manual/src/main/asciidoc/multitenancy.adoc
index 72c77cdc2..9d9c5ae8c 100644
--- a/manual/src/main/asciidoc/multitenancy.adoc
+++ b/manual/src/main/asciidoc/multitenancy.adoc
@@ -12,12 +12,10 @@
 // limitations under the License.
 //
 
-= Apache Unomi Multi-tenancy
-:toc: macro
-:toclevels: 4
-:toc-title: Table of contents
+=== Multi-tenancy guide
 
-toc::[]
+Apache Unomi 3.1+ isolates profiles, events, segments, rules, and schemas per tenant.
+Each tenant has public and private API keys. See also <<_migrate_from_3_0_to_3_1,Migrate from 3.0 to 3.1>> for authentication changes.
 
 == Overview
 
@@ -30,7 +28,7 @@ Apache Unomi provides robust multi-tenancy support, allowing multiple organizati
 * Tenant-specific configuration
 * Per-tenant usage metrics (quotas enforced upstream)
 * Migration tools for existing data
-* Support for both REST and GraphQL APIs
+* Support for REST APIs (and GraphQL when enabled)
 
 == Authentication Methods
 
diff --git a/manual/src/main/asciidoc/request-examples.adoc b/manual/src/main/asciidoc/request-examples.adoc
index 69af6fe01..ee1a3e4e4 100644
--- a/manual/src/main/asciidoc/request-examples.adoc
+++ b/manual/src/main/asciidoc/request-examples.adoc
@@ -1382,6 +1382,7 @@ Best practices for Groovy actions:
 5. Test with various date formats
 ====
 
+[[_request_tracing_explain]]
 ==== Using the explain parameter for request tracing
 
 Apache Unomi provides a powerful request tracing feature through the `explain` query parameter. This feature helps administrators understand how requests are processed internally, including event processing, condition evaluations, and rule executions.
diff --git a/manual/src/main/asciidoc/security.adoc b/manual/src/main/asciidoc/security.adoc
index 02eb3bd83..ed9939c61 100644
--- a/manual/src/main/asciidoc/security.adoc
+++ b/manual/src/main/asciidoc/security.adoc
@@ -79,4 +79,6 @@ end note
 @enduml
 ----
 
+For tenant API keys, roles (`ROLE_UNOMI_TENANT_USER`, `ROLE_UNOMI_TENANT_ADMIN`, system administrator), and operation permissions, see <<_multitenancy,Multi-tenancy>>.
+
 ===== Overview 
\ No newline at end of file
diff --git a/manual/src/main/asciidoc/shell-commands.adoc b/manual/src/main/asciidoc/shell-commands.adoc
index 81e5b9873..654099053 100644
--- a/manual/src/main/asciidoc/shell-commands.adoc
+++ b/manual/src/main/asciidoc/shell-commands.adoc
@@ -205,6 +205,56 @@ command: `undeploy-definition BUNDLE_ID * *` when `BUNDLE_ID` is the identifier
 
 |===
 
+
+==== Tenant commands
+
+Multi-tenant shell operations (Unomi 3.1+). See <<_multitenancy,Multi-tenancy>> for REST equivalents.
+
+.Table Tenant commands
+|===
+|Command|Arguments|Description
+
+|tenant-get
+|n/a
+|Print the tenant ID stored for this Karaf session.
+
+|tenant-set
+|tenant-id
+|Set the current tenant for this shell session (`system` is allowed). Other CRUD commands use this context.
+
+|crud list tenant
+|[--csv]
+|List tenants (excludes the built-in `system` tenant from normal listings).
+
+|crud read tenant
+|-i tenant-id
+|Show one tenant as JSON.
+
+|crud create tenant
+|-d JSON or --file path
+|Create a tenant. Use `help` on the command for required properties.
+
+|crud update tenant
+|-i tenant-id -d JSON or --file path
+|Update tenant metadata.
+
+|crud delete tenant
+|-i tenant-id
+|Delete a tenant.
+
+|crud list apikey
+|[--csv]
+|List API keys across all tenants (keys are masked in the table).
+
+|crud create apikey
+|-d JSON
+|Create an API key for a tenant (`tenantId`, `keyType`, optional `validityPeriod`).
+
+|cache
+|--tenant ID [--clear]
+|Inspect or clear per-tenant caches (segments, rules, and related types).
+|===
+
 ==== Scheduler commands
 
 These commands manage background tasks created by the cluster-aware scheduler (see <<_scheduler,Task scheduler>>).
diff --git a/manual/src/main/asciidoc/useful-unomi-urls.adoc b/manual/src/main/asciidoc/useful-unomi-urls.adoc
index 1f43468e9..3c5fdf3ea 100644
--- a/manual/src/main/asciidoc/useful-unomi-urls.adoc
+++ b/manual/src/main/asciidoc/useful-unomi-urls.adoc
@@ -101,4 +101,40 @@ where PROFILE_ID is a profile identifier. This will indeed retrieve all the even
 |/cxs/tasks/{taskId}/resume
 |POST
 |Resume a crashed task from checkpoint
+
+|/cxs/tenants
+|GET
+|List all tenants (system administrator only). See <<_multitenancy,Multi-tenancy>>.
+
+|/cxs/tenants/{tenantId}
+|GET
+|Get one tenant (system administrator or tenant administrator for that tenant).
+
+|/cxs/tenants
+|POST
+|Create a tenant (system administrator). Returns generated public and private API keys.
+
+|/cxs/tenants/{tenantId}/apikeys
+|POST
+|Generate a new API key (`?type=PUBLIC` or `PRIVATE`, optional `validityDays`).
+
+|/cxs/tenants/{tenantId}/usage
+|GET
+|Tenant usage snapshot (`?period=current-month` or `YYYY-MM`).
+
+|/cxs/tenants/{tenantId}/purge/events
+|POST
+|Purge events older than `retentionDays` (destructive; tenant administrator).
+
+|/cxs/cluster
+|GET
+|List cluster nodes and statistics. See <<_clustering,Cluster setup>>.
+
+|/health/check
+|GET
+|Health check JSON (role `health`, default user `health`/`health`). See <<_health_check,Health Check extension>>.
+
+|/cxs/context.json?explain=true
+|POST
+|Context request with request tracing (admin roles). See <<_request_tracing_explain,Using the explain parameter for request tracing>>.
 |===
diff --git a/manual/src/main/asciidoc/whats-new.adoc b/manual/src/main/asciidoc/whats-new.adoc
index febe1479a..a7481b186 100644
--- a/manual/src/main/asciidoc/whats-new.adoc
+++ b/manual/src/main/asciidoc/whats-new.adoc
@@ -11,6 +11,39 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
+[[_whats_new_3_1]]
+=== What's new in Apache Unomi 3.1
+
+Apache Unomi 3.1 builds on the 3.0 platform (Elasticsearch 9 client, Karaf 4.4, Java 17) with operator-focused features for multi-tenant production deployments.
+
+==== Multi-tenancy and API keys
+
+Complete tenant isolation for profiles, events, segments, rules, and schemas. Public endpoints (for example `/cxs/context.json`) require a tenant public API key; administrative work uses tenant private keys or system administrator credentials.
+
+* Operator guide: <<_multitenancy,Multi-tenancy>>
+* Migration: <<_migrate_from_3_0_to_3_1,Migrate from 3.0 to 3.1>>
+* Optional 3.0 compatibility mode: <<_v2_compatibility_mode,V2/V3 compatibility mode>> (property `unomi.v3_0.compatibility.mode`)
+
+==== Cluster-aware task scheduler
+
+Built-in background job scheduler with persistence, cluster locks, recovery, REST API (`/cxs/tasks`), and Karaf shell commands.
+
+* <<_scheduler,Task scheduler>>
+
+==== OpenSearch 3 support
+
+Official alternative to Elasticsearch, including secured Docker deployments. See <<_configuration,Configuration>> and <<_opensearch_support,OpenSearch support>> (3.0 section below).
+
+==== Persistence-based clustering
+
+Cluster node registry and heartbeats through Elasticsearch or OpenSearch (no Karaf Cellar). See <<_clustering,Cluster setup>>.
+
+==== Tenant usage and retention
+
+Read-only per-tenant usage metrics and an event retention purge API for upstream control planes.
+
+* REST: `/cxs/tenants/{tenantId}/usage` and `/cxs/tenants/{tenantId}/purge/events` (see <<_useful_apache_unomi_urls,Useful URLs>>)
+
 === What's new in Apache Unomi 3.0
 
 Apache Unomi 3 is a new release focused on integrations of the client to support elasticsearch version 9.
@@ -29,9 +62,10 @@ A procedure to migrate your data from Elasticsearch 7 to Elasticsearch 9 can be
 
 === Karaf upgrade
 
-The Karaf version has been upgraded from 4.2.15 to 4.4.8 in order to support the latest versions of the dependencies.
+The Karaf version has been upgraded from 4.2.15 to 4.4.11 in order to support the latest versions of the dependencies.
 This upgrade also brings support for Java 17.
 
+[[_opensearch_support]]
 ==== OpenSearch Support
 
 Starting with version 3.1.0, Apache Unomi now officially supports OpenSearch 3 as an alternative to Elasticsearch. This addition gives users more flexibility in choosing their search engine backend. Key features include:
diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java
index 7cbd56132..6080be64d 100644
--- a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java
@@ -29,69 +29,59 @@
 import java.util.Map;
 
 /**
- * A service to provide persistence and retrieval of context server entities.
+ * Persistence SPI for storing and querying Unomi context-server entities.
  */
 public interface PersistenceService {
 
     /**
-     * A unique name to identify the persistence service.
-     * @return a string containing the unique name for the persistence service.
+     * Returns the unique name of this persistence backend implementation.
+     *
+     * @return the persistence service name
      */
     String getName();
 
     /**
-     * Retrieves all known items of the specified class.
-     * WARNING: this method can be quite computationally intensive and calling the paged version {@link #getAllItems(Class, int, int, String)} is preferred.
+     * Loads all items of the given type.
+     * WARNING: this method can be expensive; prefer the paged overload {@link #getAllItems(Class, int, int, String)}.
      *
-     * @param    the type of the {@link Item}s we want to retrieve
-     * @param clazz the {@link Item} subclass of entities we want to retrieve
-     * @return a list of all known items with the given type
+     * @param  the item type
+     * @param clazz the {@link Item} subclass to load
+     * @return all known items of that type
      */
      List getAllItems(Class clazz);
 
     /**
-     * Retrieves all known items of the specified class, ordered according to the specified {@code sortBy} String and and paged: only {@code size} of them are retrieved,
-     * starting with the {@code offset}-th one.
+     * Loads a paged slice of all items of the given type.
      * 

- * TODO: use a Query object instead of distinct parameters? - * - * @param the type of the {@link Item}s we want to retrieve - * @param clazz the {@link Item} subclass of entities we want to retrieve - * @param offset zero or a positive integer specifying the position of the first item in the total ordered collection of matching items - * @param size a positive integer specifying how many matching items should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of pages items with the given type + * Future API versions may replace these parameters with a { Query} object. * + * @param the item type + * @param clazz the {@link Item} subclass to load + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return a paged list of items */ PartialList getAllItems(Class clazz, int offset, int size, String sortBy); /** - * Retrieves all known items of the specified class, ordered according to the specified {@code sortBy} String and and paged: only {@code size} of them are retrieved, - * starting with the {@code offset}-th one. + * Loads a paged slice of all items of the given type, optionally using a scroll query. *

- * TODO: use a Query object instead of distinct parameters? - * - * @param the type of the {@link Item}s we want to retrieve - * @param clazz the {@link Item} subclass of entities we want to retrieve - * @param offset zero or a positive integer specifying the position of the first item in the total ordered collection of matching items - * @param size a positive integer specifying how many matching items should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @param scrollTimeValidity the time the scrolling query should stay valid. This must contain a time unit value such as the ones supported by ElasticSearch, such as - * * the ones declared here : https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units - * @return a {@link PartialList} of pages items with the given type + * Future API versions may replace these parameters with a { Query} object. * + * @param the item type + * @param clazz the {@link Item} subclass to load + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @param scrollTimeValidity scroll context lifetime, using Elasticsearch time-unit syntax + * @return a paged list of items, with scroll metadata when scrolling is enabled */ PartialList getAllItems(final Class clazz, int offset, int size, String sortBy, String scrollTimeValidity); /** - * Return true if the item which is saved in the persistence service is consistent + * Returns whether the item is consistent with the persistence backend schema. * - * @param item the item to the check if consistent - * @return {@code true} if the item is consistent, false otherwise + * @param item the item to validate + * @return {@code true} when the item is consistent */ boolean isConsistent(Item item); @@ -109,6 +99,7 @@ public interface PersistenceService { * @param item the item to persist * @param useBatching whether to use batching or not for saving the item. If activating there may be a delay between * the call to this method and the actual saving in the persistence backend. + * * @return {@code true} if the item was properly persisted, {@code false} otherwise */ boolean save(Item item, boolean useBatching); @@ -274,20 +265,20 @@ default boolean updateWithQueryAndStoredScript(Class clazz, String[] scripts, boolean updateWithQueryAndStoredScript(Date dateHint, Class clazz, String[] scripts, Map[] scriptParams, Condition[] conditions); /** - * Store script in the Database for later usage with updateWithQueryAndStoredScript function for example. + * Stores inline scripts in the persistence backend for later scripted updates. * - * @param scripts inline scripts map indexed by id - * @return {@code true} if the update was successful, {@code false} otherwise + * @param scripts inline scripts keyed by script ID + * @return {@code true} when all scripts are stored successfully */ boolean storeScripts(Map scripts); /** - * Retrieves the item identified with the specified identifier and with the specified Item subclass if it exists. + * Loads an item by ID and type. * - * @param the type of the Item subclass we want to retrieve - * @param itemId the identifier of the item we want to retrieve - * @param clazz the {@link Item} subclass of the item we want to retrieve - * @return the item identified with the specified identifier and with the specified Item subclass if it exists, {@code null} otherwise + * @param the item type + * @param itemId the item identifier + * @param clazz the {@link Item} subclass to load + * @return the item, or {@code null} when it does not exist */ T load(String itemId, Class clazz); @@ -298,11 +289,11 @@ default boolean updateWithQueryAndStoredScript(Class clazz, String[] scripts, T load(String itemId, Date dateHint, Class clazz); /** - * Load a custom item type identified by an identifier, an optional date hint and the identifier of the custom item type + * Loads a custom item by ID and custom item type. * - * @param itemId the identifier of the custom type we want to retrieve - * @param customItemType an identifier of the custom item type to load - * @return the CustomItem instance with the specified identifier and the custom item type if it exists, {@code null} otherwise + * @param itemId the custom item identifier + * @param customItemType the custom item type identifier + * @return the custom item, or {@code null} when it does not exist */ default CustomItem loadCustomItem(String itemId, String customItemType) { return loadCustomItem(itemId, null, customItemType); @@ -325,11 +316,11 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { boolean remove(String itemId, Class clazz); /** - * Remove a custom item identified by the custom item identifier and the custom item type identifier + * Deletes a custom item by ID and custom item type. * - * @param itemId the identifier of the custom item to be removed - * @param customItemType the name of the custom item type - * @return {@code true} if the deletion was successful, {@code false} otherwise + * @param itemId the custom item identifier + * @param customItemType the custom item type identifier + * @return {@code true} when deletion succeeds */ boolean removeCustomItem(String itemId, String customItemType); @@ -344,46 +335,42 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { boolean removeByQuery(Condition query, Class clazz); /** - * Retrieve the type mappings for a given itemType. This method queries the persistence service implementation - * to retrieve any type mappings it may have for the specified itemType. - *

- * This method may not return any results if the implementation doesn't support property type mappings + * Returns property mappings for the given item type, when supported by the backend. * - * @param itemType the itemType we want to retrieve the mappings for - * @return properties mapping + * @param itemType the item type name + * @return property name to mapping metadata */ Map> getPropertiesMapping(String itemType); /** - * Retrieve the mapping for one specific property for a given type. + * Returns the mapping metadata for one property on the given item type. * - * @param property the property name (can use nested dot notation) - * @param itemType the itemType we want to retrieve the mappings for - * @return property mapping + * @param property the property name, including nested dot notation + * @param itemType the item type name + * @return property mapping metadata */ Map getPropertyMapping(String property, String itemType); /** - * Create the persistence mapping for specific property for a given type. + * Creates or updates the persistence mapping for a property on the given item type. * - * @param property the PropertyType to create mapping for - * @param itemType the itemType we want to retrieve the mappings for + * @param property the property type definition + * @param itemType the item type name */ void setPropertyMapping(PropertyType property, String itemType); /** - * Create mapping + * Creates an index mapping from the given source definition. * - * @param type the type - * @param source the source + * @param type the item or index type + * @param source the mapping source definition */ void createMapping(String type, String source); /** * Checks whether the specified item satisfies the provided condition. *

- * TODO: rename to isMatching? - * + * The method name may change in a future release. * * @param query the condition we're testing the specified item against * @param item the item we're checking against the specified condition * @return {@code true} if the item satisfies the condition, {@code false} otherwise @@ -391,11 +378,11 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { boolean testMatch(Condition query, Item item); /** - * validates if a condition throws exception at query build. + * Returns whether the condition can be compiled for the given item without error. * - * @param condition the condition we're testing the specified item against - * @param item the item we're checking against the specified condition - * @return {@code true} if the item satisfies the condition, {@code false} otherwise + * @param condition the condition to validate + * @param item the sample item used during validation + * @return {@code true} when the condition is valid */ boolean isValidCondition(Condition condition, Item item); @@ -416,7 +403,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { List query(String fieldName, String fieldValue, String sortBy, Class clazz); /** - * Retrieves a list of items with the specified field having the specified values. + * Queries items whose field matches any of the given values. * * @param the type of the Item subclass we want to retrieve * @param fieldName the name of the field which we want items to have the specified values @@ -431,7 +418,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { List query(String fieldName, String[] fieldValues, String sortBy, Class clazz); /** - * Retrieves a list of items with the specified field having the specified value. + * Queries items whose field matches the given value. * * @param the type of the Item subclass we want to retrieve * @param fieldName the name of the field which we want items to have the specified value @@ -448,7 +435,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { PartialList query(String fieldName, String fieldValue, String sortBy, Class clazz, int offset, int size); /** - * Retrieves a list of items with the specified field having the specified value and having at least a field with the specified full text value in it, ordered according to the + * Queries items by field value and full-text match, with paging and optional sorting. * specified {@code sortBy} String and and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. * * @param the type of the Item subclass we want to retrieve @@ -467,7 +454,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { PartialList queryFullText(String fieldName, String fieldValue, String fulltext, String sortBy, Class clazz, int offset, int size); /** - * Retrieves a list of items having at least a field with the specified full text value in it, ordered according to the specified {@code sortBy} String and and paged: only + * Queries items that match a full-text search, with paging and optional sorting. * {@code size} of them are retrieved, starting with the {@code offset}-th one. * * @param the type of the Item subclass we want to retrieve @@ -499,7 +486,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { List query(Condition query, String sortBy, Class clazz); /** - * Retrieves a list of items satisfying the specified {@link Condition}, ordered according to the specified {@code sortBy} String and and paged: only {@code size} of them + * Queries items that satisfy the condition, with paging and optional sorting. * are retrieved, starting with the {@code offset}-th one. * * @param the type of the Item subclass we want to retrieve @@ -516,7 +503,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { PartialList query(Condition query, String sortBy, Class clazz, int offset, int size); /** - * Retrieves a list of items satisfying the specified {@link Condition}, ordered according to the specified {@code sortBy} String and and paged: only {@code size} of them + * Queries items that satisfy the condition, with paging and optional sorting. * are retrieved, starting with the {@code offset}-th one. If a scroll identifier and time validity are specified, they will be used to perform a scrolling query, meaning * that only partial results will be returned, but the scrolling can be continued. * @@ -532,6 +519,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { * this will be used as the scrolling window size. * @param scrollTimeValidity the time the scrolling query should stay valid. This must contain a time unit value such as the ones supported by ElasticSearch, such as * the ones declared here : https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units + * * @return a {@link PartialList} of items matching the specified criteria, with an scroll identifier and the scroll validity used if a scroll query was requested. */ PartialList query(Condition query, String sortBy, Class clazz, int offset, int size, String scrollTimeValidity); @@ -550,7 +538,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { PartialList continueScrollQuery(Class clazz, String scrollIdentifier, String scrollTimeValidity); /** - * Retrieves a list of items satisfying the specified {@link Condition}, ordered according to the specified + * Queries items that satisfy the condition, with paging and optional * {@code sortBy} String and paged: only {@code size} of them are retrieved, starting with the * {@code offset}-th one. If a scroll identifier and time validity are specified, they will be used to perform a * scrolling query, meaning that only partial results will be returned, but the scrolling can be continued. @@ -566,6 +554,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { * this will be used as the scrolling window size. * @param scrollTimeValidity the time the scrolling query should stay valid. This must contain a time unit value such as the ones supported by ElasticSearch, such as * the ones declared here : https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units + * * @return a {@link PartialList} of items matching the specified criteria, with an scroll identifier and the scroll validity used if a scroll query was requested. */ PartialList queryCustomItem(Condition query, String sortBy, String customItemType, int offset, int size, String scrollTimeValidity); @@ -577,13 +566,14 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { * @param scrollIdentifier a scroll identifier obtained by the execution of a first query and returned in the {@link PartialList} object * @param scrollTimeValidity a scroll time validity value for the scroll query to stay valid. This must contain a time unit value such as the ones supported by ElasticSearch, such as * the ones declared here : https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units + * * @return a {@link PartialList} of items matching the specified criteria, with an scroll identifier and the scroll validity used if a scroll query was requested. Note that if * there are no more results the list will be empty but not null. */ PartialList continueCustomItemScrollQuery(String customItemType, String scrollIdentifier, String scrollTimeValidity); /** - * Retrieves the same items as {@code query(query, sortBy, clazz, 0, -1)} with the added constraints that the matching elements must also have at least a field matching the + * Queries items that satisfy the condition and a full-text filter. * specified full text query. * * @param the type of the Item subclass we want to retrieve @@ -601,7 +591,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { PartialList queryFullText(String fulltext, Condition query, String sortBy, Class clazz, int offset, int size); /** - * Retrieves the number of items of the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and matching the specified {@link Condition}. + * Counts items of the given type that match the condition. * * @param query the condition the items must satisfy * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field @@ -611,7 +601,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { long queryCount(Condition query, String itemType); /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE}. + * Counts all items of the given type. * * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field * @return the number of items of the specified type @@ -620,7 +610,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { long getAllItemsCount(String itemType); /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} for the given tenant. + * Counts all items of the given type for the tenant. * * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field * @param tenantId the ID of the tenant whose items should be counted @@ -630,7 +620,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { long getAllItemsCount(String itemType, String tenantId); /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} matching the optional specified condition and + * Aggregates item counts for the given type, optionally filtered by condition and * aggregated according to the specified {@link BaseAggregate}. * Also return the global count of document matching the {@code ITEM_TYPE} * @@ -644,7 +634,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { Map aggregateQuery(Condition filter, BaseAggregate aggregate, String itemType); /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} matching the optional specified condition and + * Aggregates item counts for the given type, optionally filtered by condition and * aggregated according to the specified {@link BaseAggregate}. * This aggregate won't return the global count and should therefore be much faster than {@link #aggregateQuery(Condition, BaseAggregate, String)} * @@ -656,7 +646,7 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { Map aggregateWithOptimizedQuery(Condition filter, BaseAggregate aggregate, String itemType); /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} matching the optional specified condition and + * Aggregates item counts for the given type, optionally filtered by condition and * aggregated according to the specified {@link BaseAggregate}. * * @param filter the condition the items must match or {@code null} if no filtering is needed @@ -668,15 +658,15 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { Map aggregateWithOptimizedQuery(Condition filter, BaseAggregate aggregate, String itemType, int size); /** - * Updates the persistence's engine indices if needed. + * Refreshes persistence engine indices when required by the backend. */ void refresh(); /** - * Updates the persistence's engine specific index. + * Refreshes the index for the item type represented by the given class. * - * @param clazz will use an index by class type - * @param a class that extends Item + * @param an {@link Item} subclass + * @param clazz the item class whose index should be refreshed */ default void refreshIndex(Class clazz) { refreshIndex(clazz, null); @@ -689,7 +679,7 @@ default void refreshIndex(Class clazz) { void refreshIndex(Class clazz, Date dateHint); /** - * deprecated: (use: purgeTimeBasedItems instead) + * @deprecated use {@link #purgeTimeBasedItems(int, Class)} instead */ @Deprecated void purge(Date date); @@ -705,7 +695,7 @@ default void refreshIndex(Class clazz) { void purgeTimeBasedItems(int existsNumberOfDays, Class clazz); /** - * Retrieves all items of the specified Item subclass which specified ranged property is within the specified bounds, ordered according to the specified {@code sortBy} String + * Queries items whose ranged property falls within the given bounds, with paging and optional sorting. * and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. *

* Both bounds are inclusive: items whose property value equals {@code from} or {@code to} are included in the results. Either bound may be {@code null} to leave that side @@ -726,7 +716,7 @@ default void refreshIndex(Class clazz) { PartialList rangeQuery(String fieldName, String from, String to, String sortBy, Class clazz, int offset, int size); /** - * Retrieves the specified metrics for the specified field of items of the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and matching the + * Computes numeric metrics for a field on items that match the condition and type. * specified {@link Condition}. * * @param condition the condition the items must satisfy @@ -741,8 +731,7 @@ default void refreshIndex(Class clazz) { /** * Creates an index with for the specified item type in the persistence engine. *

- * TODO: remove from API? - * + * These low-level index operations may be removed from the public API in a future release. * * @param itemType the item type * @return {@code true} if the operation was successful, {@code false} otherwise */ @@ -751,8 +740,7 @@ default void refreshIndex(Class clazz) { /** * Removes the index for the specified item type. *

- * TODO: remove from API? - * + * These low-level index operations may be removed from the public API in a future release. * * @param itemType the item type * @return {@code true} if the operation was successful, {@code false} otherwise */ @@ -774,7 +762,7 @@ default void refreshIndex(Class clazz) { long calculateStorageSize(String tenantId); /** - * Retrieves the number of API calls made by a specific tenant. + * Returns the number of API calls recorded for the tenant. * * @param tenantId the ID of the tenant * @return the number of API calls 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 54dda258d..1c0235b70 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 @@ -139,6 +139,7 @@ public static Condition getContextualCondition(Condition condition, Map ipAddresses; private final Set allowedEvents; - + public ProviderConfig(String key, Set ipAddresses, Set allowedEvents) { this.key = key; this.ipAddresses = ipAddresses; this.allowedEvents = allowedEvents; } - + public String getKey() { return key; } public Set getIpAddresses() { return ipAddresses; } public Set getAllowedEvents() { return allowedEvents; } } - + private volatile Map providers = new HashMap<>(); - + /** * Activates the service and loads third-party provider configuration. * @@ -92,7 +90,7 @@ public ProviderConfig(String key, Set ipAddresses, Set allowedEv public void activate(Map properties) { modified(properties); } - + /** * Reloads third-party provider configuration. * @@ -132,7 +130,7 @@ public void modified(Map properties) { } } } - + if (newProviders.isEmpty()) { // The fallback key below is the well-known Unomi V2 default key, publicly documented // in Apache Unomi changelogs and issue trackers. It provides no confidentiality on its own @@ -147,36 +145,36 @@ public void modified(Map properties) { new HashSet<>(Arrays.asList("login", "updateProperties")) )); } - + this.providers = newProviders; - + int totalEvents = newProviders.values().stream() .mapToInt(config -> config.getAllowedEvents().size()) .sum(); - - LOGGER.info("V2 Third-Party Configuration updated - {} providers with {} total protected events", + + LOGGER.info("V2 Third-Party Configuration updated - {} providers with {} total protected events", newProviders.size(), totalEvents); } - + /** - * Check if an event type is protected (requires third-party authentication). - * + * Returns whether the event type requires third-party authentication. + * * @param eventType the event type to check - * @return true if the event type is protected, false otherwise + * @return {@code true} when the event type is protected */ public boolean isProtectedEventType(String eventType) { if (StringUtils.isBlank(eventType)) { return false; } - + return providers.values().stream() .anyMatch(config -> config.getAllowedEvents().contains(eventType)); } - + /** - * Get all protected event types from all providers. - * - * @return set of all protected event types + * Returns all protected event types declared by configured providers. + * + * @return an unmodifiable set of protected event type names */ public Set getAllProtectedEventTypes() { Set allProtectedEvents = new HashSet<>(); @@ -185,22 +183,20 @@ public Set getAllProtectedEventTypes() { } return Collections.unmodifiableSet(allProtectedEvents); } - /** - * Validate a third-party provider by key for a given event type. - * This method is used when the X-Unomi-Peer header contains the provider key. - * - * @param providerKey the third-party provider key (from X-Unomi-Peer header) + * Validates a provider key from the {@code X-Unomi-Peer} header for an event and source IP. + * + * @param providerKey the third-party provider key from the request header * @param eventType the event type to validate * @param sourceIP the source IP address - * @return true if the provider is authorized for this event type and IP, false otherwise + * @return {@code true} when the provider is authorized for the event and IP */ public boolean validateProviderByKey(String providerKey, String eventType, String sourceIP) { if (StringUtils.isBlank(providerKey) || StringUtils.isBlank(eventType) || StringUtils.isBlank(sourceIP)) { return false; } - + // Find the provider that has the matching key ProviderConfig config = null; String foundProviderId = null; @@ -211,7 +207,7 @@ public boolean validateProviderByKey(String providerKey, String eventType, Strin break; } } - + if (config == null) { LOGGER.debug("V2 compatibility mode: Unknown provider key: {}", SecurityUtils.maskSecret(providerKey)); return false; @@ -226,36 +222,36 @@ public boolean validateProviderByKey(String providerKey, String eventType, Strin if (!ipAuthorized) { LOGGER.debug("V2 compatibility mode: IP {} not authorized for provider {} (key: {})", sourceIP, foundProviderId, SecurityUtils.maskSecret(providerKey)); } - + return ipAuthorized; } - + /** - * Get the key for a third-party provider. - * + * Returns the authentication key for the given provider ID. + * * @param providerId the third-party provider ID - * @return the provider key, or null if not found + * @return the provider key, or {@code null} when the provider is unknown */ public String getProviderKey(String providerId) { ProviderConfig config = providers.get(providerId); return config != null ? config.getKey() : null; } - + /** - * Check if a provider ID is valid. - * + * Returns whether the provider ID is configured. + * * @param providerId the third-party provider ID - * @return true if the provider ID is valid, false otherwise + * @return {@code true} when the provider ID is known */ public boolean isValidProvider(String providerId) { return providers.containsKey(providerId); } - + private Set parseCommaSeparatedList(String value) { if (StringUtils.isBlank(value)) { return new HashSet<>(); } - + Set result = new HashSet<>(); String[] parts = value.split(","); for (String part : parts) { @@ -266,6 +262,6 @@ private Set parseCommaSeparatedList(String value) { } return result; } - + } diff --git a/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java b/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java index 8ae679a53..2d3bf5fa1 100644 --- a/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java +++ b/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java @@ -32,7 +32,8 @@ import java.util.regex.Pattern; /** - * Default implementation for the unomi authentication on Rest endpoints + * Default OSGi-backed implementation of {@link RestAuthenticationConfig} for REST endpoint + * authentication, role mappings, and V2 compatibility mode settings. */ @Component(service = { RestAuthenticationConfig.class}, configurationPid = "org.apache.unomi.rest.authentication", immediate = true) @Designate(ocd = DefaultRestAuthenticationConfig.Config.class) @@ -78,7 +79,6 @@ public class DefaultRestAuthenticationConfig implements RestAuthenticationConfig private volatile boolean v2CompatibilityModeEnabled = false; private volatile String v2CompatibilityDefaultTenantId = "default"; - /** * Updates authentication settings from OSGi configuration. * diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java index 933f6e870..b40061e20 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java @@ -70,9 +70,9 @@ public void setGoalsService(GoalsService goalsService) { } /** - * Retrieves the set of Metadata associated with existing campaigns. + * Returns metadata for all campaigns. * - * @return the set of Metadata associated with existing campaigns + * @return campaign metadata for every stored campaign */ @GET @Path("/") @@ -92,10 +92,10 @@ public void setCampaignDefinition(Campaign campaign) { } /** - * Retrieves the set of Metadata associated with existing campaign matching the specified {@link Query} + * Returns campaign metadata matching the given query. * - * @param query the Query used to filter the campagins which metadata we want to retrieve - * @return the set of Metadata associated with existing campaigns matching the specified {@link Query} + * @param query the query used to filter campaigns + * @return metadata for campaigns that match the query */ @POST @Path("/query") @@ -104,10 +104,10 @@ public Set getCampaignMetadatas(Query query) { } /** - * Retrieves campaign details for campaigns matching the specified query. + * Returns detailed campaign data matching the given query. * - * @param query the query specifying which campaigns to retrieve - * @return a {@link PartialList} of campaign details for the campaigns matching the specified query + * @param query the query specifying which campaigns to include + * @return a paged list of campaign details */ @POST @Path("/query/detailed") @@ -116,10 +116,10 @@ public PartialList getCampaignDetails(Query query) { } /** - * Retrieves the {@link CampaignDetail} associated with the campaign identified with the specified identifier + * Returns detailed data for the campaign with the given ID. * - * @param campaignID the identifier of the campaign for which we want to retrieve the details - * @return the CampaignDetail for the campaign identified by the specified identifier or {@code null} if no such campaign exists + * @param campaignID the campaign identifier + * @return the campaign detail, or {@code null} when no such campaign exists */ @GET @Path("/{campaignID}/detailed") @@ -128,10 +128,10 @@ public CampaignDetail getCampaignDetail(@PathParam("campaignID") String campaign } /** - * Retrieves the campaign identified by the specified identifier + * Returns the campaign definition for the given ID. * - * @param campaignID the identifier of the campaign we want to retrieve - * @return the campaign associated with the specified identifier or {@code null} if no such campaign exists + * @param campaignID the campaign identifier + * @return the campaign, or {@code null} when no such campaign exists */ @GET @Path("/{campaignID}") @@ -173,10 +173,10 @@ public void removeCampaignEventDefinition(@PathParam("eventId") String campaignE } /** - * Retrieves {@link CampaignEvent}s matching the specified query. + * Returns campaign events matching the given query. * - * @param query the Query specifying which CampaignEvents to retrieve - * @return a {@link PartialList} of campaign events matching the specified query + * @param query the query specifying which campaign events to include + * @return a paged list of matching campaign events */ @POST @Path("/events/query") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java index 0c61551b5..23fdc3232 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java @@ -34,7 +34,7 @@ import java.util.List; /** - * A JAX-RS endpoint to access information about the context server's cluster. + * JAX-RS endpoint for cluster node visibility and data purge operations. */ @Produces(MediaType.APPLICATION_JSON) @CrossOriginResourceSharing( @@ -78,9 +78,9 @@ public void setMessageContext(MessageContext messageContext) { } /** - * Retrieves the list of available nodes for this context server instance. + * Returns cluster nodes known to this server instance. * - * @return a list of {@link ClusterNode} + * @return the available cluster nodes */ @GET @Path("/") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java index bbc8be05a..97b9a1697 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java @@ -36,7 +36,7 @@ import java.util.*; /** - * A JAX-RS endpoint to retrieve definition information about core context server entities such as conditions, actions and values. + * JAX-RS endpoint for condition, action, and value type definitions used by the rules engine. */ @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8") @CrossOriginResourceSharing( @@ -71,10 +71,10 @@ public void setLocalizationHelper(LocalizationHelper localizationHelper) { } /** - * Retrieves all condition types localized using the specified language. + * Returns all condition types localized for the requested language. * - * @param language the language to use to localize. - * @return a Collection of all collection types + * @param language the locale to use for labels and descriptions + * @return all condition types in REST form */ @GET @Path("/conditions") @@ -84,11 +84,11 @@ public Collection getAllConditionTypes(@HeaderParam("Accept-L } /** - * Retrieves the set of condition types with the specified tags. + * Returns condition types that match any of the given tags. * - * @param language the language to use to localize. - * @param tags a comma-separated list of tag identifiers - * @return the set of condition types with the specified tag + * @param language the locale to use for labels and descriptions + * @param tags a comma-separated list of tag identifiers + * @return matching condition types in REST form */ @GET @Path("/conditions/tags/{tags}") @@ -102,11 +102,11 @@ public Collection getConditionTypesByTag(@PathParam("tags") S } /** - * Retrieves the set of condition types with the specified system tags. + * Returns condition types that match any of the given system tags. * - * @param language the language to use to localize. - * @param tags a comma-separated list of tag identifiers - * @return the set of condition types with the specified tag + * @param language the locale to use for labels and descriptions + * @param tags a comma-separated list of system tag identifiers + * @return matching condition types in REST form */ @GET @Path("/conditions/systemTags/{tags}") @@ -120,11 +120,11 @@ public Collection getConditionTypesBySystemTag(@PathParam("ta } /** - * Retrieves the condition type associated with the specified identifier localized using the specified language. + * Returns the condition type with the given ID, localized for the requested language. * - * @param language the language to use to localize. - * @param id the identifier of the condition type to retrieve - * @return the condition type associated with the specified identifier or {@code null} if no such condition type exists + * @param language the locale to use for labels and descriptions + * @param id the condition type identifier + * @return the condition type in REST form, or {@code null} when it does not exist */ @GET @Path("/conditions/{conditionId}") @@ -134,7 +134,7 @@ public RESTConditionType getConditionType(@PathParam("conditionId") String id, @ } /** - * Stores the condition type + * Stores the given condition type definition. * * @param conditionType the condition type to store */ @@ -145,9 +145,9 @@ public void setConditionType(ConditionType conditionType) { } /** - * Removes the condition type + * Deletes the condition type with the given ID. * - * @param conditionTypeId the identifier of the action type to delete + * @param conditionTypeId the condition type identifier */ @DELETE @Path("/conditions/{conditionTypeId}") @@ -156,10 +156,10 @@ public void removeConditionType(@PathParam("conditionTypeId") String conditionTy } /** - * Retrieves all known action types localized using the specified language. + * Returns all action types localized for the requested language. * - * @param language the language to use to localize. - * @return all known action types + * @param language the locale to use for labels and descriptions + * @return all action types in REST form */ @GET @Path("/actions") @@ -169,11 +169,11 @@ public Collection getAllActionTypes(@HeaderParam("Accept-Languag } /** - * Retrieves the set of action types with the specified tags. + * Returns action types that match any of the given tags. * - * @param language the language to use to localize. - * @param tags the tag marking the action types we want to retrieve - * @return the set of action types with the specified tag + * @param language the locale to use for labels and descriptions + * @param tags a comma-separated list of tag identifiers + * @return matching action types in REST form */ @GET @Path("/actions/tags/{tags}") @@ -187,11 +187,11 @@ public Collection getActionTypeByTag(@PathParam("tags") String t } /** - * Retrieves the set of action types with the specified system tags. + * Returns action types that match any of the given system tags. * - * @param language the language to use to localize. - * @param tags the tag marking the action types we want to retrieve - * @return the set of action types with the specified tag + * @param language the locale to use for labels and descriptions + * @param tags a comma-separated list of system tag identifiers + * @return matching action types in REST form */ @GET @Path("/actions/systemTags/{tags}") @@ -205,11 +205,11 @@ public Collection getActionTypeBySystemTag(@PathParam("tags") St } /** - * Retrieves the action type associated with the specified identifier localized using the specified language. + * Returns the action type with the given ID, localized for the requested language. * - * @param language the language to use to localize. - * @param id the identifier of the action type to retrieve - * @return the action type associated with the specified identifier or {@code null} if no such action type exists + * @param language the locale to use for labels and descriptions + * @param id the action type identifier + * @return the action type in REST form, or {@code null} when it does not exist */ @GET @Path("/actions/{actionId}") @@ -219,7 +219,7 @@ public RESTActionType getActionType(@PathParam("actionId") String id, @HeaderPar } /** - * Stores the action type + * Stores the given action type definition. * * @param actionType the action type to store */ @@ -230,9 +230,9 @@ public void setActionType(ActionType actionType) { } /** - * Removes the action type + * Deletes the action type with the given ID. * - * @param actionTypeId the identifier of the action type to delete + * @param actionTypeId the action type identifier */ @DELETE @Path("/actions/{actionTypeId}") @@ -241,10 +241,10 @@ public void removeActionType(@PathParam("actionTypeId") String actionTypeId) { } /** - * Retrieves all known value types localized using the specified language. + * Returns all value types localized for the requested language. * - * @param language the language to use to localize. - * @return all known value types + * @param language the locale to use for labels and descriptions + * @return all value types in REST form */ @GET @Path("/values") @@ -253,11 +253,11 @@ public Collection getAllValueTypes(@HeaderParam("Accept-Language" } /** - * Retrieves the set of value types with the specified tags. + * Returns value types that match any of the given tags. * - * @param language the language to use to localize. - * @param tags the tag marking the value types we want to retrieve - * @return the set of value types with the specified tag + * @param language the locale to use for labels and descriptions + * @param tags a comma-separated list of tag identifiers + * @return matching value types in REST form */ @GET @Path("/values/tags/{tags}") @@ -271,11 +271,11 @@ public Collection getValueTypeByTag(@PathParam("tags") String tag } /** - * Retrieves the value type associated with the specified identifier localized using the specified language. + * Returns the value type with the given ID, localized for the requested language. * - * @param language the language to use to localize. - * @param id the identifier of the value type to retrieve - * @return the value type associated with the specified identifier or {@code null} if no such value type exists + * @param language the locale to use for labels and descriptions + * @param id the value type identifier + * @return the value type in REST form, or {@code null} when it does not exist */ @GET @Path("/values/{valueTypeId}") @@ -285,9 +285,9 @@ public RESTValueType getValueType(@PathParam("valueTypeId") String id, @HeaderPa } /** - * Retrieves a Map of plugin identifier to a list of plugin types defined by that particular plugin. + * Returns plugin types grouped by plugin identifier. * - * @return a Map of plugin identifier to a list of plugin types defined by that particular plugin + * @return plugin ID to plugin type list mappings */ @GET @Path("/typesByPlugin") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java index 5f6463294..c36ff131a 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java @@ -29,7 +29,7 @@ import java.util.Set; /** - * A JAX-RS endpoint to access information about the context server's events. + * JAX-RS endpoint for searching, loading, and deleting stored events. */ @Produces(MediaType.APPLICATION_JSON) @CrossOriginResourceSharing( @@ -53,11 +53,10 @@ public void setEventService(EventService eventService) { } /** - * Allows to search events using a query. + * Searches events using the given query. * - * @param query the query object to use to search for events. You can specify offset and limits along with a - * condition tree. - * @return a partial list containing the events that match the query. + * @param query the search query, including optional condition tree, offset, and limit + * @return a paged list of matching events */ @POST @Path("/search") @@ -66,10 +65,10 @@ public PartialList searchEvents(Query query) { } /** - * Allows to retrieve event by id. + * Returns the event with the given ID. * - * @param id the event id. - * @return {@link Event} with the provided id. + * @param id the event identifier + * @return the event, or {@code null} when it does not exist */ @GET @Path("/{id}") @@ -89,8 +88,9 @@ public void deleteEvent(@PathParam("id") final String id) { } /** - * Retrieves the list of event types identifiers that the server has processed. - * @return a Set of strings that contain event type identifiers. + * Returns event type identifiers known to the server. + * + * @return the processed event type identifiers */ @GET @Path("types") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java index 38b3885f3..3c750fd69 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java @@ -58,9 +58,9 @@ public void setGoalsService(GoalsService goalsService) { } /** - * Retrieves the set of Metadata associated with existing goals. + * Returns metadata for all goals. * - * @return the set of Metadata associated with existing goals + * @return goal metadata for every stored goal */ @GET @Path("/") @@ -68,7 +68,6 @@ public Set getGoalMetadatas() { return goalsService.getGoalMetadatas(); } - /** * Saves the specified goal in the context server and creates associated {@link Rule}s if the goal is enabled. * @@ -81,10 +80,10 @@ public void setGoal(Goal goal) { } /** - * Retrieves the set of Metadata associated with existing goals matching the specified {@link Query} + * Returns goal metadata matching the given query. * - * @param query the Query used to filter the Goals which metadata we want to retrieve - * @return the set of Metadata associated with existing goals matching the specified {@link Query} + * @param query the query used to filter goals + * @return metadata for goals that match the query */ @POST @Path("/query") @@ -93,10 +92,10 @@ public Set getGoalMetadatas(Query query) { } /** - * Retrieves the goal associated with the specified identifier. + * Returns the goal with the given ID. * - * @param goalId the identifier of the goal to retrieve - * @return the goal associated with the specified identifier or {@code null} if no such goal exists + * @param goalId the goal identifier + * @return the goal, or {@code null} when it does not exist */ @GET @Path("/{goalId}") @@ -116,10 +115,10 @@ public void removeGoal(@PathParam("goalId") String goalId) { } /** - * Retrieves the report for the goal identified with the specified identifier. + * Returns the performance report for the goal with the given ID. * - * @param goalId the identifier of the goal which report we want to retrieve - * @return the report for the specified goal + * @param goalId the goal identifier + * @return the goal report */ @GET @Path("/{goalID}/report") @@ -128,11 +127,11 @@ public GoalReport getGoalReport(@PathParam("goalID") String goalId) { } /** - * Retrieves the report for the goal identified with the specified identifier, considering only elements determined by the specified {@link AggregateQuery}. + * Returns a filtered goal report for the given goal and aggregate query. * - * @param goalId the identifier of the goal which report we want to retrieve - * @param query an {@link AggregateQuery} to further specify which elements of the report we want - * @return the report for the specified goal and query + * @param goalId the goal identifier + * @param query the aggregate query that limits report elements + * @return the filtered goal report */ @POST @Path("/{goalID}/report") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java index 42332654e..19fe51fb4 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java @@ -53,6 +53,7 @@ public void setPatchService(PatchService patchService) { /** * Apply a patch on an item + * * @param patch the patch to apply * @param force a boolean to force (or not) the application of the patch even if it was previously applied. */ diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java index dc16f2c88..8c536b234 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java @@ -113,9 +113,9 @@ public void setLocalizationHelper(LocalizationHelper localizationHelper) { } /** - * Retrieves the number of unique profiles. + * Returns the total number of unique profiles. * - * @return the number of unique profiles. + * @return the profile count */ @GET @Path("/count") @@ -124,10 +124,10 @@ public long getAllProfilesCount() { } /** - * Retrieves profiles matching the specified query. + * Returns profiles matching the given query. * - * @param query a {@link Query} specifying which elements to retrieve - * @return a {@link PartialList} of profiles instances matching the specified query + * @param query the search query + * @return a paged list of matching profiles */ @POST @Path("/search") @@ -136,10 +136,10 @@ public PartialList getProfiles(Query query) { } /** - * Retrieves an export of profiles matching the specified query as a downloadable file using the comma-separated values (CSV) format. + * Exports matching profiles as a downloadable CSV file. * - * @param query a String JSON representation of the query the profiles to export should match - * @return a Response object configured to allow caller to download the CSV export file + * @param query JSON query string describing which profiles to export + * @return a CSV download response */ @GET @Path("/export") @@ -173,10 +173,10 @@ public Response formExportProfiles(@FormParam("query") String query) { } /** - * Retrieves an export of profiles matching the specified query as a downloadable file using the comma-separated values (CSV) format. + * Exports matching profiles as a downloadable CSV file. * - * @param query a String JSON representation of the query the profiles to export should match - * @return a Response object configured to allow caller to download the CSV export file + * @param query the query describing which profiles to export + * @return a CSV download response */ @POST @Path("/export") @@ -190,7 +190,7 @@ public Response exportProfiles(Query query) { } /** - * Update all profiles in batch according to the specified {@link BatchUpdate} + * Applies a batch update to all matching profiles. * * @param update the batch update specification */ @@ -201,10 +201,10 @@ public void batchProfilesUpdate(BatchUpdate update) { } /** - * Retrieves the profile identified by the specified identifier. + * Returns the profile with the given ID. * - * @param profileId the identifier of the profile to retrieve - * @return the profile identified by the specified identifier or {@code null} if no such profile exists + * @param profileId the profile identifier + * @return the profile, or {@code null} when it does not exist */ @GET @Path("/{profileId}") @@ -246,20 +246,16 @@ public void delete(@PathParam("profileId") String profileId, @QueryParam("person } /** - * Retrieves the sessions associated with the profile identified by the specified identifier that match the specified query (if specified), ordered according to the specified - * {@code sortBy} String and and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. - * - * TODO: use a Query object instead of distinct parameter? + * Returns sessions for the profile with the given ID. + *

+ * Results can be filtered with an optional full-text query and paged with offset, size, and sort parameters. * - * @param profileId the identifier of the profile we want to retrieve sessions from - * @param query a String of text used for fulltext filtering which sessions we are interested in or {@code null} (or an empty String) if we want to retrieve all sessions - * @param offset zero or a positive integer specifying the position of the first session in the total ordered collection of matching sessions - * @param size a positive integer specifying how many matching sessions should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of matching sessions + * @param profileId the profile identifier + * @param query optional full-text filter, or {@code null} for all sessions + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return a paged list of matching sessions */ @GET @Path("/{profileId}/sessions") @@ -272,10 +268,10 @@ public PartialList getProfileSessions(@PathParam("profileId") String pr } /** - * Retrieves the list of segment metadata for the segments the specified profile is a member of. + * Returns segment metadata for segments that contain the given profile. * - * @param profileId the identifier of the profile for which we want to retrieve the segment metadata - * @return the (possibly empty) list of segment metadata for the segments the specified profile is a member of + * @param profileId the profile identifier + * @return segment metadata for memberships of this profile */ @GET @Path("/{profileId}/segments") @@ -285,10 +281,10 @@ public List getProfileSegments(@PathParam("profileId") String profileI } /** - * TODO + * Returns the property-type mapping for the given source property type ID. * - * @param fromPropertyTypeId fromPropertyTypeId - * @return property type mapping + * @param fromPropertyTypeId the source property type identifier + * @return the mapped target property type identifier */ @GET @Path("/properties/mappings/{fromPropertyTypeId}") @@ -297,10 +293,10 @@ public String getPropertyTypeMapping(@PathParam("fromPropertyTypeId") String fro } /** - * Retrieves {@link Persona} matching the specified query. + * Returns personas matching the given query. * - * @param query a {@link Query} specifying which elements to retrieve - * @return a {@link PartialList} of Persona instances matching the specified query + * @param query the search query + * @return a paged list of matching personas */ @POST @Path("/personas/search") @@ -309,10 +305,10 @@ public PartialList getPersonas(Query query) { } /** - * Retrieves the {@link Persona} identified by the specified identifier. + * Returns the persona with the given ID. * - * @param personaId the identifier of the persona to retrieve - * @return the persona identified by the specified identifier or {@code null} if no such persona exists + * @param personaId the persona identifier + * @return the persona, or {@code null} when it does not exist */ @GET @Path("/personas/{personaId}") @@ -321,10 +317,10 @@ public Persona loadPersona(@PathParam("personaId") String personaId) { } /** - * Retrieves the persona identified by the specified identifier and all its associated sessions + * Returns the persona with the given ID and all associated sessions. * - * @param personaId the identifier of the persona to retrieve - * @return a {@link PersonaWithSessions} instance with the persona identified by the specified identifier and all its associated sessions + * @param personaId the persona identifier + * @return the persona and its sessions */ @GET @Path("/personasWithSessions/{personaId}") @@ -333,10 +329,10 @@ public PersonaWithSessions loadPersonaWithSessions(@PathParam("personaId") Strin } /** - * Save the posted persona with its sessions + * Saves the posted persona together with its sessions. * - * @param personaWithSessions the persona to save with its sessions. - * @return a {@link PersonaWithSessions} instance with the persona identified by the specified identifier and all its associated sessions + * @param personaWithSessions the persona and sessions to persist + * @return the saved persona and sessions */ @POST @Path("/personasWithSessions") @@ -382,17 +378,13 @@ public Persona createPersona(@PathParam("personaId") String personaId) { } /** - * Retrieves the sessions associated with the persona identified by the specified identifier, ordered according to the specified {@code sortBy} String and and paged: only - * {@code size} of them are retrieved, starting with the {@code offset}-th one. + * Returns sessions for the persona with the given ID. * - * @param personaId the persona id - * @param offset zero or a positive integer specifying the position of the first session in the total ordered collection of matching sessions - * @param size a positive integer specifying how many matching sessions should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of sessions for the persona identified by the specified identifier + * @param personaId the persona identifier + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return a paged list of persona sessions */ @GET @Path("/personas/{personaId}/sessions") @@ -404,11 +396,11 @@ public PartialList getPersonaSessions(@PathParam("personaId") St } /** - * Retrieves the session identified by the specified identifier. + * Returns the session with the given ID. * - * @param sessionId the identifier of the session to be retrieved - * @return the session identified by the specified identifier - * @throws ParseException if the date hint cannot be parsed as a proper {@link Date} object + * @param sessionId the session identifier + * @return the session + * @throws ParseException if a stored date hint cannot be parsed */ @GET @Path("/sessions/{sessionId}") @@ -429,9 +421,9 @@ public Session saveSession(Session session) { } /** - * Delete the session by specifying its unique identifier. + * Deletes the session with the given ID. * - * @param sessionId the session identifier for the session to delete + * @param sessionId the session identifier */ @DELETE @Path("/sessions/{sessionId}") @@ -440,20 +432,17 @@ public void deleteSession(@PathParam("sessionId") String sessionId) { } /** - * Retrieves {@link Event}s for the {@link Session} identified by the provided session identifier, matching any of the provided event types, - * ordered according to the specified {@code sortBy} String and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. - * If a {@code query} is provided, a full text search is performed on the matching events to further filter them. + * Returns events for the session with the given ID. + *

+ * Results can be filtered by event type, optional full-text query, and paging parameters. * - * @param sessionId the identifier of the user session we're considering - * @param eventTypes an array of event type names; the events to retrieve should at least match one of these - * @param query a String to perform full text filtering on events matching the other conditions - * @param offset zero or a positive integer specifying the position of the first event in the total ordered collection of matching events - * @param size a positive integer specifying how many matching events should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in - * the String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of matching events + * @param sessionId the session identifier + * @param eventTypes event types to include; an event must match at least one + * @param query optional full-text filter + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return a paged list of matching events */ @GET @Path("/sessions/{sessionId}/events") @@ -489,17 +478,16 @@ public boolean matchCondition(Condition condition, Profile profile, Session sess } /** - * Retrieves the existing property types for the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and with the specified tag or system tag. + * Returns property types already in use for the given item type and tag. * - * TODO: move to a different class - * - * @param tag the tag we're interested in - * @param isSystemTag if we should look in system tags instead of tags - * @param itemType the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param language the value of the {@code Accept-Language} header to specify in which locale the properties description should be returned TODO unused - * @param response the http response object - * @return all property types defined for the specified item type and with the specified tag - * @throws IOException if there was an error sending the response + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * @param tag the tag or system tag to match + * @param isSystemTag whether {@code tag} is a system tag + * @param itemType the item type name from the class {@code ITEM_TYPE} field + * @param language the requested locale for property descriptions (currently unused) + * @param response the HTTP response used to signal missing query parameters + * @return matching property types, or {@code null} when required parameters are missing + * @throws IOException if sending the error response fails */ @GET @Path("/existingProperties") @@ -518,12 +506,11 @@ public Collection getExistingProperties(@QueryParam("tag") String } /** - * Retrieves all known property types. - * - * TODO: move to a different class + * Returns all known property types grouped by target. * - * @param language the value of the {@code Accept-Language} header to specify in which locale the properties description should be returned TODO unused - * @return a Map associating targets as keys to related {@link PropertyType}s + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * @param language the requested locale for property descriptions (currently unused) + * @return target name to property type mappings */ @GET @Path("/properties") @@ -532,13 +519,12 @@ public Map> getPropertyTypes(@HeaderParam("Acce } /** - * Retrieves the property type associated with the specified property ID. + * Returns the property type for the given property ID. * - * TODO: move to a different class - * - * @param propertyId the property ID for which we want to retrieve the associated property type - * @param language the value of the {@code Accept-Language} header to specify in which locale the properties description should be returned TODO unused - * @return the property type associated with the specified ID + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * @param propertyId the property identifier + * @param language the requested locale for property descriptions (currently unused) + * @return the property type */ @GET @Path("/properties/{propertyId}") @@ -547,13 +533,12 @@ public PropertyType getPropertyType(@PathParam("propertyId") String propertyId, } /** - * Retrieves all the property types associated with the specified target. - * - * TODO: move to a different class + * Returns property types for the given target. * - * @param target the target for which we want to retrieve the associated property types - * @param language the value of the {@code Accept-Language} header to specify in which locale the properties description should be returned TODO unused - * @return a collection of all the property types associated with the specified target + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * @param target the property target name + * @param language the requested locale for property descriptions (currently unused) + * @return property types for the target */ @GET @Path("/properties/targets/{target}") @@ -562,14 +547,12 @@ public Collection getPropertyTypesByTarget(@PathParam("target") St } /** - * Retrieves all property types with the specified tags. + * Returns property types that match any of the given tags. * - * TODO: move to a different class - * TODO: passing a list of tags via a comma-separated list is not very RESTful - * - * @param tags a comma-separated list of tag identifiers - * @param language the value of the {@code Accept-Language} header to specify in which locale the properties description should be returned TODO unused - * @return a Set of the property types with the specified tag + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * Tags are passed as a comma-separated path segment for backward compatibility. * + * @param tags comma-separated tag identifiers + * @param language the requested locale for property descriptions (currently unused) + * @return matching property types */ @GET @Path("/properties/tags/{tags}") @@ -583,14 +566,12 @@ public Collection getPropertyTypeByTag(@PathParam("tags") String t } /** - * Retrieves all property types with the specified tags. - * - * TODO: move to a different class - * TODO: passing a list of tags via a comma-separated list is not very RESTful + * Returns property types that match any of the given system tags. * - * @param tags a comma-separated list of tag identifiers - * @param language the value of the {@code Accept-Language} header to specify in which locale the properties description should be returned TODO unused - * @return a Set of the property types with the specified tag + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * Tags are passed as a comma-separated path segment for backward compatibility. * + * @param tags comma-separated system tag identifiers + * @param language the requested locale for property descriptions (currently unused) + * @return matching property types */ @GET @Path("/properties/systemTags/{tags}") @@ -606,8 +587,7 @@ public Collection getPropertyTypeBySystemTag(@PathParam("tags") St /** * Persists the specified property type in the context server. * - * TODO: move to a different class - * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * * @param property the property type to persist * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed */ @@ -620,8 +600,7 @@ public boolean setPropertyType(PropertyType property) { /** * Persists the specified properties type in the context server. * - * TODO: move to a different class - * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * * @param properties the properties type to persist * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed */ @@ -638,8 +617,7 @@ public boolean setPropertyTypes(List properties) { /** * Deletes the property type identified by the specified identifier. * - * TODO: move to a different class - * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * * @param propertyId the identifier of the property type to delete * @return {@code true} if the property type was properly deleted, {@code false} otherwise */ @@ -650,10 +628,10 @@ public boolean deleteProperty(@PathParam("propertyId") String propertyId) { } /** - * Retrieves sessions matching the specified query. + * Returns sessions matching the given query. * - * @param query a {@link Query} specifying which elements to retrieve - * @return a {@link PartialList} of sessions matching the specified query + * @param query the search query + * @return a paged list of matching sessions */ @POST @Path("/search/sessions") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java index b019ccf45..d52844361 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java @@ -36,7 +36,7 @@ import java.util.Map; /** - * A JAX-RS endpoint to perform queries against context-server data. + * JAX-RS endpoint for aggregate counts, metrics, and conditional item counts. */ @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -74,12 +74,11 @@ public void setLocalizationHelper(LocalizationHelper localizationHelper) { } /** - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and aggregated by possible values of the specified - * property. + * Returns item counts grouped by distinct values of the given property. * - * @param type the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param property the property we're aggregating on, i.e. for each possible value of this property, we are counting how many items of the specified type have that value - * @return a Map associating a specific value of the property to the cardinality of items with that value + * @param type the item type name from the class {@code ITEM_TYPE} field + * @param property the property whose distinct values form aggregation buckets + * @return property value to item count mappings * @see Item Item for a discussion of {@code ITEM_TYPE} */ @GET @@ -89,18 +88,15 @@ public Map getAggregate(@PathParam("type") String type, @PathParam } /** - * TODO: rework, this method is confusing since it either behaves like {@link #getAggregate(String, String)} if query is null but completely differently if it isn't + * Returns property-value counts for an item type, optionally using an aggregate query. + *

+ * When {@code optimizedQuery} is {@code true}, the global document count is omitted for faster execution. * - * Retrieves the number of items with the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and aggregated by possible values of the specified - * property or, if the specified query is not {@code null}, perform that aggregate query. - * Also return the global count of document matching the {@code ITEM_TYPE} if you don't use {@code optimizedQuery} or set it to false, - * otherwise if {@code optimizedQuery} is set to true then it won't return the global count but the query will be executed much faster. - * - * @param type the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param property the property we're aggregating on, i.e. for each possible value of this property, we are counting how many items of the specified type have that value - * @param aggregateQuery the {@link AggregateQuery} specifying the aggregation that should be performed - * @param optimizedQuery the {@code optimizedQuery} specifying if we should optimized the aggregate query or not - * @return a Map associating a specific value of the property to the cardinality of items with that value + * @param type the item type name from the class {@code ITEM_TYPE} field + * @param property the property whose distinct values form aggregation buckets + * @param optimizedQuery whether to use the optimized aggregate path + * @param aggregateQuery optional aggregate query constraints + * @return property value to item count mappings * @see Item Item for a discussion of {@code ITEM_TYPE} */ @POST @@ -115,15 +111,13 @@ public Map getAggregate(@PathParam("type") String type, @PathParam } /** - * Retrieves the specified metrics for the specified field of items of the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and matching the - * specified {@link Condition}. + * Returns numeric metrics for a field on items that match the given condition. * - * @param condition the condition the items must satisfy - * @param metricsType a String specifying which metrics should be computed, separated by a slash ({@code /}) (possible values: {@code sum} for the sum of the - * values, {@code avg} for the average of the values, {@code min} for the minimum value and {@code max} for the maximum value) - * @param property the name of the field for which the metrics should be computed - * @param type the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @return a Map associating computed metric name as key to its associated value + * @param condition the condition matching items must satisfy + * @param metricsType slash-separated metric names ({@code sum}, {@code avg}, {@code min}, {@code max}) + * @param property the numeric field to aggregate + * @param type the item type name from the class {@code ITEM_TYPE} field + * @return metric name to computed value mappings * @see Item Item for a discussion of {@code ITEM_TYPE} */ @POST @@ -133,16 +127,13 @@ public Map getMetric(@PathParam("type") String type, @PathParam( } /** - * Retrieves the number of items of the specified type as defined by the Item subclass public field {@code ITEM_TYPE} and matching the specified {@link Condition}. + * Returns how many items of the given type match the condition. * - * @param condition the condition the items must satisfy - * @param validate optional parameter, in case of draft condition that have missing required parameters an IllegalArgumentException is throw - * and this end point will return status code 400, to avoid that you can set validate to false. - * @param type the String representation of the item type we want to retrieve the count of, as defined by its class' {@code ITEM_TYPE} field - * @param response the httpServletResponse - * @return the number of items of the specified type. - * 0 and status code 400 in case of IllegalArgumentException (bad condition) and validate null or true - * 0 and status code 200 in case of IllegalArgumentException (bad condition) and validate false + * @param condition the condition matching items must satisfy + * @param validate when {@code true} or omitted, invalid draft conditions return HTTP 400; when {@code false}, returns 0 with HTTP 200 + * @param type the item type name from the class {@code ITEM_TYPE} field + * @param response the HTTP response used to set status on validation failure + * @return the matching item count, or {@code 0} when validation fails * @see Item Item for a discussion of {@code ITEM_TYPE} */ @POST diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java index 4127aed55..a98f8afb4 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java @@ -69,9 +69,11 @@ public void setRulesService(RulesService rulesService) { } /** - * Retrieves the metadata for all known rules. - * Note that it only includes the rules in memory, not those persisted in storage. - * @return the Set of known metadata + * Returns metadata for all in-memory rules. + *

+ * Note that this includes only rules currently loaded in memory, not every rule in storage. + * + * @return known rule metadata */ @GET @Path("/") @@ -91,9 +93,9 @@ public void setRule(Rule rule) { } /** - * Retrieves the rule statistics for all known rules. + * Returns execution statistics for all known rules. * - * @return a map that contains the rule key as a key and as the value a @RuleStatistics object. + * @return rule ID to statistics mappings */ @GET @Path("/statistics") @@ -102,7 +104,7 @@ public Map getAllRuleStatistics() { } /** - * Deletes all the rule statistics, which basically resets them to 0. + * Resets execution statistics for all rules to zero. */ @DELETE @Path("/statistics") @@ -111,10 +113,10 @@ public void resetAllRuleStatistics() { } /** - * Retrieves rule metadatas for rules matching the specified {@link Query}. + * Returns rule metadata matching the given query. * - * @param query the query the rules which metadata we want to retrieve must match - * @return a {@link PartialList} of rules metadata for the rules matching the specified query + * @param query the query rules must match + * @return a paged list of matching rule metadata */ @POST @Path("/query") @@ -123,10 +125,10 @@ public PartialList getRuleMetadatas(Query query) { } /** - * Retrieves rule details for rules matching the specified query. + * Returns full rule definitions matching the given query. * - * @param query the query specifying which rules to retrieve - * @return a {@link PartialList} of rule details for the rules matching the specified query + * @param query the query specifying which rules to include + * @return a paged list of matching rules */ @POST @Path("/query/detailed") @@ -135,10 +137,10 @@ public PartialList getRuleDetails(Query query) { } /** - * Retrieves the rule identified by the specified identifier. + * Returns the rule with the given ID. * - * @param ruleId the identifier of the rule we want to retrieve - * @return the rule identified by the specified identifier or {@code null} if no such rule exists. + * @param ruleId the rule identifier + * @return the rule, or {@code null} when it does not exist */ @GET @Path("/{ruleId}") @@ -147,10 +149,10 @@ public Rule getRule( @PathParam("ruleId") String ruleId) { } /** - * Retrieves the statistics for the rule with the specified identifier + * Returns execution statistics for the rule with the given ID. * - * @param ruleId the identifier of the rule we want to retrieve - * @return the statistics for the specified rule or {@code null} if no such rule exists. + * @param ruleId the rule identifier + * @return the rule statistics, or {@code null} when the rule does not exist */ @GET @Path("/{ruleId}/statistics") @@ -170,8 +172,7 @@ public void removeRule(@PathParam("ruleId") String ruleId) { } /** - * TODO: remove - * + * Deprecated maintenance endpoint kept for backward compatibility. * * @deprecated As of version 1.1.0-incubating, not needed anymore */ @Deprecated diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java index e795690bc..cbee62b60 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java @@ -62,9 +62,9 @@ public void setScopeService(ScopeService scopeService) { } /** - * Retrieves all known scopes. + * Returns all configured scopes. * - * @return a List of the scopes + * @return all known scopes */ @GET @Path("/") @@ -85,10 +85,10 @@ public Response save(Scope scope) { } /** - * Retrieves the scope identified by the specified identifier. + * Returns the scope with the given ID. * - * @param scopeId the identifier of the scope we want to retrieve - * @return the scope identified by the specified identifier or {@code null} if no such scope exists. + * @param scopeId the scope identifier + * @return the scope, or {@code null} when it does not exist */ @GET @Path("/{scopeId}") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java index 5623b2bb3..91f145a52 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java @@ -35,7 +35,7 @@ import java.util.List; /** - * A JAX-RS endpoint to manage {@link Scoring}s + * JAX-RS endpoint for managing {@link Scoring} definitions and their dependents. */ @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -69,15 +69,12 @@ public void setSegmentService(SegmentService segmentService) { } /** - * Retrieves the set of all scoring metadata. - * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements - * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. + * Returns scoring metadata with paging and optional sorting. * - * @return the set of all scoring metadata + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return matching scoring metadata */ @GET @Path("/") @@ -86,10 +83,10 @@ public List getScoringMetadatas(@QueryParam("offset") @DefaultValue("0 } /** - * Retrieves the set of scoring metadata for scorings matching the specified query. + * Returns scoring metadata matching the given query. * - * @param query the query the scorings must match for their metadata to be retrieved - * @return the set of scoring metadata for scorings matching the specified query + * @param query the query scorings must match + * @return a paged list of matching scoring metadata */ @POST @Path("/query") @@ -98,10 +95,10 @@ public PartialList getScoringMetadatas(Query query) { } /** - * Retrieves the scoring identified by the specified identifier. + * Returns the scoring definition with the given ID. * - * @param scoringId the identifier of the scoring to be retrieved - * @return the scoring identified by the specified identifier or {@code null} if no such scoring exists + * @param scoringId the scoring identifier + * @return the scoring, or {@code null} when it does not exist */ @GET @Path("/{scoringID}") @@ -153,11 +150,12 @@ public DependentMetadata removeScoringDefinition(@PathParam("scoringID") String } /** - * Retrieves the list of Segment and Scoring metadata depending on the specified scoring. - * A segment or scoring is depending on a segment if it includes a scoringCondition with a test on this scoring. + * Returns segment and scoring metadata that depend on the given scoring. + *

+ * A dependent definition includes a scoring condition that references this scoring. * - * @param scoringId the segment identifier - * @return a list of Segment/Scoring Metadata depending on the specified scoring + * @param scoringId the scoring identifier + * @return metadata for dependent segments and scorings */ @GET @Path("/{scoringID}/impacted") @@ -166,8 +164,7 @@ public DependentMetadata getScoringDependentMetadata(@PathParam("scoringID") Str } /** - * TODO: remove - * + * Deprecated maintenance endpoint kept for backward compatibility. * * @deprecated As of version 1.1.0-incubating, not needed anymore */ @Deprecated diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java index c304b0f52..a42460608 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java @@ -69,17 +69,13 @@ public void setSegmentService(SegmentService segmentService) { } /** - * Retrieves a list of profiles matching the conditions defined by the segment identified by the specified identifier, ordered according to the specified {@code sortBy} - * String and and paged: only {@code size} of them are retrieved, starting with the {@code offset}-th one. + * Returns profiles that match the segment with the given ID. * - * @param segmentId the identifier of the segment for which we want to retrieve matching profiles - * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements - * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. - * @return a {@link PartialList} of profiles matching the specified segment + * @param segmentId the segment identifier + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return a paged list of matching profiles */ @GET @Path("/{segmentID}/match") @@ -88,10 +84,10 @@ public PartialList getMatchingIndividuals(@PathParam("segmentID") Strin } /** - * Retrieves the number of profiles matching the conditions defined by the segment identified by the specified identifier. + * Returns how many profiles match the segment with the given ID. * - * @param segmentId the identifier of the segment for which we want to retrieve matching profiles - * @return the number of profiles matching the conditions defined by the segment identified by the specified identifier + * @param segmentId the segment identifier + * @return the number of matching profiles */ @GET @Path("/{segmentID}/count") @@ -113,16 +109,12 @@ public Boolean isProfileInSegment(@PathParam("profile") Profile profile, @PathPa } /** - * Retrieves the 50 first segment metadatas. - * - * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements - * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. + * Returns segment metadata with paging and optional sorting. * - * @return a List of the 50 first segment metadata + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return matching segment metadata */ @GET @Path("/") @@ -131,11 +123,12 @@ public List getSegmentMetadatas(@QueryParam("offset") @DefaultValue("0 } /** - * Retrieves the list of Segment and Scoring metadata depending on the specified segment. - * A segment or scoring is depending on a segment if it includes a profileSegmentCondition with a test on this segment. + * Returns segment and scoring metadata that depend on the given segment. + *

+ * A dependent definition includes a profile-segment condition that references this segment. * * @param segmentId the segment identifier - * @return a list of Segment/Scoring Metadata depending on the specified segment + * @return metadata for dependent segments and scorings */ @GET @Path("/{segmentID}/impacted") @@ -155,10 +148,10 @@ public void setSegmentDefinition(Segment segment) { } /** - * Retrieves the metadata for segments matching the specified {@link Query}. + * Returns segment metadata matching the given query. * - * @param query the query that the segments must match for their metadata to be retrieved - * @return a {@link PartialList} of segment metadata + * @param query the query segments must match + * @return a paged list of matching segment metadata */ @POST @Path("/query") @@ -167,10 +160,10 @@ public PartialList getListMetadatas(Query query) { } /** - * Retrieves the segment identified by the specified identifier. + * Returns the segment definition with the given ID. * - * @param segmentId the identifier of the segment to be retrieved - * @return the segment identified by the specified identifier or {@code null} if no such segment exists + * @param segmentId the segment identifier + * @return the segment, or {@code null} when it does not exist */ @GET @Path("/{segmentID}") @@ -195,8 +188,7 @@ public DependentMetadata removeSegmentDefinition(@PathParam("segmentID") String } /** - * TODO: remove - * + * Deprecated maintenance endpoint kept for backward compatibility. * * @deprecated As of version 1.1.0-incubating, not needed anymore */ @Deprecated diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java index 81bf270be..2d86ec0ec 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java @@ -27,7 +27,9 @@ import javax.ws.rs.core.MediaType; /** - * @author Jerome Blanchard + * Simple health-style JAX-RS endpoint for manual connectivity checks. + * Returns plain text so load balancers or operators can verify the REST + * layer is reachable without exercising business services. */ @Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8") @Path("/test") diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java index 94626f98d..5d8e80ea8 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java @@ -17,10 +17,6 @@ package org.apache.unomi.rest.endpoints; -/** - * Created by amidani on 24/03/2017. - */ - import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing; import org.apache.unomi.api.Metadata; import org.apache.unomi.api.lists.UserList; @@ -35,7 +31,9 @@ import java.util.List; /** - * A JAX-RS endpoint to manage {@link UserList}s. + * JAX-RS endpoint for static {@link UserList} CRUD and membership management. + * Delegates to {@link UserListService} so marketers can maintain fixed audience + * lists used by campaigns and exports. */ @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -69,16 +67,12 @@ public void setUserListService(UserListService userListService) { } /** - * Retrieves the 50 first {@link UserList} metadatas. - * - * @param offset zero or a positive integer specifying the position of the first element in the total ordered collection of matching elements - * @param size a positive integer specifying how many matching elements should be retrieved or {@code -1} if all of them should be retrieved - * @param sortBy an optional ({@code null} if no sorting is required) String of comma ({@code ,}) separated property names on which ordering should be performed, ordering - * elements according to the property order in the - * String, considering each in turn and moving on to the next one in case of equality of all preceding ones. Each property name is optionally followed by - * a column ({@code :}) and an order specifier: {@code asc} or {@code desc}. + * Returns user list metadata with paging and optional sorting. * - * @return a List of the 50 first {@link UserList} metadata + * @param offset zero-based index of the first result + * @param size maximum number of results to return, or {@code -1} for all matches + * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc} + * @return matching user list metadata */ @GET @Path("/") diff --git a/rest/src/main/java/org/apache/unomi/rest/exception/InvalidRequestException.java b/rest/src/main/java/org/apache/unomi/rest/exception/InvalidRequestException.java index 1d477a016..7bf517fa9 100644 --- a/rest/src/main/java/org/apache/unomi/rest/exception/InvalidRequestException.java +++ b/rest/src/main/java/org/apache/unomi/rest/exception/InvalidRequestException.java @@ -28,6 +28,7 @@ public class InvalidRequestException extends RuntimeException { /** * Build an invalid request + * * @param message message in the logs. It contains detailed information * @param responseMessage message in the response, sent to the browser, must be vague as possible. */ diff --git a/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java b/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java index 8b00ed219..b442c17c1 100644 --- a/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java +++ b/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java @@ -21,9 +21,9 @@ import java.io.Serializable; /** - * Response model for the event collector endpoint. - * This class provides information about the result of event processing, including - * which entities were updated and tracing information. + * REST response returned after the events collector processes a batch. + * Reports bitwise change flags (session/profile updated, errors) and may include + * a {@link TraceNode} tree when request tracing is enabled for the call. */ public class EventCollectorResponse implements Serializable { /** diff --git a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java index 645c8beaf..cddc93b4e 100644 --- a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java +++ b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java @@ -19,7 +19,9 @@ import java.util.Map; /** - * Request body for tenant create and update operations. + * REST request payload for creating or updating a {@link org.apache.unomi.api.tenants.Tenant}. + * Carries the desired tenant id and an open properties map that maps to + * tenant configuration fields accepted by {@link org.apache.unomi.api.tenants.TenantService}. */ public class TenantRequest { private String requestedId; @@ -60,4 +62,4 @@ public Map getProperties() { public void setProperties(Map properties) { this.properties = properties; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/rest/src/main/java/org/apache/unomi/utils/HttpUtils.java b/rest/src/main/java/org/apache/unomi/utils/HttpUtils.java index d09bd9f8a..8242c39c1 100644 --- a/rest/src/main/java/org/apache/unomi/utils/HttpUtils.java +++ b/rest/src/main/java/org/apache/unomi/utils/HttpUtils.java @@ -37,7 +37,9 @@ /** - * This is duplicate of the class from the wab bundle, the original file will be removed once endpoints forwarded + * HTTP request helpers shared by REST endpoints. + *

+ * Temporary duplicate of the WAB bundle class; the original will be removed once endpoints are fully forwarded. */ public class HttpUtils { private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class); @@ -77,7 +79,6 @@ public static String dumpBasicRequestInfo(HttpServletRequest httpServletRequest) return stringBuilder.toString(); } - /** * Dumps request cookies. * @@ -115,13 +116,14 @@ public static String dumpRequestHeaders(HttpServletRequest httpServletRequest) { } /** - * Return the cookie string for the given profile - * We can't use the build in NewCookie jax-rs object as it does not support the SameSite value. + * Builds a {@code Set-Cookie} header value for the profile ID cookie. + *

+ * Uses a raw header string because JAX-RS {@code NewCookie} does not support {@code SameSite}. * - * @param profile to parse - * @param configSharingService shared config location. - * @param isSecure whether the cookie should include the Secure flag - * @return the cookie string to set in the header. + * @param profile the profile whose ID is stored in the cookie + * @param configSharingService shared configuration for cookie name, domain, and flags + * @param isSecure whether the cookie should include the {@code Secure} flag + * @return the cookie header value */ public static String getProfileCookieString(Profile profile, ConfigSharingService configSharingService, boolean isSecure) { final String profileIdCookieDomain = (String) configSharingService.getProperty("profileIdCookieDomain"); diff --git a/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerProvider.java b/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerProvider.java index 35bd0d5e6..d2bc83bb6 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerProvider.java +++ b/services/src/main/java/org/apache/unomi/services/impl/scheduler/SchedulerProvider.java @@ -110,12 +110,14 @@ public interface SchedulerProvider { /** * Permanently removes a task record from storage. + * * @param taskId the task ID to remove */ void deleteTask(String taskId); /** * Saves a task to the persistence service if it's persistent. + * * @param task The task to save * @return true if the task was successfully saved, false otherwise */ 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 6c0a1804b..dc305c0b4 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 @@ -232,6 +232,7 @@ private enum TaskTransition { /** * Checks if a state transition is valid + * * @param from Current task state * @param to Target task state * @return true if transition is valid @@ -245,6 +246,7 @@ public static boolean isValidTransition(TaskStatus from, TaskStatus to) { /** * Checks if all required services are initialized and available + * * @return true if services are ready, false otherwise */ private boolean areServicesReady() { @@ -255,6 +257,7 @@ private boolean areServicesReady() { /** * Checks if all required services are initialized and available, including persistence provider if required + * * @param requirePersistenceProvider Whether the operation requires persistence provider to be available * @return true if services are ready, false otherwise */ @@ -273,6 +276,7 @@ private boolean areServicesReady(boolean requirePersistenceProvider) { /** * Queues an operation to be executed once services are available + * * @param type The type of operation * @param description Human-readable description of the operation * @param parameters The parameters for the operation @@ -283,6 +287,7 @@ private void queuePendingOperation(OperationType type, String description, Objec /** * Queues an operation to be executed once services are available + * * @param type The type of operation * @param description Human-readable description of the operation * @param requirePersistenceProvider Whether the operation requires persistence provider to be available @@ -385,6 +390,7 @@ private void processPendingOperations() { /** * Determines if an operation type requires the persistence provider to be available + * * @param operation The pending operation * @return true if the operation requires persistence provider, false otherwise */ @@ -411,6 +417,7 @@ private boolean requiresPersistenceProvider(PendingOperation operation) { /** * Executes a specific pending operation + * * @param operation The operation to execute */ private void executePendingOperation(PendingOperation operation) { @@ -461,6 +468,7 @@ private void executePendingOperation(PendingOperation operation) { /** * Updates task state with validation and persistence + * * @param task The task to update * @param newStatus The new status to set * @param error Optional error message for failed states @@ -662,6 +670,7 @@ public void setPersistenceProvider(SchedulerProvider persistenceProvider) { /** * Checks if all remaining operations in the queue require the persistence provider + * * @return true if all remaining operations require persistence, false otherwise */ private boolean checkIfAllRemainingOperationsRequirePersistence() { @@ -917,6 +926,7 @@ public void preDestroy() { /** * Checks if the scheduler is shutting down. * This method is used by TaskExecutionManager to skip task execution during shutdown. + * * @return true if the scheduler is shutting down, false otherwise */ public boolean isShutdownNow() { @@ -1170,6 +1180,7 @@ public void scheduleTask(ScheduledTask task) { /** * Internal method to schedule a task - called when services are ready + * * @param task The task to schedule */ private void scheduleTaskInternal(ScheduledTask task) { @@ -1229,6 +1240,7 @@ public void deleteTask(String taskId) { /** * Internal method to cancel a task - called when services are ready + * * @param taskId The task ID to cancel */ private void cancelTaskInternal(String taskId) { @@ -1552,6 +1564,7 @@ public void retryTask(String taskId, boolean resetFailureCount) { /** * Internal method to retry a task - called when services are ready + * * @param taskId The task ID to retry * @param resetFailureCount Whether to reset the failure count */ @@ -1580,6 +1593,7 @@ public void resumeTask(String taskId) { /** * Internal method to resume a task - called when services are ready + * * @param taskId The task ID to resume */ private void resumeTaskInternal(String taskId) { @@ -1689,6 +1703,7 @@ private boolean updateTaskInPersistence(ScheduledTask task) { /** * Saves a task to the persistence service if it's persistent. + * * @param task The task to save * @return true if the task was successfully saved, false otherwise */ @@ -1869,6 +1884,7 @@ private int compareStrings(String str1, String str2) { /** * Gets the number of pending operations waiting to be processed + * * @return The number of pending operations */ public int getPendingOperationsCount() { @@ -1877,6 +1893,7 @@ public int getPendingOperationsCount() { /** * Gets a list of pending operations for debugging purposes + * * @return List of pending operation descriptions */ public List getPendingOperationsList() { diff --git a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskHistoryManager.java b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskHistoryManager.java index 510ba761e..1425fc965 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskHistoryManager.java +++ b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskHistoryManager.java @@ -70,7 +70,7 @@ public void recordSuccess(ScheduledTask task, long executionTime) { entry.put("status", "SUCCESS"); entry.put("nodeId", nodeId); entry.put("executionTime", executionTime); - + addToHistory(task, entry); metricsManager.updateMetric(TaskMetricsManager.METRIC_TASKS_COMPLETED); metricsManager.updateMetric(TaskMetricsManager.METRIC_TASKS_EXECUTION_TIME, executionTime); @@ -88,7 +88,7 @@ public void recordFailure(ScheduledTask task, String error) { entry.put("status", "FAILED"); entry.put("nodeId", nodeId); entry.put("error", error); - + addToHistory(task, entry); metricsManager.updateMetric(TaskMetricsManager.METRIC_TASKS_FAILED); } @@ -103,7 +103,7 @@ public void recordCrash(ScheduledTask task) { entry.put("timestamp", new Date()); entry.put("status", "CRASHED"); entry.put("nodeId", nodeId); - + addToHistory(task, entry); metricsManager.updateMetric(TaskMetricsManager.METRIC_TASKS_CRASHED); } @@ -118,7 +118,7 @@ public void recordCancellation(ScheduledTask task) { entry.put("timestamp", new Date()); entry.put("status", "CANCELLED"); entry.put("nodeId", nodeId); - + addToHistory(task, entry); metricsManager.updateMetric(TaskMetricsManager.METRIC_TASKS_CANCELLED); } @@ -133,7 +133,7 @@ public void recordResume(ScheduledTask task) { entry.put("timestamp", new Date()); entry.put("status", "RESUMED"); entry.put("nodeId", nodeId); - + addToHistory(task, entry); metricsManager.updateMetric(TaskMetricsManager.METRIC_TASKS_RESUMED); } @@ -148,7 +148,7 @@ public void recordRetry(ScheduledTask task) { entry.put("timestamp", new Date()); entry.put("status", "RETRIED"); entry.put("nodeId", nodeId); - + addToHistory(task, entry); metricsManager.updateMetric(TaskMetricsManager.METRIC_TASKS_RETRIED); } @@ -184,10 +184,10 @@ private void addToHistory(ScheduledTask task, Map entry) { } /** - * Gets execution history for a task. + * Returns execution history entries stored on the task. * * @param task the task - * @return execution history entries, or an empty list + * @return execution history entries, or an empty list when none exist */ public List> getExecutionHistory(ScheduledTask task) { Map details = task.getStatusDetails(); @@ -199,4 +199,4 @@ public List> getExecutionHistory(ScheduledTask task) { List> history = (List>) details.get("executionHistory"); return history != null ? history : Collections.emptyList(); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskLockManager.java b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskLockManager.java index e7bb5f2df..0605ddb0f 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskLockManager.java +++ b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskLockManager.java @@ -25,7 +25,7 @@ /** * Manages task locks to coordinate execution in a cluster environment. - * This class ensures that tasks which don't allow parallel execution + * Ensures that tasks which do not allow parallel execution * only run on a single node at a time. * *

Distributed Locking Strategy:

diff --git a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskRecoveryManager.java b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskRecoveryManager.java index 4b2938729..7d6da402e 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskRecoveryManager.java +++ b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskRecoveryManager.java @@ -114,7 +114,7 @@ public void setSchedulerService(SchedulerServiceImpl schedulerService) { } /** - * Set the shutdown flag to prevent operations during shutdown + * Marks the manager as shutting down so recovery work is skipped. */ public void prepareForShutdown() { this.shutdownNow = true; @@ -122,12 +122,10 @@ public void prepareForShutdown() { } /** - * Recovers tasks that crashed due to node failure or unexpected termination - * Process: - * 1. Identify tasks with expired locks - * 2. Release locks and update states - * 3. Attempt to resume tasks with checkpoint data - * 4. Reschedule tasks that can't be resumed + * Recovers crashed and stale locked tasks after node failure. + *

+ * Running tasks with expired locks are marked crashed, then resumed or restarted. + * Expired locks on non-running tasks are released and eligible tasks are rescheduled. */ public void recoverCrashedTasks() { if (shutdownNow) { @@ -144,7 +142,7 @@ public void recoverCrashedTasks() { } /** - * Recovers tasks that are marked as running but have expired locks + * Recovers running tasks whose locks have expired. */ private void recoverRunningTasks() { if (shutdownNow) return; @@ -162,7 +160,9 @@ private void recoverRunningTasks() { } /** - * Recovers a single crashed task + * Recovers one crashed task by marking it crashed, recording history, and resuming or restarting it. + * + * @param task the task to recover */ private void recoverCrashedTask(ScheduledTask task) { // Skip cancelled tasks - they should not be recovered @@ -206,7 +206,10 @@ private void recoverCrashedTask(ScheduledTask task) { } /** - * Records a task crash in its execution history + * Appends a crash entry to the task execution history. + * + * @param task the crashed task + * @param previousOwner the node that previously held the lock */ private void recordCrash(ScheduledTask task, String previousOwner) { Map crash = new HashMap<>(); @@ -235,7 +238,10 @@ private void recordCrash(ScheduledTask task, String previousOwner) { } /** - * Attempts to resume a crashed task + * Reschedules and executes a crashed task that supports resumption. + * + * @param task the task to resume + * @param executor the executor for the task type */ private void attemptTaskResumption(ScheduledTask task, TaskExecutor executor) { LOGGER.info("Node {} resuming crashed task {} : {}", nodeId, task.getTaskType(), task.getItemId()); @@ -247,7 +253,10 @@ private void attemptTaskResumption(ScheduledTask task, TaskExecutor executor) { } /** - * Attempts to restart a task that can't be resumed + * Reschedules and executes a crashed task that cannot be resumed. + * + * @param task the task to restart + * @param executor the executor for the task type */ private void attemptTaskRestart(ScheduledTask task, TaskExecutor executor) { LOGGER.info("Node {} restarting crashed task: {}", nodeId, task.getItemId()); @@ -258,7 +267,7 @@ private void attemptTaskRestart(ScheduledTask task, TaskExecutor executor) { } /** - * Recovers tasks with expired locks that are not marked as running + * Releases expired locks on tasks that are not currently running. */ private void recoverLockedTasks() { List lockedTasks = schedulerService.findLockedTasks(); @@ -272,7 +281,9 @@ private void recoverLockedTasks() { } /** - * Recovers a single locked task + * Releases an expired lock and reschedules the task when appropriate. + * + * @param task the locked task to recover */ private void recoverLockedTask(ScheduledTask task) { lockManager.releaseLock(task); @@ -295,7 +306,10 @@ private void recoverLockedTask(ScheduledTask task) { } /** - * Determines if a crashed task should be restarted + * Returns whether a crashed task should be restarted instead of abandoned. + * + * @param task the crashed task + * @return {@code true} when the task should be restarted */ private boolean shouldRestartTask(ScheduledTask task) { // Don't restart one-shot tasks that have already started @@ -311,9 +325,11 @@ private boolean shouldRestartTask(ScheduledTask task) { return task.isEnabled(); } - /** - * Gets dependencies for a task + * Loads dependency tasks referenced by the given task. + * + * @param task the task whose dependencies are needed + * @return dependency tasks keyed by ID */ private Map getTaskDependencies(ScheduledTask task) { if (task.getDependsOn() == null || task.getDependsOn().isEmpty()) { @@ -331,7 +347,9 @@ private Map getTaskDependencies(ScheduledTask task) { } /** - * Update running task to crashed state + * Marks a running task as crashed and releases its lock. + * + * @param task the task to mark as crashed */ private void markAsCrashed(ScheduledTask task) { try { @@ -361,7 +379,9 @@ private void markAsCrashed(ScheduledTask task) { } /** - * Resets a task that has been in running state for too long + * Marks a stalled running task as failed after a timeout. + * + * @param task the stalled task */ private void resetStalledTask(ScheduledTask task) { try { diff --git a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskStateManager.java b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskStateManager.java index 844908077..07c118c50 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskStateManager.java +++ b/services/src/main/java/org/apache/unomi/services/impl/scheduler/TaskStateManager.java @@ -24,8 +24,9 @@ import java.util.*; /** - * Manages task state transitions and validation. - * This class centralizes all state-related logic for scheduled tasks. + * Enforces allowed {@link org.apache.unomi.api.tasks.ScheduledTask.TaskStatus} transitions. + * The scheduler calls this helper before running, completing, or recovering tasks so + * invalid state changes are rejected and logged consistently across nodes. */ public class TaskStateManager { private static final Logger LOGGER = LoggerFactory.getLogger(TaskStateManager.class); @@ -95,7 +96,11 @@ public void updateTaskState(ScheduledTask task, TaskStatus newStatus, String err } /** - * Validates a state transition + * Validates that the requested status change is allowed. + * + * @param currentStatus the current task status + * @param newStatus the requested status + * @throws IllegalStateException when the transition is not allowed */ private void validateStateTransition(TaskStatus currentStatus, TaskStatus newStatus) { if (currentStatus == TaskStatus.CANCELLED && newStatus == TaskStatus.CRASHED) { @@ -103,7 +108,7 @@ private void validateStateTransition(TaskStatus currentStatus, TaskStatus newSta String.format("Cannot recover a cancelled task: Invalid state transition from %s to %s", currentStatus, newStatus)); } - + if (!TaskTransition.isValidTransition(currentStatus, newStatus)) { throw new IllegalStateException( String.format("Invalid state transition from %s to %s", @@ -112,7 +117,11 @@ private void validateStateTransition(TaskStatus currentStatus, TaskStatus newSta } /** - * Updates state-specific fields based on the new status + * Updates fields that depend on the new task status. + * + * @param task the task being updated + * @param newStatus the new status + * @param nodeId the node performing the update */ private void updateStateSpecificFields(ScheduledTask task, TaskStatus newStatus, String nodeId) { switch (newStatus) { @@ -250,9 +259,10 @@ private void validateDependencies(ScheduledTask task, Map } /** - * Calculates the next execution time for a task - * @param task The task to calculate next execution for - * @param isRetry Whether this calculation is for a retry attempt + * Calculates and stores the next execution time for a task. + * + * @param task the task to schedule + * @param isRetry {@code true} when computing a retry delay */ public void calculateNextExecutionTime(ScheduledTask task, boolean isRetry) { long now = System.currentTimeMillis(); @@ -327,8 +337,9 @@ public void calculateNextExecutionTime(ScheduledTask task, boolean isRetry) { } /** - * Calculates the next execution time for a task (non-retry case) - * @param task The task to calculate next execution for + * Calculates and stores the next execution time for a normal (non-retry) run. + * + * @param task the task to schedule */ public void calculateNextExecutionTime(ScheduledTask task) { calculateNextExecutionTime(task, false); diff --git a/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java b/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java index ceb5fdcf8..201a01fe9 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java +++ b/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java @@ -64,7 +64,7 @@ public boolean validateRequest(String tenantId, String apiKey) { private boolean validateApiKey(String tenantId, String apiKey) { // Implementation of API key validation - return true; // TODO: Implement actual validation + return true; // Stub: full API key validation is handled by CXF tenant filters } private void loadSecurityConfigurations() { diff --git a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java index aec758fe4..683d75bc8 100644 --- a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java +++ b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java @@ -24,8 +24,10 @@ import java.util.List; /** - * Represents a node in the request tracing tree structure. - * Each node contains information about an operation, its timing, and any child operations. + * One node in the hierarchical request trace returned to operators. + * Each node records an operation name, timing, optional context/result strings, + * log lines, and nested child nodes. The tracing service assembles these into + * a tree attached to context and event processing responses. */ @JsonIgnoreProperties(ignoreUnknown = true) public class TraceNode implements Serializable { From bf23805204309e90d8b57f9147e230fb09e9d15b Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 08:54:44 +0200 Subject: [PATCH 10/19] UNOMI-960: Fix 3.0/3.1 documentation accuracy and gaps Remove fictional 3.0 compatibility mode, align version pins and config keys with the codebase, update tenant/shell examples, expand whats-new and security chapters, and refresh root and module READMEs for 3.1. --- README.md | 10 +++ docker/README.md | 21 +++++- extensions/healthcheck/README.md | 4 +- graphql/README.md | 9 +++ .../src/main/asciidoc/5-min-quickstart.adoc | 26 +++++++- manual/src/main/asciidoc/architecture.adoc | 2 + .../main/asciidoc/building-and-deploying.adoc | 4 +- manual/src/main/asciidoc/configuration.adoc | 42 +++++++----- manual/src/main/asciidoc/datamodel.adoc | 14 ++++ manual/src/main/asciidoc/foreword.adoc | 2 + manual/src/main/asciidoc/getting-started.adoc | 6 +- manual/src/main/asciidoc/graphql.adoc | 11 ++++ manual/src/main/asciidoc/index.adoc | 5 ++ .../migrations/migrate-2.x-to-3.0.adoc | 2 +- .../migrations/migrate-3.0-to-3.1.adoc | 64 ++++-------------- .../migrate-elasticsearch-to-opensearch.adoc | 2 +- .../migrations/v2-compatibility-mode.adoc | 12 ++-- manual/src/main/asciidoc/multitenancy.adoc | 8 +-- manual/src/main/asciidoc/privacy.adoc | 8 ++- manual/src/main/asciidoc/recipes.adoc | 2 +- manual/src/main/asciidoc/security.adoc | 19 +++++- .../src/main/asciidoc/useful-unomi-urls.adoc | 2 +- manual/src/main/asciidoc/whats-new.adoc | 66 +++++++------------ manual/src/main/asciidoc/writing-plugins.adoc | 2 +- rest/README.md | 32 +++++++-- 25 files changed, 232 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index 9c2fca51c..9499890ad 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,16 @@ Branches compile and be stable. These are recommended for users that prefer to work from the source code. Otherwise you can find packaged binaries on the [Apache Unomi website](https://unomi.apache.org). + +Apache Unomi 3.x +-------------- +Unomi 3.0+ requires **Java 17**, **Karaf 4.4**, and **Elasticsearch 9.x**. Unomi 3.1 adds **multi-tenancy**, **OpenSearch 3** support, a cluster-aware **task scheduler**, and persistence-based clustering. + +* Build and run: see `manual/src/main/asciidoc/building-and-deploying.adoc` or `manual/src/main/asciidoc/5-min-quickstart.adoc` +* Docker: see `docker/README.md` +* Multi-tenancy and API keys: see `manual/src/main/asciidoc/multitenancy.adoc` +* Migrations: see `manual/src/main/asciidoc/migrations/migrations.adoc` + Documentation ------------- You can find all the updated documentation, including building and deployment instructions, on the [Apache Unomi diff --git a/docker/README.md b/docker/README.md index 8cfa6c048..c5ab6553d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -83,7 +83,7 @@ docker pull apache/unomi:3.1.0-SNAPSHOT docker run -d --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ -e UNOMI_DISTRIBUTION=unomi-distribution-opensearch \ -e UNOMI_OPENSEARCH_ADDRESSES=opensearch:9200 \ - -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} + -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ apache/unomi:3.1.0-SNAPSHOT ``` @@ -101,7 +101,7 @@ For OpenSearch: ```bash docker run -d --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ - -e UNOMI_DISTRIBUTION=unomi-distribution-opensearch + -e UNOMI_DISTRIBUTION=unomi-distribution-opensearch \ -e UNOMI_OPENSEARCH_ADDRESSES=host.docker.internal:9200 \ -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ apache/unomi:3.1.0-SNAPSHOT @@ -125,6 +125,23 @@ Note: Linux doesn't support the host.docker.internal DNS lookup method yet, it s - `UNOMI_OPENSEARCH_ADDRESSES`: OpenSearch host:port (default: localhost:9200) - `UNOMI_OPENSEARCH_PASSWORD`: Required admin password for OpenSearch (SSL and authentication are mandatory) + + +## First steps after startup (Unomi 3.1+) + +Multi-tenancy requires a tenant before client endpoints such as `/cxs/context.json` accept traffic: + +```bash +curl -X POST http://localhost:8181/cxs/tenants \ + --user karaf:karaf \ + -H "Content-Type: application/json" \ + -d '{"requestedId":"default","properties":{"name":"Default Tenant"}}' +``` + +Save the public and private API keys from the response. Use the public key in the `X-Unomi-Api-Key` header for public endpoints. + +See the [Multi-tenancy](https://github.com/apache/unomi/blob/master/manual/src/main/asciidoc/multitenancy.adoc) chapter in the manual for details. + # Using docker build tools If you want to rebuild the images or use docker compose directly, you must still first use `mvn clean install` to generate diff --git a/extensions/healthcheck/README.md b/extensions/healthcheck/README.md index 323768e0e..fccbf65d7 100644 --- a/extensions/healthcheck/README.md +++ b/extensions/healthcheck/README.md @@ -36,8 +36,8 @@ The healthcheck is available even if unomi is not started. It gives health infor All healthcheck can have a status : - DOWN (service is not available) - - UP (service is up but does not respond to request (starting or misconfigured)) - - LIVE (service is ready to serve request) + - UP (service is running or starting) + - LIVE (service is ready to serve requests) - ERROR (an error occurred during service health check) Any subsystem health check have a timeout of 500ms where check is cancelled and will be returned as error. diff --git a/graphql/README.md b/graphql/README.md index ee39d64b3..80d78aefe 100644 --- a/graphql/README.md +++ b/graphql/README.md @@ -18,6 +18,15 @@ Apache Unomi GraphQL API ======================== + +## Authentication (Unomi 3.1+) + +- **Public `cdp` queries**: `X-Unomi-Api-Key` header with a tenant public API key +- **Mutations / admin queries**: Basic auth `tenantId:privateApiKey` or JAAS + `X-Unomi-Tenant-Id` +- **Introspection**: allowed without credentials + +Enable GraphQL via `unomi-distribution-*-graphql` or the `cdp-graphql-feature` Karaf feature. See `manual/src/main/asciidoc/graphql.adoc`. + Install ------- diff --git a/manual/src/main/asciidoc/5-min-quickstart.adoc b/manual/src/main/asciidoc/5-min-quickstart.adoc index 420095493..bbf4b4c4f 100644 --- a/manual/src/main/asciidoc/5-min-quickstart.adoc +++ b/manual/src/main/asciidoc/5-min-quickstart.adoc @@ -24,7 +24,7 @@ Begin by creating a `docker-compose.yml` file. You can choose between ElasticSea version: '3.8' services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1 + image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3 environment: - discovery.type=single-node ports: @@ -94,6 +94,26 @@ volumes: From the same folder, start the environment using `docker-compose up` and wait for the startup to complete. +==== After startup (ElasticSearch Docker path) + +Once Unomi is running, create a tenant and save the API keys from the response: + +[source,bash] +---- +curl -X POST http://localhost:8181/cxs/tenants \ + --user karaf:karaf \ + -H "Content-Type: application/json" \ + -d '{ + "requestedId": "default", + "properties": { + "name": "Default Tenant", + "description": "Default tenant for quick start" + } + }' +---- + +Use the public API key in `X-Unomi-Api-Key` for `/cxs/context.json` requests. See <<_multitenancy,Multi-tenancy>>. + Try accessing https://localhost:9443/cxs/cluster with username/password: karaf/karaf . You might get a certificate warning in your browser, just accept it despite the warning it is safe. === Quick Start manually @@ -102,7 +122,7 @@ Try accessing https://localhost:9443/cxs/cluster with username/password: karaf/k 1) Install JDK 17 and make sure you set the JAVA_HOME variable (see our <<_jdk_compatibility,Getting Started>> guide for more information on JDK compatibility) -2) Download ElasticSearch here : https://www.elastic.co/downloads/past-releases/elasticsearch-9-2-1 (please *make sure* you use the proper version : 9.2.1) +2) Download ElasticSearch here : https://www.elastic.co/downloads/past-releases/elasticsearch-9-4-3 (please *make sure* you use the proper version : 9.4.3) 3) Uncompress it and change the `config/elasticsearch.yml` to include the following config : @@ -115,7 +135,7 @@ cluster.name: contextElasticSearch ==== Option 2: Using OpenSearch -1) Install JDK 11 as described above +1) Install JDK 17 as described above 2) Download OpenSearch here: https://opensearch.org/downloads.html (please *make sure* you use version 3.x) diff --git a/manual/src/main/asciidoc/architecture.adoc b/manual/src/main/asciidoc/architecture.adoc index 0bfaa1376..383809d11 100644 --- a/manual/src/main/asciidoc/architecture.adoc +++ b/manual/src/main/asciidoc/architecture.adoc @@ -14,6 +14,8 @@ === High-Level Architecture +NOTE: Unomi 3.1 adds <<_multitenancy,multi-tenancy>>, <<_scheduler,task scheduling>>, and <<_clustering,persistence-based clustering>>. The diagram below focuses on core request processing. + [plantuml] ---- @startuml diff --git a/manual/src/main/asciidoc/building-and-deploying.adoc b/manual/src/main/asciidoc/building-and-deploying.adoc index 6736fc335..a18013838 100644 --- a/manual/src/main/asciidoc/building-and-deploying.adoc +++ b/manual/src/main/asciidoc/building-and-deploying.adoc @@ -240,11 +240,11 @@ To merge the branch into master. ==== Installing a Search Engine -Starting with version 1.2, Apache Unomi no longer embeds a search engine server. You will need to install either ElasticSearch or OpenSearch as a standalone service. +Apache Unomi 3.x does not embed a search engine. Install Elasticsearch 9.x or OpenSearch 3.x as a standalone service. See <<_migrate_from_2_x_to_3_0,Migrate from 2.x to 3.0>> for version requirements. ===== Option 1: Using ElasticSearch -1. Download ElasticSearch 7.17.5 from: https://www.elastic.co/downloads/past-releases/elasticsearch-7-17-5[https://www.elastic.co/downloads/past-releases/elasticsearch-7-17-5] +1. Download Elasticsearch 9.4.3 from: https://www.elastic.co/downloads/past-releases/elasticsearch-9-4-3[https://www.elastic.co/downloads/past-releases/elasticsearch-9-4-3] 2. Uncompress the downloaded package into a directory diff --git a/manual/src/main/asciidoc/configuration.adoc b/manual/src/main/asciidoc/configuration.adoc index 96a265f43..6d0b7369c 100644 --- a/manual/src/main/asciidoc/configuration.adoc +++ b/manual/src/main/asciidoc/configuration.adoc @@ -82,6 +82,11 @@ org.apache.unomi.elasticsearch.cluster.name=contextElasticSearch # hostA:9200,hostB:9200 # Note: the port number must be repeated for each host. org.apache.unomi.elasticsearch.addresses=localhost:9200 +# Optional Elasticsearch security (when xpack security is enabled) +org.apache.unomi.elasticsearch.username=elastic +org.apache.unomi.elasticsearch.password=${env:ELASTIC_PASSWORD} +org.apache.unomi.elasticsearch.sslEnable=true +org.apache.unomi.elasticsearch.sslTrustAllCertificates=true ---- For OpenSearch: @@ -111,7 +116,7 @@ To select which search engine to use, you can: * For OpenSearch: use `UNOMI_DISTRIBUTION=unomi-distribution-opensearch` * For custom configurations: use `UNOMI_DISTRIBUTION=your-custom-distribution-name` -Note: The `UNOMI_DISTRIBUTION` environment variable accepts any karaf feature that is available form Karaf. Two built-in distributions (unomi-distribution-elasticsearch, unomi-distribution-opensearch) are packaged with UNOMI archive but any other can be added, either in the package or by adding a feature repository that contains the desired one in Karaf. +Note: The `UNOMI_DISTRIBUTION` environment variable accepts any karaf feature that is available from Karaf. Two built-in distributions (unomi-distribution-elasticsearch, unomi-distribution-opensearch) are packaged with UNOMI archive but any other can be added, either in the package or by adding a feature repository that contains the desired one in Karaf. Note: When using OpenSearch 3.x: - Security is enabled by default and requires SSL/TLS @@ -282,10 +287,10 @@ curl -X POST "http://localhost:8181/cxs/tenants" \ -u karaf:karaf \ -H "Content-Type: application/json" \ -d '{ - "itemId": "mytenant", - "name": "My Company", - "description": "My Company tenant", + "requestedId": "mytenant", "properties": { + "name": "My Company", + "description": "My Company tenant", "address": "123 Main St", "country": "USA" } @@ -293,7 +298,7 @@ curl -X POST "http://localhost:8181/cxs/tenants" \ # Response (HTTP 201 Created): { - "itemId": "mytenant", + "requestedId": "mytenant", "name": "My Company", "description": "My Company tenant", "properties": { @@ -322,20 +327,21 @@ curl -X DELETE "http://localhost:8181/cxs/tenants/mytenant" \ -u karaf:karaf ---- -Using Karaf shell (requires admin access to Karaf console): +Using Karaf shell (requires admin access to Karaf console). See <<_shell_commands,Shell commands>> for full syntax: + [source,bash] ---- -# Create a tenant -unomi:tenant-create mytenant "My Company" --description="My Company tenant" +# Create a tenant (JSON properties) +unomi:crud create tenant -d '{"itemId":"mytenant","name":"My Company","description":"My Company tenant"}' -# List all tenants -unomi:tenant-list +# List tenants +unomi:crud list tenant # View tenant details -unomi:tenant-show mytenant +unomi:crud read tenant -i mytenant # Delete a tenant -unomi:tenant-delete mytenant +unomi:crud delete tenant -i mytenant ---- ==== API Keys and Authentication @@ -348,7 +354,7 @@ The API keys are automatically generated when creating a tenant. You can view th [source,bash] ---- # Using Karaf shell (requires admin access) -unomi:tenant-show mytenant +unomi:crud read tenant -i mytenant # Output example: Tenant Details: @@ -379,7 +385,7 @@ curl -X POST "http://localhost:8181/cxs/tenants/mytenant/apikeys?type=PUBLIC&val } # Using Karaf shell (requires admin access) -unomi:tenant-generate-key mytenant PUBLIC 30 +unomi:crud create apikey -d '{"tenantId":"mytenant","keyType":"PUBLIC","validityPeriod":30}' ---- ==== Accessing API Endpoints @@ -1648,7 +1654,7 @@ services: depends_on: - elasticsearch elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.15.0 + image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3 environment: - discovery.type=single-node - xpack.security.enabled=false @@ -1716,7 +1722,7 @@ services: depends_on: - elasticsearch elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.15.0 + image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3 environment: - discovery.type=single-node - xpack.security.enabled=false @@ -1767,8 +1773,8 @@ Existing health checks gives information about : All healthcheck can have a status : - DOWN (service is not available) -- UP (service is up but does not respond to request (starting or misconfigured)) -- LIVE (service is ready to serve request) +- UP (service is running or starting) +- LIVE (service is ready to serve requests) - ERROR (an error occurred during service health check) Any subsystem health check have a timeout of 400ms where check is cancelled and will be returned as error. diff --git a/manual/src/main/asciidoc/datamodel.adoc b/manual/src/main/asciidoc/datamodel.adoc index 2f4d17e0a..0e2193d29 100755 --- a/manual/src/main/asciidoc/datamodel.adoc +++ b/manual/src/main/asciidoc/datamodel.adoc @@ -60,6 +60,20 @@ This base structure can be extended, if needed, using properties in the form of These properties are further defined by the `Item`’s type definition which explicits the `Item`’s structure and semantics. By defining new types, users specify which properties (including the type of values they accept) are available to items of that specific type. + +[[_tenant_entity]] +=== Tenant (Unomi 3.1+) + +A *tenant* is the top-level isolation boundary in Unomi 3.1. Each tenant owns its own profiles, events, segments, rules, property types, and JSON schemas. Tenant metadata is stored as an `Item` with `itemType` `tenant`. + +Most persisted items carry a `tenantId` (or inherit tenant scope through the execution context). Client requests must establish tenant context through: + +* `X-Unomi-Api-Key` (public key resolves the tenant), or +* Basic authentication `tenantId:privateApiKey`, or +* JAAS administrator credentials with `X-Unomi-Tenant-Id`. + +See <<_multitenancy,Multi-tenancy>> for management and authentication. + Unomi defines default value types: `date`, `email`, `integer` and `string`, all pretty self-explanatory. While you can think of these value types as "primitive" types, it is possible to extend Unomi by providing additional value types. diff --git a/manual/src/main/asciidoc/foreword.adoc b/manual/src/main/asciidoc/foreword.adoc index 19fcbeec9..4cacc9233 100644 --- a/manual/src/main/asciidoc/foreword.adoc +++ b/manual/src/main/asciidoc/foreword.adoc @@ -24,3 +24,5 @@ This guide covers everything from getting started with your first installation t The Apache Unomi community is committed to providing comprehensive, accurate, and up-to-date documentation. We welcome feedback and contributions to help improve this documentation for all users. Happy reading! + +This edition documents Apache Unomi 3.x, which requires Java 17, Elasticsearch 9.x or OpenSearch 3.x, and (from 3.1) tenant API keys for public endpoints. diff --git a/manual/src/main/asciidoc/getting-started.adoc b/manual/src/main/asciidoc/getting-started.adoc index c7acd9121..22fd2788c 100644 --- a/manual/src/main/asciidoc/getting-started.adoc +++ b/manual/src/main/asciidoc/getting-started.adoc @@ -37,8 +37,8 @@ them at your own risks. Apache Unomi supports two search engine backends: -* *ElasticSearch*: Version 7.17.5 is supported starting with Unomi 2.0.0 -* *OpenSearch*: Version 3.x is supported +* *ElasticSearch*: Version **9.x** (for example 9.4.3) is required for Unomi 3.0 and later. Version 7.17.5 applies only to Unomi 2.x. +* *OpenSearch*: Version 3.x is supported starting with Unomi 3.1 It is highly recommended to use the versions specified in the documentation. When in doubt, consult the Apache Unomi community for the latest compatibility information. @@ -85,7 +85,7 @@ curl -X POST http://localhost:8181/cxs/tenants \ }' ---- -Save the API keys from the response - you'll need them for all subsequent API calls. +Save the API keys from the response - you'll need them for all subsequent API calls. See <<_multitenancy,Multi-tenancy>> for authentication details. Now you can: diff --git a/manual/src/main/asciidoc/graphql.adoc b/manual/src/main/asciidoc/graphql.adoc index 54da2adaf..3824517e2 100644 --- a/manual/src/main/asciidoc/graphql.adoc +++ b/manual/src/main/asciidoc/graphql.adoc @@ -36,6 +36,17 @@ Thanks to GraphQL introspection, there is no dedicated documentation per-se as t You can easily view the schema by navigrating to `/graphql-ui`, depending on your setup (localhost, public host, ...), you might need to adjust the URL to point GraphQL UI to the `/graphql` endpoint. + +==== Authentication + +GraphQL requests follow the same tenant security model as REST: + +* *Public read operations* on the `cdp` root field: send `X-Unomi-Api-Key` with a tenant **public** API key. +* *Mutations and administrative queries*: HTTP Basic authentication with `tenantId:privateApiKey`, or JAAS credentials plus optional `X-Unomi-Tenant-Id`. +* *Introspection* (`IntrospectionQuery`): allowed without credentials for schema discovery. + +See <<_multitenancy,Multi-tenancy>> for key management. + === Multi-Tenancy Support The GraphQL API includes comprehensive multi-tenancy support, allowing different tenants to have customized GraphQL schemas based on their specific data models and property types. diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc index a464d89bc..ef29dfc26 100644 --- a/manual/src/main/asciidoc/index.adoc +++ b/manual/src/main/asciidoc/index.adoc @@ -97,6 +97,11 @@ include::clustering.adoc[] include::scheduler.adoc[] +[[_health_check_chapter]] +== Health check + +The health check endpoint and providers are documented in the Configuration chapter: <<_health_check,Health check extension>>. + == Reference === Architecture Overview diff --git a/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc b/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc index 8b58df24e..34f7143f0 100644 --- a/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc @@ -42,7 +42,7 @@ NOTE: OpenSearch support is introduced in Apache Unomi 3.1. If you plan to use O Apache Unomi 3.0 includes an upgrade of the underlying Karaf framework: - **Previous Version**: Karaf 4.2.15 -- **New Version**: Karaf 4.4.8 +- **New Version**: Karaf 4.4.11 This upgrade brings improved stability and support for the latest dependencies. diff --git a/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc b/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc index 8a1f82c35..a4a58c242 100644 --- a/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc @@ -246,65 +246,29 @@ Contributors maintaining the Groovy scripts that implement this upgrade should f 4. **Start your Apache Unomi 3.1 cluster** 5. **Test your applications** with the new authentication model -=== 3.0 Compatibility Mode +=== Transitional authentication options -To facilitate the migration process, Unomi 3.1 includes a **3.0 compatibility mode** that allows 3.0 client applications to work with Unomi 3.1 without immediate code changes. +Unomi 3.1 does **not** provide a separate "3.0 compatibility mode" system property. Use one of the following approaches while migrating clients: -==== Enabling 3.0 Compatibility Mode +==== Option 1: Update clients to 3.1 authentication (recommended) -To enable 3.0 compatibility mode, set the following system property when starting Unomi 3.1: +Adopt tenant public API keys for public endpoints and tenant private keys or JAAS for administrative work. See <<_multitenancy,Multi-tenancy>> and the authentication examples in this guide. -[source,bash] ----- -# Enable 3.0 compatibility mode --Dunomi.v3_0.compatibility.mode=true ----- - -==== 3.0 Compatibility Mode Behavior - -When 3.0 compatibility mode is enabled: - -- **Public endpoints** (e.g., `/context.json`): No authentication required (same as 3.0) -- **Private endpoints**: JAAS authentication required (same as 3.0) -- **All API endpoints**: Identical behavior to 3.0 -- **Data isolation**: Still enforced through tenant context - -==== Migration Strategy with 3.0 Compatibility Mode - -1. **Phase 1: Enable 3.0 Compatibility Mode** - - Start Unomi 3.1 with `-Dunomi.v3_0.compatibility.mode=true` - - Verify all 3.0 client applications work without changes - - Migrate data to tenant structure +==== Option 2: V2 compatibility mode (2.x clients only) -2. **Phase 2: Gradual Migration** - - Update client applications one by one to use 3.1 authentication - - Test each application with 3.1 authentication - - Keep 3.0 compatibility mode enabled for remaining applications +If you are migrating from **Unomi 2.x** (not 3.0), you can enable <<_v2_compatibility_mode,V2 compatibility mode>> so legacy clients work without API keys during a phased rollout: -3. **Phase 3: Complete Migration** - - Update all client applications to 3.1 authentication - - Disable 3.0 compatibility mode: `-Dunomi.v3_0.compatibility.mode=false` - - Verify all applications work with full 3.1 multi-tenancy - -==== Security Considerations - -- **3.0 compatibility mode** should only be used during migration -- **Production environments** should use full 3.1 authentication for security -- **3.0 compatibility mode** bypasses tenant API key requirements -- **Data isolation** is still enforced through tenant context - -==== Example: Starting Unomi 3.1 with 3.0 Compatibility Mode - -[source,bash] +[source,properties] ---- -# Start Unomi 3.1 with 3.0 compatibility mode -./karaf -Dunomi.v3_0.compatibility.mode=true - -# Or set as environment variable -export KARAF_OPTS="-Dunomi.v3_0.compatibility.mode=true" -./karaf +# etc/org.apache.unomi.rest.authentication.cfg +v2.compatibilitymode.enabled = true +v2.compatibilitymode.defaultTenantId = default ---- +Environment variable equivalent: `UNOMI_REST_AUTHENTICATION_V2COMPATIBILITYMODEENABLED=true` + +WARNING: V2 compatibility mode is intended for migration from 2.x only. It bypasses tenant API key requirements on public endpoints. Disable it once all clients use 3.1 authentication. + === Post Migration Once the migration has been completed, you will be able to start Apache Unomi 3.1 with full multi-tenancy support. diff --git a/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc b/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc index 06316e129..ffd2a3a76 100644 --- a/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc @@ -32,7 +32,7 @@ Before starting the migration, ensure you have: * Running Elasticsearch cluster with your Unomi data * Target OpenSearch cluster set up and running * Sufficient disk space on the target cluster -* Java 11 or later installed +* Java 17 or later installed * Network connectivity between source and target clusters ===== Migration Options diff --git a/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc b/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc index b87bed040..4e956cd6b 100644 --- a/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc +++ b/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc @@ -45,6 +45,8 @@ Before enabling V2 compatibility mode, ensure that: === Enable V2 Compatibility Mode +You can also set the environment variable `UNOMI_REST_AUTHENTICATION_V2COMPATIBILITYMODEENABLED=true` (maps to `org.apache.unomi.rest.authentication.v2CompatibilityModeEnabled` in `custom.system.properties`). + 1. **Edit the configuration file**: ```bash # Edit the configuration file @@ -54,10 +56,10 @@ Before enabling V2 compatibility mode, ensure that: 2. **Enable V2 compatibility mode**: ```properties # Enable V2 compatibility mode - v2CompatibilityModeEnabled = true + v2.compatibilitymode.enabled = true # Set the default tenant ID (should match the tenant ID used during migration) - v2CompatibilityDefaultTenantId = your-migration-tenant-id + v2.compatibilitymode.defaultTenantId = your-migration-tenant-id ``` 3. **Restart the server** to apply the configuration changes: @@ -95,7 +97,7 @@ Enable V2 compatibility mode by updating the configuration file: # Edit the configuration file vi etc/org.apache.unomi.rest.authentication.cfg -# Set v2CompatibilityModeEnabled = true +# Set v2.compatibilitymode.enabled = true # Set v2CompatibilityDefaultTenantId = your-tenant-id # Restart the server to apply changes @@ -254,7 +256,7 @@ The V2 third-party configuration supports dynamic updates: **V2 clients still not working**: - Check configuration file: `etc/org.apache.unomi.rest.authentication.cfg` -- Verify `v2CompatibilityModeEnabled = true` +- Verify `v2.compatibilitymode.enabled = true` - Ensure `v2CompatibilityDefaultTenantId` matches the tenant ID used during migration - Ensure the tenant exists and is accessible @@ -290,7 +292,7 @@ Once all clients are migrated to V3 authentication: 1. **Update configuration**: ```properties - v2CompatibilityModeEnabled = false + v2.compatibilitymode.enabled = false ``` 2. **Restart the server**: diff --git a/manual/src/main/asciidoc/multitenancy.adoc b/manual/src/main/asciidoc/multitenancy.adoc index 9d9c5ae8c..a4b3efb1d 100644 --- a/manual/src/main/asciidoc/multitenancy.adoc +++ b/manual/src/main/asciidoc/multitenancy.adoc @@ -138,11 +138,11 @@ tenant.default.id=default tenant.apikey.validity.period=30 tenant.apikey.validity.unit=DAYS -# Maximum number of API calls per tenant per day -tenant.apikey.maxCalls=100000 +# Warn this many days before an API key expires +tenant.apikey.rotation.warning.days=7 -# Enable/disable tenant isolation -tenant.isolation.enabled=true +# Prefix for tenant-specific security roles +tenant.security.roles.prefix=unomi_tenant_ ---- === Security Provider Configuration diff --git a/manual/src/main/asciidoc/privacy.adoc b/manual/src/main/asciidoc/privacy.adoc index bbde3c398..3384ba6be 100644 --- a/manual/src/main/asciidoc/privacy.adoc +++ b/manual/src/main/asciidoc/privacy.adoc @@ -34,11 +34,15 @@ geographic location) It is possible to anonymize a profile, meaning it will remove all "identifying" property values from the profile. Basically all properties with the tag `personalIdentifierProperties` will be purged from the profile. + +NOTE: In Unomi 3.1+, `/cxs/privacy/*` endpoints require tenant or administrator authentication. The examples below use a tenant private key; you may also proxy these endpoints and validate the profile cookie server-side as described above. + Here's an example of a request to anonymize a profile: -[source] +[source,bash] ---- -curl -X POST http://localhost:8181/cxs/privacy/profiles/{profileID}/anonymize?scope=ASCOPE +curl -X POST http://localhost:8181/cxs/privacy/profiles/{profileID}/anonymize?scope=ASCOPE \ + --user "TENANT_ID:PRIVATE_KEY" ---- where `{profileID}` must be replaced by the actual identifier of a profile diff --git a/manual/src/main/asciidoc/recipes.adoc b/manual/src/main/asciidoc/recipes.adoc index 5d21c2d4d..fae5bf2c4 100644 --- a/manual/src/main/asciidoc/recipes.adoc +++ b/manual/src/main/asciidoc/recipes.adoc @@ -137,7 +137,7 @@ defining the corresponding JSON schema and you're ready to update profiles using only. Again, prefer the custom events solution because as this is a protected event it will require sending the Unomi key as a request header, and as Unomi only supports a single key for the moment it could be problematic if the key is intercepted. But at least by using an event you will get the benefits of auditing and historical property modification -tracing. +tracing (see <<_request_tracing_explain,request tracing>>). Let's go into more detail about the preferred way to update a profile. Let's consider the following example of a rule: diff --git a/manual/src/main/asciidoc/security.adoc b/manual/src/main/asciidoc/security.adoc index ed9939c61..a46996c62 100644 --- a/manual/src/main/asciidoc/security.adoc +++ b/manual/src/main/asciidoc/security.adoc @@ -81,4 +81,21 @@ end note For tenant API keys, roles (`ROLE_UNOMI_TENANT_USER`, `ROLE_UNOMI_TENANT_ADMIN`, system administrator), and operation permissions, see <<_multitenancy,Multi-tenancy>>. -===== Overview \ No newline at end of file +===== Overview + +==== Authentication overview + +Unomi 3.1 uses a layered authentication model: + +* *Public endpoints* (for example `/cxs/context.json`, `/cxs/eventcollector`): require a tenant **public** API key via the `X-Unomi-Api-Key` header. +* *Private REST endpoints*: require tenant `tenantId:privateApiKey` basic authentication, or JAAS credentials with the `X-Unomi-Tenant-Id` header. +* *System administrator*: Karaf JAAS user (for example `karaf:karaf`) for tenant management and cluster operations. + +Configuration keys live in `etc/org.apache.unomi.rest.authentication.cfg` and `custom.system.properties`. Tenant API key settings are in `etc/org.apache.unomi.tenant.cfg`. + +For endpoint lists, auth examples, and migration from 2.x/3.0, see <<_multitenancy,Multi-tenancy>>, <<_configuration,Tenant management in Configuration>>, and <<_v2_compatibility_mode,V2 compatibility mode>>. + +==== Request tracing + +Administrators with the appropriate roles can enable request tracing with `explain=true` on context and event-collector requests. See <<_request_tracing_explain,Using the explain parameter for request tracing>>. + diff --git a/manual/src/main/asciidoc/useful-unomi-urls.adoc b/manual/src/main/asciidoc/useful-unomi-urls.adoc index 3c5fdf3ea..846296c88 100644 --- a/manual/src/main/asciidoc/useful-unomi-urls.adoc +++ b/manual/src/main/asciidoc/useful-unomi-urls.adoc @@ -84,7 +84,7 @@ where PROFILE_ID is a profile identifier. This will indeed retrieve all the even |/cxs/tasks |GET -|Listing scheduled background tasks (filter with `?status=` or `?type=`) +|Listing scheduled background tasks (see <<_scheduler,Task scheduler>>) (filter with `?status=` or `?type=`) |/cxs/tasks/{taskId} |GET diff --git a/manual/src/main/asciidoc/whats-new.adoc b/manual/src/main/asciidoc/whats-new.adoc index a7481b186..d1a9bba46 100644 --- a/manual/src/main/asciidoc/whats-new.adoc +++ b/manual/src/main/asciidoc/whats-new.adoc @@ -22,7 +22,7 @@ Complete tenant isolation for profiles, events, segments, rules, and schemas. Pu * Operator guide: <<_multitenancy,Multi-tenancy>> * Migration: <<_migrate_from_3_0_to_3_1,Migrate from 3.0 to 3.1>> -* Optional 3.0 compatibility mode: <<_v2_compatibility_mode,V2/V3 compatibility mode>> (property `unomi.v3_0.compatibility.mode`) +* Migrating from Unomi 2.x: <<_v2_compatibility_mode,V2 compatibility mode>> (`v2.compatibilitymode.enabled` in `org.apache.unomi.rest.authentication.cfg`) ==== Cluster-aware task scheduler @@ -30,9 +30,13 @@ Built-in background job scheduler with persistence, cluster locks, recovery, RES * <<_scheduler,Task scheduler>> +[[_opensearch_support]] ==== OpenSearch 3 support -Official alternative to Elasticsearch, including secured Docker deployments. See <<_configuration,Configuration>> and <<_opensearch_support,OpenSearch support>> (3.0 section below). +Official alternative to Elasticsearch, including secured Docker deployments. + +* Configuration: <<_configuration,Configuration>> +* Migration from Elasticsearch: <<_migrate_from_elasticsearch_to_opensearch,Migrate from Elasticsearch to OpenSearch>> ==== Persistence-based clustering @@ -44,59 +48,39 @@ Read-only per-tenant usage metrics and an event retention purge API for upstream * REST: `/cxs/tenants/{tenantId}/usage` and `/cxs/tenants/{tenantId}/purge/events` (see <<_useful_apache_unomi_urls,Useful URLs>>) -=== What's new in Apache Unomi 3.0 - -Apache Unomi 3 is a new release focused on integrations of the client to support elasticsearch version 9. -It also include the upgrade of the Karaf version. - -==== Elasticsearch client upgrade +==== Request tracing -The official client for Elasticsearch has been added to Apache Unomi in version 3.0 in order to replace the old rest-client which -is not supported anymore. +Administrators can trace how context and event-collector requests are processed using `explain=true` on supported endpoints. -The documentation of the client can be found here: https://www.elastic.co/docs/reference/elasticsearch/clients/java +* <<_request_tracing_explain,Using the explain parameter for request tracing>> -=== Elasticsearch 7 data migration - -A procedure to migrate your data from Elasticsearch 7 to Elasticsearch 9 can be found in the <<_migrate_from_elasticsearch_7_to_elasticsearch_9,Migrate from Elasticsearch 7 to Elasticsearch 9>> section +=== What's new in Apache Unomi 3.0 -=== Karaf upgrade +Apache Unomi 3.0 is a major platform release. It upgrades the search-engine client, the OSGi runtime, and the minimum Java version. -The Karaf version has been upgraded from 4.2.15 to 4.4.11 in order to support the latest versions of the dependencies. -This upgrade also brings support for Java 17. +==== Java 17 -[[_opensearch_support]] -==== OpenSearch Support +Unomi 3.0 requires **Java 17** or later. Java 11 is no longer supported. -Starting with version 3.1.0, Apache Unomi now officially supports OpenSearch 3 as an alternative to Elasticsearch. This addition gives users more flexibility in choosing their search engine backend. Key features include: +==== Karaf 4.4 -- Full support for OpenSearch 3 -- Seamless integration with existing Unomi features -- Support for security features enabled by default in OpenSearch -- Compatible with both standalone and Docker deployments +Karaf was upgraded from 4.2.x to **4.4.11** to support current dependencies and Java 17. -===== Configuration Options +==== Elasticsearch 9 Java API client -Users can choose between ElasticSearch and OpenSearch through various configuration methods: +The legacy Elasticsearch REST high-level client was replaced by the official **Elasticsearch 9** Java API client. -1. Using configuration properties -2. Using Maven profiles during build -3. Using Docker environment variables +* Client reference: https://www.elastic.co/docs/reference/elasticsearch/clients/java +* Data migration from Elasticsearch 7: <<_migrate_from_elasticsearch_7_to_elasticsearch_9,Migrate from Elasticsearch 7 to Elasticsearch 9>> -For detailed configuration instructions, see the <<_configuration,configuration section>>. +==== QueryBuilder ID renames -===== Security Considerations +Built-in condition `queryBuilder` IDs were renamed to remove Elasticsearch-specific suffixes. Custom plugins and JSON definitions must be updated. See the mapping table in <<_migrate_from_2_x_to_3_0,Migrate from 2.x to 3.0>>. -When using OpenSearch: -- Security is enabled by default and requires SSL/TLS -- Default admin credentials are required -- The initial admin password can be configured via environment variables +==== Persistence-based clustering -===== Migration Notes +Cluster coordination moved from Karaf Cellar to a persistence-backed node registry (see <<_clustering,Cluster setup>>). -For users wanting to migrate from ElasticSearch to OpenSearch: -- Data migration requires a full cluster shutdown -- All existing features, including the flattened field type, are supported -- Existing queries and aggregations work seamlessly with both backends +==== Health check extension -For detailed migration instructions, refer to the <<_migrations,migration guide>>. +HTTP health endpoint at `/health/check` with pluggable providers (Karaf, search engine, persistence, cluster, Unomi bundles). See <<_health_check,Health check>>. diff --git a/manual/src/main/asciidoc/writing-plugins.adoc b/manual/src/main/asciidoc/writing-plugins.adoc index 43637feb3..f87022c4e 100644 --- a/manual/src/main/asciidoc/writing-plugins.adoc +++ b/manual/src/main/asciidoc/writing-plugins.adoc @@ -1001,7 +1001,7 @@ my-custom-plugin/ ```yaml services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.15.0 + image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3 opensearch: image: opensearchproject/opensearch:3.4.0 ``` diff --git a/rest/README.md b/rest/README.md index b44c62130..2c9d4288a 100644 --- a/rest/README.md +++ b/rest/README.md @@ -15,10 +15,32 @@ ~ limitations under the License. --> -# How to generate the REST API documentation +# Apache Unomi REST API -- Switch to the `rest-documentation` branch, a different branch is need so that we can add the proper `@Path` annotations on the endpoint and add the Maven plugin configuration -for documentation generation. -- Make sure that the changes from master are incorporated to the branch by performing a rebase: `git rebase master`. +## Consumer documentation + +The REST API is served under `/cxs` on the default HTTP port (8181). + +Unomi 3.1 authentication: + +| Access | Credentials | +|--------|-------------| +| Public endpoints (`/cxs/context.json`, `/cxs/eventcollector`, …) | `X-Unomi-Api-Key: ` | +| Tenant private endpoints | Basic auth `tenantId:privateApiKey` | +| System administration (tenants, cluster, tasks, …) | JAAS user (for example `karaf:karaf`) | + +Key manual chapters: + +* `manual/src/main/asciidoc/multitenancy.adoc` — tenants and API keys +* `manual/src/main/asciidoc/request-examples.adoc` — curl examples with auth +* `manual/src/main/asciidoc/useful-unomi-urls.adoc` — endpoint reference +* `manual/src/main/asciidoc/scheduler.adoc` — `/cxs/tasks` API + +Postman collections: see `rest/postman-readme.md`. + +## Generating Miredot API documentation (maintainers) + +- Switch to the `rest-documentation` branch (adds `@Path` annotations and the Maven plugin for doc generation). +- Rebase on master: `git rebase master`. - Run `mvn test`. -- The documentation should now be available via `target/miredot/index.html` \ No newline at end of file +- Open `target/miredot/index.html`. From 356af70b5b387eb07de4e71d658ccaf56cfd5e35 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 08:59:58 +0200 Subject: [PATCH 11/19] UNOMI-960: Add conditions guide and complete builtin type catalogs Add a progressive conditions guide with PlantUML diagrams and REST examples. Document all built-in condition and action types in catalog tables, fix remaining 3.x doc gaps (anchors, query builder naming, healthcheck note, index structure). --- .../main/asciidoc/builtin-action-types.adoc | 102 ++++-- .../asciidoc/builtin-condition-types.adoc | 130 ++++--- .../main/asciidoc/condition-evaluation.adoc | 15 +- .../src/main/asciidoc/conditions-guide.adoc | 335 ++++++++++++++++++ manual/src/main/asciidoc/configuration.adoc | 2 + manual/src/main/asciidoc/datamodel.adoc | 2 +- manual/src/main/asciidoc/index.adoc | 10 +- .../main/asciidoc/past-event-conditions.adoc | 14 +- .../asciidoc/queries-and-aggregations.adoc | 3 + 9 files changed, 514 insertions(+), 99 deletions(-) create mode 100644 manual/src/main/asciidoc/conditions-guide.adoc diff --git a/manual/src/main/asciidoc/builtin-action-types.adoc b/manual/src/main/asciidoc/builtin-action-types.adoc index 7163fc6da..222005095 100644 --- a/manual/src/main/asciidoc/builtin-action-types.adoc +++ b/manual/src/main/asciidoc/builtin-action-types.adoc @@ -11,60 +11,96 @@ // See the License for the specific language governing permissions and // limitations under the License. // + [[_builtin_action_types]] === Built-in action types -Unomi comes with quite a lot of built-in action types. Instead of detailing them one by one you will find here an overview of -what an action type descriptor looks like: +Actions run when a rule matches. Each action type references an `actionExecutor` OSGi service. + +==== Action type descriptor shape [source,json] ---- { "metadata": { - "id": "UNIQUE_IDENTIFIER_STRING", - "name": "DISPLAYABLE_ACTION_NAME", - "description": "DISPLAYABLE_ACTION_DESCRIPTION", - "systemTags": [ - "profileTags", - "event", - "availableToEndUser", - "allowMultipleInstances" - ], + "id": "setPropertyAction", + "name": "setPropertyAction", + "systemTags": ["profileTags", "availableToEndUser"], "readOnly": true }, - "actionExecutor": "ACTION_EXECUTOR_ID", + "actionExecutor": "setProperty", "parameters": [ - ... parameters specific to each action ... + { "id": "setPropertyName", "type": "string" }, + { "id": "setPropertyValue", "type": "string" } ] } ---- -The ACTION_EXECUTOR_ID points to a OSGi Blueprint parameter that is defined when implementing the action in a plugin. -Here's an example of such a registration: - -From https://github.com/apache/unomi/blob/master/plugins/mail/src/main/resources/OSGI-INF/blueprint/blueprint.xml +==== OSGi registration example [source,xml] ---- - - - - - - - - + + + + + + ---- -In the above example the ACTION_EXECUTOR_ID is `sendMail` +==== Catalog + +[cols="2,2,2,4", options="header"] +|=== +| Action type ID | Source | Executor | Parameters / description + +| `addToListsAction` | lists-extension | `addToLists` | Params: listIdentifiers +| `allEventToProfilePropertiesAction` | baseplugin | `allEventToProfileProperties` | Params: — +| `cdpSessionEventAction` | graphql | `cdp_sessionEvent` | Params: state, sessionId, scope +| `copyPropertiesAction` | baseplugin | `copyProperties` | Params: setPropertyStrategy, rootProperty, mandatoryPropTypeSystemTag +| `evaluateProfileAgeAction` | baseplugin | `evaluateProfileAge` | Params: — +| `evaluateProfileSegmentsAction` | baseplugin | `evaluateProfileSegments` | Params: — +| `evaluateVisitPropertiesAction` | baseplugin | `evaluateVisitProperties` | Params: — +| `eventToProfilePropertyAction` | baseplugin | `eventToProfileProperty` | Params: eventPropertyName, profilePropertyName +| `incrementPropertyAction` | baseplugin | `incrementProperty` | Params: propertyName, propertyTarget, storeInSession +| `mergeProfilesOnPropertyAction` | baseplugin | `mergeProfilesOnProperty` | Params: mergeProfilePropertyName, forceEventProfileAsMaster +| `modifyConsentAction` | baseplugin | `modifyConsent` | Modify a profile consent. Params: — +| `requestHeaderToProfilePropertyAction` | request | `requestHeaderToProfileProperty` | Params: requestHeaderName, profilePropertyName, sessionPropertyName, storeAsList +| `requestParameterToProfilePropertyAction` | request | `requestParameterToProfileProperty` | Params: requestParameterName, profilePropertyName, sessionPropertyName, storeAsList +| `sendEventAction` | baseplugin | `sendEvent` | Params: eventType, eventProperties +| `sendMailAction` | mail | `sendMail` | Params: notificationType, notificationTypeId, notifyOncePerProfile, from, to, cc, bcc, subject, template +| `setEventOccurenceCountAction` | baseplugin | `setEventOccurenceCount` | Params: pastEventCondition +| `setPropertyAction` | baseplugin | `setProperty` | Params: setPropertyName, setPropertyValue, setPropertyValueBoolean, setPropertyValueInteger, setPropertyValueMultiple, setPropertyValueCurrentEventTimestamp, setPropertyValueCurrentDate, setPropertyStrategy, storeInSession +| `setRemoteHostInfoAction` | request | `setRemoteHostInfo` | Params: — +| `sfdcCreateOrUpdateLeadAction` | salesforce-connector | `sfdcCreateOrUpdateLead` | Params: — +| `sfdcUpdateProfileFromLeadAction` | salesforce-connector | `sfdcUpdateProfileFromLead` | Params: — +| `updateConsentEventAction` | graphql | `updateConsent` | Params: type, status, lastUpdate, expiration +| `updateListsEventAction` | graphql | `updateLists` | Params: join_lists, leave_lists +| `updatePropertiesAction` | baseplugin | `updateProperties` | Update multiple properties in profile/persona. Params: — +| `weatherUpdateAction` | weather-update | `weatherUpdate` | This action will retrieve the weather associated with the resolved location of the user (from his IP address). Params: — +|=== + +==== Common action groups + +*Profile maintenance* — `setPropertyAction`, `updatePropertiesAction`, `incrementPropertyAction`, `copyPropertiesAction`, `eventToProfilePropertyAction`, `allEventToProfilePropertiesAction`, `mergeProfilesOnPropertyAction` + +*Segmentation & scoring* — `evaluateProfileSegmentsAction`, `evaluateProfileAgeAction`, `evaluateVisitPropertiesAction` + +*Events & consent* — `sendEventAction`, `modifyConsentAction`, `setEventOccurenceCountAction` + +*HTTP request* — `requestHeaderToProfilePropertyAction`, `requestParameterToProfilePropertyAction`, `setRemoteHostInfoAction` (request plugin) + +*Notifications* — `sendMailAction` (mail plugin) + +*Integrations* — `addToListsAction`, Salesforce actions, `weatherUpdateAction`, GraphQL CDP actions (when GraphQL feature is installed) -==== Existing action types descriptors +==== Source locations -Here is a non-exhaustive list of actions built into Apache Unomi. Feel free to browse the source code if you want to -discover more. But the list below should get you started with the most useful actions: +* `plugins/baseplugin/src/main/resources/META-INF/cxs/actions/` +* `plugins/mail/src/main/resources/META-INF/cxs/actions/` +* `plugins/request/src/main/resources/META-INF/cxs/actions/` +* Extension plugins under `extensions/*/src/main/resources/META-INF/cxs/actions/` -- https://github.com/apache/unomi/tree/master/plugins/baseplugin/src/main/resources/META-INF/cxs/actions -- https://github.com/apache/unomi/tree/master/plugins/request/src/main/resources/META-INF/cxs/actions -- https://github.com/apache/unomi/tree/master/plugins/mail/src/main/resources/META-INF/cxs/actions +List action types at runtime: `GET /cxs/definitions/actions` (tenant private key or admin). -Of course it is also possible to build your own custom actions by developing custom Unomi plugins/extensions. \ No newline at end of file +See <<_writing_plugins,Writing plugins>> to implement custom actions. diff --git a/manual/src/main/asciidoc/builtin-condition-types.adoc b/manual/src/main/asciidoc/builtin-condition-types.adoc index 247d5252c..32398f22a 100644 --- a/manual/src/main/asciidoc/builtin-condition-types.adoc +++ b/manual/src/main/asciidoc/builtin-condition-types.adoc @@ -15,8 +15,10 @@ [[_builtin_condition_types]] === Built-in condition types -Apache Unomi comes with an extensive collection of built-in condition types. Instead of detailing them one by one you will -find here an overview of what a JSON condition descriptor looks like: +Apache Unomi ships with built-in condition types in the base plugin and optional extensions. +For a hands-on introduction with examples, see <<_conditions_guide,Conditions guide>>. + +==== Condition type descriptor shape [source,json] ---- @@ -24,67 +26,47 @@ find here an overview of what a JSON condition descriptor looks like: "metadata": { "id": "booleanCondition", "name": "booleanCondition", - "description": "", - "systemTags": [ - "profileTags", - "logical", - "condition", - "profileCondition", - "eventCondition", - "sessionCondition", - "sourceEventCondition" - ], + "description": "Combines multiple conditions with a logical operator", + "systemTags": ["logical", "profileCondition", "eventCondition"], "readOnly": true }, "conditionEvaluator": "booleanConditionEvaluator", "queryBuilder": "booleanConditionQueryBuilder", "parameters": [ - { - "id": "operator", - "type": "String", - "multivalued": false, - "defaultValue": "and" - }, - { - "id": "subConditions", - "type": "Condition", - "multivalued": true - } + { "id": "operator", "type": "string", "defaultValue": "and" }, + { "id": "subConditions", "type": "Condition", "multivalued": true } ] } ---- -Note that condition types have three important identifiers: +Each condition type provides: -- conditionEvaluator: For real-time condition evaluation -- queryBuilder: For building search engine queries (either ElasticSearch or OpenSearch) +* `conditionEvaluator` — real-time evaluation against profile, session, and/or event +* `queryBuilder` — converts the condition to an Elasticsearch or OpenSearch query (Unomi 3.0+ uses generic `*QueryBuilder` IDs) -This is because condition types can be used in three ways: -1. To evaluate a condition in real time -2. To build ElasticSearch queries -3. To build OpenSearch queries +Some types use `parentCondition` to specialize a base type (for example `eventTypeCondition` extends `eventPropertyCondition`). -When implementing a new condition type, you need to provide implementations for each use case. Here's an example of OSGi Blueprint registrations for the above condition type descriptor: +==== Plugin registration (Unomi 3.0+) [source,xml] ---- - + - + - + - + - + @@ -93,19 +75,63 @@ When implementing a new condition type, you need to provide implementations for ---- -As you can see, three Java classes are used to build a single condition type: -1. A condition evaluator (shared) -2. An ElasticSearch query builder -3. An OpenSearch query builder - -You don't need to understand all these details to use condition types, but this might be interesting if you're building your own condition type implementations. For more details on building custom plugins/extensions with search engine specific implementations, please refer to the corresponding sections. - -==== Existing condition type descriptors - -Here is a non-exhaustive list of condition types built into Apache Unomi. Feel free to browse the source code if you want to discover more. The list below should get you started with the most useful conditions: - -- https://github.com/apache/unomi/tree/master/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions - -Of course it is also possible to build your own custom condition types by developing custom Unomi plugins/extensions. - -You will also note that some condition types can re-use a `parentCondition`. This is a way to inherit from another condition type to make them more specific. +==== Catalog — base plugin + +[cols="2,3,2,4", options="header"] +|=== +| Condition type ID | Category | Parent | Key parameters / notes + +| `booleanCondition` | Event | — | Combines multiple conditions with a logical operator. Params: operator, subConditions +| `eventPropertyCondition` | Event | — | Params: propertyName, comparisonOperator, propertyValue, propertyValueInteger, propertyValueDouble, propertyValueDate, propertyValueDateExpr, propertyValues +| `eventTypeCondition` | Event | eventPropertyCondition | Params: eventTypeId +| `formEventCondition` | Event | booleanCondition | Params: formId, pagePath +| `geoLocationByPointSessionCondition` | Session | — | Params: type, rectLatitudeNE, rectLongitudeNE, rectLatitudeSW, rectLongitudeSW, circleLatitude, circleLongitude, distance +| `geoLocationSessionCondition` | Session | booleanCondition | Params: country, admin1, admin2, city +| `goalMatchCondition` | Profile | profilePropertyCondition | Params: goalId, comparisonOperator +| `idsCondition` | Other | — | Params: ids, match +| `matchAllCondition` | Event | — | Params: — +| `modifyAnyConsentEventCondition` | Event | eventTypeCondition | Used to match any consent modification. Params: — +| `modifyConsentEventCondition` | Event | booleanCondition | Used to match status modifications on a specific consent. Params: consentTypeId, consentStatus +| `nestedCondition` | Session | — | Evaluates a condition on nested items within a profile or session property. Params: path, subCondition +| `newVisitorCondition` | Session | booleanCondition | Params: since +| `notCondition` | Event | — | Params: subCondition +| `pastEventCondition` | Profile | — | Evaluates past events that occurred within a specified time range. Params: eventCondition, operator, numberOfDays, fromDate, toDate, minimumEventCount, maximumEventCount +| `profileAliasesPropertyCondition` | Other | — | Params: propertyName, comparisonOperator, propertyValue +| `profilePropertyCondition` | Profile | — | Evaluates a profile property against various comparison criteria. Params: propertyName, comparisonOperator, propertyValue, propertyValueInteger, propertyValueDouble, propertyValueDate, propertyValueDateExpr, propertyValues +| `profileSegmentCondition` | Profile | profilePropertyCondition | Params: segments, matchType +| `profileUpdatedEventCondition` | Event | eventTypeCondition | Params: — +| `profileUserListCondition` | Profile | profilePropertyCondition | Params: lists, matchType +| `returningVisitorCondition` | Session | booleanCondition | Params: since +| `scoringCondition` | Profile | profilePropertyCondition | Params: scoringPlanId, scoreValue, comparisonOperator +| `sessionCreatedEventCondition` | Event | eventTypeCondition | Params: — +| `sessionDurationCondition` | Session | sessionPropertyCondition | Params: minimumDuration, maximumDuration +| `sessionPropertyCondition` | Session | — | Params: propertyName, comparisonOperator, propertyValue, propertyValueInteger, propertyValueDouble, propertyValueDate, propertyValueDateExpr, propertyValues +| `topicPropertyCondition` | Other | — | Params: propertyName, comparisonOperator, propertyValue +| `updatePropertiesEventCondition` | Event | eventTypeCondition | Params: — +| `videoViewEventCondition` | Event | booleanCondition | Params: videoId, pagePath +|=== + +==== Catalog — extensions + +[cols="2,2,4", options="header"] +|=== +| Condition type ID | Source | Key parameters / notes +| `cdpSessionEventCondition` | graphql | Params: — +| `hoverEventCondition` | hover-event | Params: targetId, targetPath +| `sourceEventPropertyCondition` | advanced-conditions | Params: id, path, scope, type +| `updateConsentEventCondition` | graphql | Params: — +| `updateListsEventCondition` | graphql | Params: — +| `userListPropertyCondition` | graphql | Params: propertyName, comparisonOperator, propertyValue, propertyValues +|=== + +==== Source locations + +Condition descriptors are JSON files under `META-INF/cxs/conditions/` in each plugin, for example: + +* `plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/` +* `plugins/advanced-conditions/src/main/resources/META-INF/cxs/conditions/` +* `plugins/hover-event/src/main/resources/META-INF/cxs/conditions/` + +GraphQL-specific condition types ship with the GraphQL feature (`graphql/cxs-impl/...`). + +Custom condition types can be added in your own plugins; see <<_writing_plugins,Writing plugins>>. diff --git a/manual/src/main/asciidoc/condition-evaluation.adoc b/manual/src/main/asciidoc/condition-evaluation.adoc index 04bc6b661..3103b973b 100644 --- a/manual/src/main/asciidoc/condition-evaluation.adoc +++ b/manual/src/main/asciidoc/condition-evaluation.adoc @@ -11,8 +11,11 @@ // See the License for the specific language governing permissions and // limitations under the License. // +[[_condition_evaluation]] === Condition Evaluation System +For examples and progressive tutorials, see <<_conditions_guide,Conditions guide>>. + The condition evaluation system in Apache Unomi provides flexible and efficient evaluation of conditions across different storage backends. ==== Architecture Overview @@ -32,8 +35,8 @@ skinparam component { package "Condition Evaluation" { [ConditionDispatcher] <> [ConditionEvaluator] <> - [ConditionESQueryBuilder] <> - [ConditionOSQueryBuilder] <> + [ConditionQueryBuilder] <> + [ConditionQueryBuilder] <> } package "Query Building" { @@ -50,11 +53,11 @@ package "Storage" { [ConditionDispatcher] --> [ConditionEvaluator] [ConditionDispatcher] --> QB -QB <|.. [ConditionESQueryBuilder] -QB <|.. [ConditionOSQueryBuilder] +QB <|.. [ConditionQueryBuilder] +QB <|.. [ConditionQueryBuilder] -[ConditionESQueryBuilder] --> [ElasticSearchQueryBuilder] -[ConditionOSQueryBuilder] --> [OpenSearchQueryBuilder] +[ConditionQueryBuilder] --> [ElasticSearchQueryBuilder] +[ConditionQueryBuilder] --> [OpenSearchQueryBuilder] [ElasticSearchQueryBuilder] --> [ElasticSearch] [OpenSearchQueryBuilder] --> [OpenSearch] diff --git a/manual/src/main/asciidoc/conditions-guide.adoc b/manual/src/main/asciidoc/conditions-guide.adoc new file mode 100644 index 000000000..0d9802257 --- /dev/null +++ b/manual/src/main/asciidoc/conditions-guide.adoc @@ -0,0 +1,335 @@ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +[[_conditions_guide]] +=== Conditions guide + +Conditions are the building blocks of segmentation, personalization, rules, queries, campaigns, and goals in Apache Unomi. +This guide walks from simple property checks to advanced behavioral conditions with diagrams and REST examples. + +See also: + +* <<_builtin_condition_types,Built-in condition types>> — full catalog +* <<_condition_evaluation,Condition evaluation system>> — runtime architecture +* <<_past_event_conditions,Past event conditions>> — deep dive on behavioral conditions +* <<_queries_and_aggregations,Queries and aggregations>> — using conditions in search APIs + +==== Concepts + +* *Condition type* — a reusable definition (JSON descriptor) registered at startup, for example `profilePropertyCondition`. +* *Condition instance* — a JSON object with `type` and `parameterValues` used in segments, rules, or queries. +* *Evaluation context* — profile, session, and/or current event, depending on where the condition runs. + +[plantuml] +---- +@startuml +skinparam componentStyle uml2 + +package "Definitions" { + [Condition types\n(JSON descriptors)] as CT +} + +package "Consumers" { + [Segments] as Seg + [Rules] as Rule + [Queries] as Q + [Goals / Campaigns] as GC +} + +package "Runtime" { + [ConditionDispatcher] as CD + [ConditionEvaluator] as CE + [QueryBuilder\n(ES or OS)] as QB +} + +database "Search index" as IDX + +CT --> Seg +CT --> Rule +CT --> Q +CT --> GC + +Seg --> CD +Rule --> CD +Q --> CD + +CD --> CE : direct evaluation\n(profile / session / event) +CD --> QB : query-based evaluation\n(segments, counts) +QB --> IDX + +@enduml +---- + +==== Condition instance JSON shape + +Every condition instance uses the same structure: + +[source,json] +---- +{ + "type": "CONDITION_TYPE_ID", + "parameterValues": { + "parameterName": "value" + } +} +---- + +Nested conditions use the `Condition` parameter type (for example `subConditions` on `booleanCondition`). + +==== Comparison operators + +Property conditions (`profilePropertyCondition`, `sessionPropertyCondition`, `eventPropertyCondition`) accept a `comparisonOperator` parameter. +Common values include: + +`equals`, `notEquals`, `lessThan`, `greaterThan`, `lessThanOrEqualTo`, `greaterThanOrEqualTo`, `between`, `exists`, `missing`, `contains`, `notContains`, `startsWith`, `endsWith`, `matchesRegex`, `in`, `notIn`, `all`, `hasSomeOf`, `hasNoneOf`, `isDay`, `isNotDay`, `distance`. + +Use the typed value parameter that matches your data (`propertyValue`, `propertyValueInteger`, `propertyValues`, and so on). + +==== Level 1 — Profile property check + +Match profiles where a property equals a value. Used in segments and query counts. + +[source,json] +---- +{ + "type": "profilePropertyCondition", + "parameterValues": { + "propertyName": "properties.country", + "comparisonOperator": "equals", + "propertyValue": "US" + } +} +---- + +Test with the query count API: + +[source,bash] +---- +curl -X POST http://localhost:8181/cxs/query/profile/count \ + --user "TENANT_ID:PRIVATE_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "profilePropertyCondition", + "parameterValues": { + "propertyName": "properties.country", + "comparisonOperator": "equals", + "propertyValue": "US" + } + }' +---- + +==== Level 2 — Event type in a rule + +Rules evaluate *event conditions* against the incoming event. `eventTypeCondition` is a shortcut over `eventPropertyCondition` on `eventType`. + +[source,json] +---- +{ + "type": "eventTypeCondition", + "parameterValues": { + "eventTypeId": "view" + } +} +---- + +Use this in a rule `condition` to fire when a `view` event arrives. + +[plantuml] +---- +@startuml +actor Client +participant "REST /cxs/eventcollector" as API +participant "Rule engine" as Rules +database "Profile + events" as Store + +Client -> API : POST event (type=view) +API -> Rules : evaluate rule condition +Rules -> Rules : eventTypeCondition matches? +Rules -> Store : execute actions if true +@enduml +---- + +==== Level 3 — Boolean AND / OR + +Combine conditions with `booleanCondition`. This is the main way to build segment definitions. + +[source,json] +---- +{ + "type": "booleanCondition", + "parameterValues": { + "operator": "and", + "subConditions": [ + { + "type": "profilePropertyCondition", + "parameterValues": { + "propertyName": "properties.nbOfVisits", + "comparisonOperator": "greaterThan", + "propertyValueInteger": 1 + } + }, + { + "type": "profilePropertyCondition", + "parameterValues": { + "propertyName": "properties.country", + "comparisonOperator": "equals", + "propertyValue": "US" + } + } + ] + } +} +---- + +Negate a sub-condition with `notCondition`: + +[source,json] +---- +{ + "type": "notCondition", + "parameterValues": { + "subCondition": { + "type": "profileSegmentCondition", + "parameterValues": { + "segments": ["opt-out-segment"], + "matchType": "in" + } + } + } +} +---- + +==== Level 4 — Segment membership and session properties + +`profileSegmentCondition` checks whether a profile belongs to other segments (useful for segment hierarchies). + +[source,json] +---- +{ + "type": "profileSegmentCondition", + "parameterValues": { + "segments": ["high-value-customers"], + "matchType": "in" + } +} +---- + +`sessionPropertyCondition` evaluates session-scoped properties during event processing: + +[source,json] +---- +{ + "type": "sessionPropertyCondition", + "parameterValues": { + "propertyName": "properties.referringCampaign", + "comparisonOperator": "exists" + } +} +---- + +==== Level 5 — Past events (behavioral) + +`pastEventCondition` checks whether events occurred in a time window. See <<_past_event_conditions,Past event conditions>> for caching, auto-rules, and performance notes. + +[source,json] +---- +{ + "type": "pastEventCondition", + "parameterValues": { + "numberOfDays": 30, + "operator": "eventsOccurred", + "minimumEventCount": 3, + "eventCondition": { + "type": "eventTypeCondition", + "parameterValues": { + "eventTypeId": "purchase" + } + } + } +} +---- + +==== Level 6 — Scoring, geo, and nested data + +*Scoring* — compare a profile score from a scoring plan: + +[source,json] +---- +{ + "type": "scoringCondition", + "parameterValues": { + "scoringPlanId": "engagement", + "comparisonOperator": "greaterThan", + "scoreValue": 50 + } +} +---- + +*Geo (session)* — `geoLocationSessionCondition` filters on country/region/city; `geoLocationByPointSessionCondition` uses map coordinates. + +*Nested properties* — `nestedCondition` evaluates a sub-condition on a nested path (for example list items inside profile properties). + +==== Using conditions in segments and rules + +Segments store a condition on the segment definition. Rules store a condition that is evaluated for each incoming event. + +[plantuml] +---- +@startuml +|Segment definition| +start +:Store condition JSON; +:Scheduler / manual refresh; +:QueryBuilder converts condition\nto search index query; +:Matching profile IDs updated; + +|Rule definition| +start +:Event arrives; +:ConditionEvaluator tests\nevent + profile + session; +if (matches?) then (yes) + :Run actions; +else (no) + :Skip rule; +endif +@enduml +---- + +==== Testing conditions + +*Query count* — verify how many profiles match (see Level 1 example). + +*Segment membership* — after creating a segment with your condition, test a profile: + +[source,bash] +---- +curl -X GET "http://localhost:8181/cxs/segments/SEGMENT_ID/match/PROFILE_ID" \ + --user "TENANT_ID:PRIVATE_KEY" +---- + +==== Discovering available condition types + +List registered condition types (admin or tenant private key): + +[source,bash] +---- +curl -X GET http://localhost:8181/cxs/definitions/conditions \ + --user "TENANT_ID:PRIVATE_KEY" +---- + +Each entry includes parameter definitions, evaluators, and query builder IDs (Unomi 3.0+ uses engine-neutral `*QueryBuilder` names). + +==== Custom condition types + +To add new condition types, implement a `ConditionEvaluator` and search-engine-specific query builders. See <<_writing_plugins,Writing plugins>> and <<_builtin_condition_types,plugin registration example>>. diff --git a/manual/src/main/asciidoc/configuration.adoc b/manual/src/main/asciidoc/configuration.adoc index 6d0b7369c..461d0357f 100644 --- a/manual/src/main/asciidoc/configuration.adoc +++ b/manual/src/main/asciidoc/configuration.adoc @@ -1474,6 +1474,8 @@ Be aware that in distribution's feature, you should only reference other feature unomi-wab unomi-web-tracker unomi-healthcheck-elasticsearch +NOTE: For OpenSearch distributions, use `unomi-healthcheck-opensearch` instead of `unomi-healthcheck-elasticsearch` in custom feature XML. + unomi-router-karaf-feature unomi-groovy-actions unomi-rest-ui diff --git a/manual/src/main/asciidoc/datamodel.adoc b/manual/src/main/asciidoc/datamodel.adoc index 0e2193d29..bfe46de2f 100755 --- a/manual/src/main/asciidoc/datamodel.adoc +++ b/manual/src/main/asciidoc/datamodel.adoc @@ -746,7 +746,7 @@ The result of a condition is always a boolean value of true or false. Apache Unomi provides quite a lot of built-in condition types, including boolean types that make it possible to compose conditions using operators such as `and`, `or` or `not`. Composition is an essential element of building more complex conditions. -For a more complete list of available condition types, see the <<_builtin_condition_types,Built-in condition types>> reference section. +For tutorials and examples, see <<_conditions_guide,Conditions guide>>. For the full catalog, see <<_builtin_condition_types,Built-in condition types>>. ==== Structure definition diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc index ef29dfc26..6304c550e 100644 --- a/manual/src/main/asciidoc/index.adoc +++ b/manual/src/main/asciidoc/index.adoc @@ -72,6 +72,15 @@ include::graphql-examples.adoc[] include::migrations/migrations.adoc[] +[[_conditions]] +== Conditions + +include::conditions-guide.adoc[] + +include::condition-evaluation.adoc[] + +include::past-event-conditions.adoc[] + == Queries and aggregations include::queries-and-aggregations.adoc[] @@ -135,7 +144,6 @@ include::builtin-action-types.adoc[] include::updating-events.adoc[] -include::past-event-conditions.adoc[] include::web-tracker.adoc[] diff --git a/manual/src/main/asciidoc/past-event-conditions.adoc b/manual/src/main/asciidoc/past-event-conditions.adoc index 58abdf7bd..379d26aff 100644 --- a/manual/src/main/asciidoc/past-event-conditions.adoc +++ b/manual/src/main/asciidoc/past-event-conditions.adoc @@ -12,6 +12,7 @@ // limitations under the License. // +[[_past_event_conditions]] === Past Event Conditions in Apache Unomi :toc: macro :toclevels: 4 @@ -405,11 +406,12 @@ skinparam component { package "Past Event System" { [SetEventOccurenceCountAction] as action [PastEventConditionEvaluator] as evaluator - [PastEventConditionESQueryBuilder] as queryBuilder + [pastEventConditionQueryBuilder] as queryBuilder [SegmentServiceImpl] as segmentService } -database "Elasticsearch" { +database "Search index +(Elasticsearch or OpenSearch)" { [Events] as events [Profiles] as profiles } @@ -749,7 +751,7 @@ The past event condition system consists of four main components: 1. `SetEventOccurenceCountAction` - Handles real-time event processing and updates profile event counts 2. `PastEventConditionEvaluator` - Manages condition evaluation with optimized caching -3. `PastEventConditionESQueryBuilder` - Constructs optimized Elasticsearch queries +3. `pastEventConditionQueryBuilder` - Constructs optimized search index queries 4. `SegmentServiceImpl` - Manages segments and auto-generated rules == Condition Parameters @@ -837,7 +839,7 @@ When an event occurs in Unomi, the `SetEventOccurenceCountAction` processes it i * The original event condition * Time window constraints * Profile filters -3. Queries existing matching events using optimized Elasticsearch queries +3. Queries existing matching events using optimized search index queries 4. Updates the profile's past event count in system properties 5. Triggers profile update events if configured @@ -937,8 +939,8 @@ The evaluation process involves multiple components working together to efficien ** For `eventsOccurred`: Verifies count is between `minimumEventCount` and `maximumEventCount` ** For `eventsNotOccurred`: Verifies count is exactly 0 -2. *Query Builder* (`PastEventConditionESQueryBuilder`) - * Responsible for constructing optimized Elasticsearch queries +2. *Query Builder* (`pastEventConditionQueryBuilder`) + * Responsible for constructing optimized search index queries * Supports two query strategies: ** Property-based queries: Uses cached counts from profile properties ** Event-based queries: Aggregates events directly when needed diff --git a/manual/src/main/asciidoc/queries-and-aggregations.adoc b/manual/src/main/asciidoc/queries-and-aggregations.adoc index aa536a03c..2c9a8a68d 100644 --- a/manual/src/main/asciidoc/queries-and-aggregations.adoc +++ b/manual/src/main/asciidoc/queries-and-aggregations.adoc @@ -11,9 +11,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // +[[_queries_and_aggregations]] Apache Unomi contains a `query` endpoint that is quite powerful. It provides ways to perform queries that can quickly get result counts, apply metrics such as sum/min/max/avg or even use powerful aggregations. +Conditions used in queries follow the same JSON shape described in <<_conditions_guide,Conditions guide>>. + In this section we will show examples of requests that may be built using this API. === Query counts From 7e01245c241d01469b5996d2fdcf216eaff5e286 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 09:10:32 +0200 Subject: [PATCH 12/19] UNOMI-960: Document condition validation and debugging in manual Add a condition validation chapter, a debugging section in the conditions guide, and cross-links from whats-new and request tracing docs. --- .../asciidoc/builtin-condition-types.adoc | 18 +- .../main/asciidoc/condition-validation.adoc | 251 ++++++++++++++++++ .../src/main/asciidoc/conditions-guide.adoc | 65 +++++ manual/src/main/asciidoc/index.adoc | 2 + .../src/main/asciidoc/request-examples.adoc | 2 + manual/src/main/asciidoc/whats-new.adoc | 8 + 6 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 manual/src/main/asciidoc/condition-validation.adoc diff --git a/manual/src/main/asciidoc/builtin-condition-types.adoc b/manual/src/main/asciidoc/builtin-condition-types.adoc index 32398f22a..2b46ec487 100644 --- a/manual/src/main/asciidoc/builtin-condition-types.adoc +++ b/manual/src/main/asciidoc/builtin-condition-types.adoc @@ -17,6 +17,7 @@ Apache Unomi ships with built-in condition types in the base plugin and optional extensions. For a hands-on introduction with examples, see <<_conditions_guide,Conditions guide>>. +For save-time parameter rules (`required`, `allowedValues`, exclusive groups), see <<_condition_validation,Condition validation>>. ==== Condition type descriptor shape @@ -33,12 +34,25 @@ For a hands-on introduction with examples, see <<_conditions_guide,Conditions gu "conditionEvaluator": "booleanConditionEvaluator", "queryBuilder": "booleanConditionQueryBuilder", "parameters": [ - { "id": "operator", "type": "string", "defaultValue": "and" }, - { "id": "subConditions", "type": "Condition", "multivalued": true } + { + "id": "operator", + "type": "string", + "defaultValue": "and", + "validation": { "required": true, "allowedValues": ["and", "or"] } + }, + { + "id": "subConditions", + "type": "Condition", + "multivalued": true, + "validation": { "required": true } + } ] } ---- +Parameters may include a `validation` object (see <<_condition_validation,Condition validation>>). +Real descriptors in the base plugin use this heavily — for example `pastEventCondition` requires an `eventCondition` sub-condition tagged `eventCondition`. + Each condition type provides: * `conditionEvaluator` — real-time evaluation against profile, session, and/or event diff --git a/manual/src/main/asciidoc/condition-validation.adoc b/manual/src/main/asciidoc/condition-validation.adoc new file mode 100644 index 000000000..93e78f16a --- /dev/null +++ b/manual/src/main/asciidoc/condition-validation.adoc @@ -0,0 +1,251 @@ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +[[_condition_validation]] +=== Condition validation + +Apache Unomi validates condition instances against their condition type definitions when you save segments, rules, queries, goals, and related items. +This catches missing parameters, wrong value types, and invalid nested conditions *before* they reach runtime evaluation. + +NOTE: This is separate from <<_json_schemas,JSON Schema event validation>>. +JSON schemas validate incoming events on public endpoints. +Condition validation checks the structure of condition JSON used in segments, rules, and queries. + +See also: + +* <<_conditions_guide,Conditions guide>> — hands-on introduction +* <<_builtin_condition_types,Built-in condition types>> — descriptor format and catalog +* <<_request_tracing_explain,Request tracing with explain>> — see validation results in trace output + +==== When validation runs + +Validation runs on the server when a condition is saved or resolved, not on every profile lookup or event. + +Typical entry points: + +* *Segments* — saving or updating a segment condition (`SegmentServiceImpl`) +* *Rules* — saving or updating a rule condition (`RulesServiceImpl`) +* *Queries* — saving queries used in profiles, events, goals, and campaigns +* *Goals* — start and target event conditions +* *Condition type resolution* — when `DefinitionsService` resolves a condition tree (deprecated path still used in some flows) + +There is *no* standalone REST endpoint such as `/cxs/conditions/validate`. +To test a condition, save it on a segment or rule and read the error response, or use <<_debugging_conditions,debugging with explain>> when the condition is evaluated at runtime. + +==== Validation metadata on condition types + +Each parameter in a condition type descriptor can include a `validation` object. +The base plugin ships examples in `plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/*.json`. + +Supported fields: + +[cols="1,2,1", options="header"] +|=== +| Field | Purpose | Example use + +| `required` +| Parameter must have a literal value at save time +| `propertyName` on `profilePropertyCondition` + +| `recommended` +| Parameter is optional but advised; produces a warning, not a blocking error +| `minimumEventCount` on `pastEventCondition` + +| `allowedValues` +| String value must be one of the listed values +| `operator` on `pastEventCondition` (`eventsOccurred`, `eventsNotOccurred`) + +| `exclusive` + `exclusiveGroup` +| Only one parameter in the group may have a value +| `propertyValue` vs `propertyValueInteger` on `profilePropertyCondition` + +| `allowedConditionTags` +| Nested condition must carry at least one of these system tags +| `eventCondition` tag required for `eventCondition` on `pastEventCondition` + +| `disallowedConditionTypes` +| Nested condition type ID must not be in this list +| Restrict which sub-conditions a composite type accepts +|=== + +Example from `pastEventCondition`: + +[source,json] +---- +{ + "id": "eventCondition", + "type": "Condition", + "validation": { + "required": true, + "allowedConditionTags": ["eventCondition"] + } +} +---- + +Example of mutually exclusive value parameters from `profilePropertyCondition`: + +[source,json] +---- +{ + "id": "propertyValue", + "type": "string", + "validation": { + "exclusive": true, + "exclusiveGroup": "propertyValue" + } +}, +{ + "id": "propertyValueInteger", + "type": "integer", + "validation": { + "exclusive": true, + "exclusiveGroup": "propertyValue" + } +} +---- + +==== Parameter type checks + +Before rule-specific checks, the validation service verifies that each literal value matches the parameter `type` and `multivalued` flag. + +Built-in type validators cover: + +* `string`, `integer`, `long`, `float`, `double`, `boolean`, `date` +* `condition` — nested condition instances (validated recursively) +* `comparisonOperator` — custom validator for comparison operators + +Plugins can register additional `ValueTypeValidator` OSGi services for custom parameter types. + +==== Contextual parameters (partial validation) + +Parameters whose values start with `parameter::` or `script::` are resolved at evaluation time, not at save time. +The validation service skips most checks on these values because the final value is unknown when the item is saved. + +Exceptions: + +* A *required* parameter that uses a contextual reference still produces a *warning* (`MISSING_RECOMMENDED_PARAMETER`) advising that static validation was skipped. +* Nested conditions inside contextual values are not validated until runtime. + +This lets rules and segments use dynamic values while still validating fully literal condition trees. + +==== Error types + +Validation returns a list of `ValidationError` objects. +Each error has a `type`, a human-readable `message`, the `parameterName`, and optional context (location, allowed values, actual value). + +[cols="1,2,1", options="header"] +|=== +| Type | Meaning | Blocks save? + +| `MISSING_REQUIRED_PARAMETER` +| Required parameter is absent or an empty collection +| Yes + +| `INVALID_VALUE` +| Value has wrong type, shape, or is not in `allowedValues` +| Yes + +| `INVALID_CONDITION_TYPE` +| Unknown condition type, or nested condition fails tag/type rules +| Yes + +| `EXCLUSIVE_PARAMETER_VIOLATION` +| More than one parameter in an exclusive group has a value +| Yes + +| `MISSING_RECOMMENDED_PARAMETER` +| Recommended parameter is missing, or required parameter uses a contextual reference +| No (logged as warning) +|=== + +==== Save-time behavior + +On save, services separate *errors* from *warnings*: + +* *Errors* — any type except `MISSING_RECOMMENDED_PARAMETER` — cause the save to fail with an `IllegalArgumentException` or domain-specific exception (for example `BadSegmentConditionException` for segments). +* *Warnings* — `MISSING_RECOMMENDED_PARAMETER` only — are written to the server log but do not block the save. + +Example error message when saving a segment with a missing required parameter: + +[source] +---- +Invalid segment condition: +- Required parameter is missing (parameter: propertyName, condition: profilePropertyCondition, location: condition[profilePropertyCondition].propertyName) +---- + +REST clients receive this text in the error response body when the save fails. + +==== Rules and invalid conditions + +By default, a rule with validation errors cannot be saved. +During bundle deployment or when a rule references a missing plugin, Unomi may save the rule with `allowInvalidRules` semantics: validation errors are logged and the rule is marked invalid instead of rejected. +This supports gradual plugin rollout without blocking server startup. + +==== Validation in request tracing + +When `explain=true` is set on context or event-collector requests, validation steps appear in the `requestTracing` tree. +Look for operation types such as: + +* `condition-validation` — validation during condition type resolution +* `segment-condition-validation` — segment save path (when triggered in that flow) +* `rule-condition-validation` — rule condition checks + +Trace nodes include validation messages and whether validation passed. +See <<_request_tracing_explain,Using the explain parameter for request tracing>> for full examples. + +==== Adding validation to custom condition types + +When you define a new condition type in a plugin: + +1. Declare parameters with correct `type` and `multivalued` flags. +2. Add a `validation` block on parameters that need constraints. +3. Use `allowedConditionTags` on nested `Condition` parameters to restrict which sub-types users can pick. +4. Use `exclusive` / `exclusiveGroup` when several parameters represent alternative value shapes (string vs integer vs date). +5. Register a custom `ValueTypeValidator` OSGi service if you introduce a new parameter type ID. + +Condition type descriptors are loaded at startup. +After deployment, list types to confirm your validation metadata is present: + +[source,bash] +---- +curl -X GET http://localhost:8181/cxs/definitions/conditions \ + --user "TENANT_ID:PRIVATE_KEY" +---- + +Each condition type entry in the response includes parameter definitions with their `validation` objects, which UIs and integrators can use to guide authors before save. + +==== Relation to JSON Schema validation + +[cols="1,2,2", options="header"] +|=== +| | Condition validation | JSON Schema validation + +| What is validated +| Condition instances in segments, rules, queries +| Event payloads on `/cxs/context.json` and `/cxs/eventcollector` + +| When +| Save time (server-side) +| Request time (public endpoints) + +| Defined in +| Condition type JSON descriptors (`META-INF/cxs/conditions/`) +| JSON schema definitions (`META-INF/cxs/json-schema/`) + +| REST test endpoint +| None (save the parent item instead) +| `/cxs/jsonSchema/validateEvent` +|=== + +Both systems can appear in the same `explain=true` trace: event schema validation under `event-validation`, condition checks under `condition-evaluation` or `condition-validation`. diff --git a/manual/src/main/asciidoc/conditions-guide.adoc b/manual/src/main/asciidoc/conditions-guide.adoc index 0d9802257..652145bed 100644 --- a/manual/src/main/asciidoc/conditions-guide.adoc +++ b/manual/src/main/asciidoc/conditions-guide.adoc @@ -21,9 +21,11 @@ This guide walks from simple property checks to advanced behavioral conditions w See also: * <<_builtin_condition_types,Built-in condition types>> — full catalog +* <<_condition_validation,Condition validation>> — save-time checks and parameter rules * <<_condition_evaluation,Condition evaluation system>> — runtime architecture * <<_past_event_conditions,Past event conditions>> — deep dive on behavioral conditions * <<_queries_and_aggregations,Queries and aggregations>> — using conditions in search APIs +* <<_debugging_conditions,Debugging conditions>> — tracing and troubleshooting ==== Concepts @@ -333,3 +335,66 @@ Each entry includes parameter definitions, evaluators, and query builder IDs (Un ==== Custom condition types To add new condition types, implement a `ConditionEvaluator` and search-engine-specific query builders. See <<_writing_plugins,Writing plugins>> and <<_builtin_condition_types,plugin registration example>>. + +Add `validation` metadata on parameters so invalid instances are rejected at save time — see <<_condition_validation,Condition validation>>. + +[[_debugging_conditions]] +==== Debugging conditions + +When a segment or rule does not behave as expected, use these approaches in order. + +===== Test segment membership + +Check whether a specific profile matches a segment condition: + +[source,bash] +---- +curl -X GET "http://localhost:8181/cxs/segments/SEGMENT_ID/match/PROFILE_ID" \ + --user "TENANT_ID:PRIVATE_KEY" +---- + +===== Count matching profiles + +Use a query with your condition and read the total hit count (see Level 1 in this guide). + +===== Request tracing with explain + +For runtime debugging — which rules fired, which conditions matched, how long each step took — add `explain=true` to context or event-collector requests. +You need an `ADMINISTRATOR` or `TENANT_ADMINISTRATOR` role. + +[source,bash] +---- +curl -X POST "http://localhost:8181/cxs/context.json?sessionId=1234&explain=true" \ + -H "X-Unomi-Api-Key: YOUR_PUBLIC_API_KEY" \ + -H "Content-Type: application/json" \ + -d @- <<'EOF' +{ + "source": { "itemId": "homepage", "itemType": "page", "scope": "mydigital" }, + "requireSegments": true +} +EOF +---- + +The response includes a `requestTracing` tree with nodes such as `condition-evaluation`, `rule-evaluation`, and `event-validation`. +Validation steps during processing appear as `condition-validation` or `rule-condition-validation` children. + +Full documentation, sample trace JSON, and sequence diagrams: + +* <<_request_tracing_explain,Using the explain parameter for request tracing>> + +TIP: Use explain only when debugging. +It adds overhead and exposes internal processing detail to admin users. + +===== Fix save-time validation errors + +If creating or updating a segment or rule fails with `Invalid … condition`, the condition JSON does not match its type definition. +Read the error list in the response, fix the parameter values, and retry. + +Common causes: + +* Missing `required` parameter +* Wrong value type (string instead of integer) +* Two parameters set in the same `exclusiveGroup` +* Nested condition missing a required tag (for example `eventCondition` inside `pastEventCondition`) + +See <<_condition_validation,Condition validation>> for the full rule set and plugin author guide. diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc index 6304c550e..c4c797190 100644 --- a/manual/src/main/asciidoc/index.adoc +++ b/manual/src/main/asciidoc/index.adoc @@ -77,6 +77,8 @@ include::migrations/migrations.adoc[] include::conditions-guide.adoc[] +include::condition-validation.adoc[] + include::condition-evaluation.adoc[] include::past-event-conditions.adoc[] diff --git a/manual/src/main/asciidoc/request-examples.adoc b/manual/src/main/asciidoc/request-examples.adoc index ee1a3e4e4..abdbbc0ae 100644 --- a/manual/src/main/asciidoc/request-examples.adoc +++ b/manual/src/main/asciidoc/request-examples.adoc @@ -1387,6 +1387,8 @@ Best practices for Groovy actions: Apache Unomi provides a powerful request tracing feature through the `explain` query parameter. This feature helps administrators understand how requests are processed internally, including event processing, condition evaluations, and rule executions. +For save-time condition checks (before runtime), see <<_condition_validation,Condition validation>>. For a conditions-focused debugging walkthrough, see <<_debugging_conditions,Debugging conditions>>. + ===== Prerequisites To use the explain parameter, you must have one of the following roles: diff --git a/manual/src/main/asciidoc/whats-new.adoc b/manual/src/main/asciidoc/whats-new.adoc index d1a9bba46..850710e54 100644 --- a/manual/src/main/asciidoc/whats-new.adoc +++ b/manual/src/main/asciidoc/whats-new.adoc @@ -54,6 +54,14 @@ Administrators can trace how context and event-collector requests are processed * <<_request_tracing_explain,Using the explain parameter for request tracing>> +==== Condition validation + +Segments, rules, and queries are validated against condition type definitions at save time. +Invalid parameters are rejected before they reach runtime evaluation. + +* <<_condition_validation,Condition validation>> +* <<_debugging_conditions,Debugging conditions>> + === What's new in Apache Unomi 3.0 Apache Unomi 3.0 is a major platform release. It upgrades the search-engine client, the OSGi runtime, and the minimum Java version. From 609abab6b4d061f174322ce8c970924b5e54a388 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 09:12:14 +0200 Subject: [PATCH 13/19] UNOMI-960: Rewrite plugin writing manual for Unomi 3.1 Fix stale examples (evaluator interfaces, query builder paths, feature names, POM artifacts), consolidate ES/OS guidance, document missing META-INF/cxs paths, and add cross-links to validation and samples. --- manual/src/main/asciidoc/samples/samples.adoc | 1 + manual/src/main/asciidoc/writing-plugins.adoc | 1224 +++++------------ 2 files changed, 350 insertions(+), 875 deletions(-) diff --git a/manual/src/main/asciidoc/samples/samples.adoc b/manual/src/main/asciidoc/samples/samples.adoc index 45d0c3829..e023c1b78 100644 --- a/manual/src/main/asciidoc/samples/samples.adoc +++ b/manual/src/main/asciidoc/samples/samples.adoc @@ -16,5 +16,6 @@ Apache Unomi provides the following samples: +* `samples/trainingplugin` — minimal plugin (action + rule + Blueprint); see <<_writing_plugins,Writing plugins>> * <<_twitter_sample,Twitter integration>> * <<_login_sample,Login integration>> diff --git a/manual/src/main/asciidoc/writing-plugins.adoc b/manual/src/main/asciidoc/writing-plugins.adoc index f87022c4e..845b57de3 100644 --- a/manual/src/main/asciidoc/writing-plugins.adoc +++ b/manual/src/main/asciidoc/writing-plugins.adoc @@ -11,318 +11,134 @@ // See the License for the specific language governing permissions and // limitations under the License. // -=== Writing Plugins -Apache Unomi is architected to be extensible through plugins. However, it's important to note that plugins are only necessary when custom Java logic is required. Most common needs and use cases can now be covered using: +[[_writing_plugins]] +=== Writing plugins -* Groovy actions (for custom action logic without deployment) -* Complex condition trees (for sophisticated matching logic) -* Rules (for defining business logic and automation) -* Apache Unomi's REST API (for configuration and management) +Apache Unomi is extensible through OSGi plugins (Karaf bundles). Many use cases do **not** need a plugin: -These built-in features allow you to implement most functionality without deploying any code, making the system more maintainable and reducing development overhead. You should only consider developing a plugin when you need to: +* Groovy actions — custom rule actions stored at runtime (see <<_configuration,Configuration>>) +* Complex condition trees — combine built-in condition types +* Rules, segments, and REST configuration — manage through APIs or JSON definitions in a bundle +* <<_json_schemas,JSON Schemas>> — validate events without Java code -* Implement custom Java-based query builders for search engine integration -* Add new core system functionality that requires deep integration -* Optimize performance-critical operations with compiled code -* Integrate with external systems requiring complex Java libraries +Build a plugin when you need compiled Java logic, for example: -=== Types vs. instances +* Custom `ConditionEvaluator` or search-engine `*QueryBuilder` implementations +* Custom `ActionExecutor` integrations with external systems +* Index mappings, Painless scripts, or migration patches shipped with your feature +* Background work through `TaskExecutor` and `SchedulerService` (see <<_scheduler,Task scheduler>>) -Several extension points in Unomi rely on the concept of type: a plugin defines a prototype for what the actual -items will be once parameterized with values known only at runtime. This is similar to the concept of classes in -object-oriented programming: types define classes, providing the expected structure and which fields are expected to -be provided at runtime, that are then instantiated when needed with actual values. +==== Prerequisites -So for example we have the following types vs instances: +* **Java 17** or later +* **Apache Karaf 4.4** (bundled with Unomi distributions) +* Maven `packaging` set to `bundle` (Felix maven-bundle-plugin) +* Depend on `unomi-api` and import `unomi-bom` for aligned artifact versions -- ConditionTypes vs Conditions -- ActionTypes vs Actions -- PropertyTypes vs Properties (for profiles and sessions) +See also: -=== Plugin structure +* <<_data_model_overview,Data model overview>> +* <<_builtin_condition_types,Built-in condition types>> — registration patterns for Unomi 3.0+ +* <<_builtin_action_types,Built-in action types>> +* <<_condition_validation,Condition validation>> — `validation` metadata on condition parameters +* <<_samples,Samples>> — working plugin projects in the repository -Being built on top of Apache Karaf, Unomi leverages OSGi to support plugins. A Unomi plugin is, thus, an OSGi -bundle specifying some specific metadata to tell Unomi the kind of entities it provides. A plugin can provide the -following entities to extend Unomi, each with its associated definition (as a JSON file), located in a specific spot -within the `META-INF/cxs/` directory of the bundle JAR file: +==== Types vs instances -|==== -|Entity |Location in `cxs` directory +Extension points follow a type/instance model (like classes and objects): -|ActionType |actions -|ConditionType |conditions -|Persona |personas -|PropertyMergeStrategyType |mergers -|PropertyType |properties then profiles or sessions subdirectory then `<category name>` directory -|Rule |rules -|Scoring |scorings -|Segment |segments -|ValueType |values -|==== +* *ConditionType* → *Condition* instances in segments, rules, and queries +* *ActionType* → *Action* instances executed by rules +* *PropertyType* → profile or session property metadata (UI hints; not server-side validation) -http://aries.apache.org/modules/blueprint.html[Blueprint] is used to declare what the plugin provides and inject -any required dependency. The Blueprint file is located, as usual, at `OSGI-INF/blueprint/blueprint.xml` in the bundle JAR file. +==== `META-INF/cxs/` layout -=== Scheduling background work +A Unomi plugin is an OSGi bundle. JSON definitions and bundle resources live under `src/main/resources/META-INF/cxs/` (and related paths below). -Plugins can register `TaskExecutor` implementations and schedule one-shot or recurring tasks through `SchedulerService`. -This is the recommended way to run maintenance, async follow-up work, or long-running jobs in a cluster-safe way. +[cols="2,3,2", options="header"] +|=== +| Entity | Directory under `META-INF/cxs/` | Auto-loaded at bundle start? -See <<_scheduler,Task scheduler>> for architecture, REST operations, shell commands, and copy-paste examples -(including tenant-aware executors and checkpoint/resume patterns). +| ActionType | `actions/` | Yes +| ConditionType | `conditions/` | Yes +| ValueType | `values/` | Yes +| PropertyMergeStrategyType | `mergers/` | Yes +| PropertyType | `properties/profiles/...` or `properties/sessions/...` | Yes +| Rule | `rules/` | Yes +| Segment | `segments/` | Yes +| Scoring | `scoring/` | Yes +| Persona | `personas/` | Yes +| Patch | `patches/` | Yes (first run) +| Index mapping | `mappings/` | Loaded by persistence layer +| JSON Schema | `schemas/` | Loaded by schema service +| Expression filter | `expressions/` | Loaded by scripting layer +| Painless script | `painless/` | Loaded by persistence layer +| Migration script | `migration/` | Used by shell migration tooling +|=== -=== Search Engine Specific Implementations +Goals and campaigns can be deployed with `unomi:deploy-definition` but are not auto-loaded from bundles the same way as rules and segments. -When implementing plugins that need to interact with the search engine layer (ElasticSearch or OpenSearch), special consideration must be taken to ensure compatibility and proper deployment. +Tags are not separate JSON files — use `systemTags` on metadata inside other definitions. -==== Search Engine Specific Bundles +==== OSGi service registration -For plugins that provide search query builder implementations, you should structure your plugin with three bundles: -1. A common bundle for shared code and resources -2. An ElasticSearch-specific implementation bundle -3. An OpenSearch-specific implementation bundle +Plugins register Java implementations as OSGi services. Two styles are used in the codebase: -Example structure: -``` -my-plugin/ -├── common/ -│ ├── src/main/java/.../ -│ │ ├── model/ -│ │ │ └── MyCustomCondition.java -│ │ └── services/ -│ │ └── MyCustomService.java -│ ├── src/main/resources/META-INF/cxs/ -│ │ ├── conditions/ -│ │ │ └── my-custom-condition.json -│ │ └── actions/ -│ │ └── my-custom-action.json -│ └── pom.xml -├── elasticsearch/ -│ ├── src/main/java/.../ -│ │ └── impl/ -│ │ └── MyElasticSearchQueryBuilder.java -│ ├── src/main/resources/META-INF/cxs/ -│ │ └── mappings/ -│ │ └── my-index-mapping.json -│ └── pom.xml -├── opensearch/ -│ ├── src/main/java/.../ -│ │ └── impl/ -│ │ └── MyOpenSearchQueryBuilder.java -│ ├── src/main/resources/META-INF/cxs/ -│ │ └── mappings/ -│ │ └── my-index-mapping.json -│ └── pom.xml -└── pom.xml -``` - -Example POM dependencies: - -Common Bundle (my-plugin-common): -[source,xml] ----- - - - org.apache.unomi - unomi-api - ${unomi.version} - provided - - ----- +* **Blueprint** — `OSGI-INF/blueprint/blueprint.xml` (for example `plugins/baseplugin`, `samples/trainingplugin`) +* **Declarative Services** — `@Component` annotations (for example `plugins/advanced-conditions`) -ElasticSearch Bundle (my-plugin-elasticsearch): -[source,xml] ----- - - - my.group.id - my-plugin-common - ${project.version} - - - org.apache.unomi - unomi-persistence-elasticsearch-core - ${unomi.version} - provided - - ----- +Both are valid. Use Blueprint when you prefer XML wiring; use DS for annotation-driven registration. -OpenSearch Bundle (my-plugin-opensearch): -[source,xml] ----- - - - my.group.id - my-plugin-common - ${project.version} - - - org.apache.unomi - unomi-persistence-opensearch-core - ${unomi.version} - provided - - ----- - -==== Configuration and Deployment +Service property keys: -Administrators can control which search engine implementation to use by setting up a distribution. This distribution 'macro' feature determines which features (including your plugin's bundles) are deployed based on the chosen search engine: +[cols="2,2,3", options="header"] +|=== +| Extension | Service interface | OSGi property key -[source,xml] ----- - - - mvn:my.group.id/my-plugin-common/${project.version} - mvn:my.group.id/my-plugin-${search.engine}/${project.version} - +| Condition evaluator | `org.apache.unomi.persistence.spi.conditions.evaluator.ConditionEvaluator` | `conditionEvaluatorId` +| Elasticsearch query builder | `org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder` | `queryBuilderId` +| OpenSearch query builder | `org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder` | `queryBuilderId` +| Action executor | `org.apache.unomi.api.actions.ActionExecutor` | `actionExecutorId` +| Value type validator | `org.apache.unomi.api.services.ValueTypeValidator` | (service type only) +|=== - - unomi-base - unomi-startup - unomi-elasticsearch-core - unomi-persistence-core - unomi-services - unomi-rest-api - unomi-cxs-lists-extension - unomi-cxs-geonames-extension - unomi-cxs-privacy-extension - unomi-elasticsearch-conditions - unomi-plugins-base - unomi-plugins-request - unomi-plugins-mail - unomi-wab - unomi-web-tracker - unomi-healthcheck-elasticsearch - unomi-router-karaf-feature - unomi-groovy-actions - unomi-rest-ui - unomi-startup-complete - my-plugin-feature - - ----- +The JSON descriptor field (`conditionEvaluator`, `queryBuilder`, `actionExecutor`) must match the OSGi property value. -==== Custom Plugins +==== Creating a Maven plugin project -The plugin otherwise follows a regular maven project layout and should depend on the Unomi API maven artifact: +A minimal plugin is a Maven bundle project. Prefer importing `unomi-bom` rather than pinning versions manually. [source,xml] ---- - - org.apache.unomi - unomi-api - ... - ----- - -Some plugins consists only of JSON definitions that are used to instantiate the appropriate structures at runtime -while some more involved plugins provide code that extends Unomi in deeper ways. - -In both cases, plugins can provide more that one type of extension. For example, a plugin could provide both `ActionType`s and `ConditionType`s. - -=== Extension points - -In this section the value types that may be used as extension points are presented. Examples of these types will be -given in the next section with more details. - -==== ActionType - -`ActionType`s define new actions that can be used as consequences of Rules being triggered. When a rule triggers, it -creates new actions based on the event data and the rule internal processes, providing values for parameters defined -in the associated `ActionType`. Example actions include: “Set user property x to value y” or “Send a message to service x”. - -==== ConditionType - -`ConditionType`s define new conditions that can be applied to items (for example to decide whether a rule needs to be -triggered or if a profile is considered as taking part in a campaign) or to perform queries against the stored Unomi -data. They may be implemented in Java when attempting to define a particularly complex test or one that can better be -optimized by coding it. They may also be defined as combination of other conditions. A simple condition could be: -“User is male”, while a more generic condition with parameters may test whether a given property has a specific value: -“User property x has value y”. - -==== Persona - -A persona is a "virtual" profile used to represent categories of profiles, and may also be used to test how a -personalized experience would look like using this virtual profile. A persona can define predefined properties and -sessions. Persona definition make it possible to “emulate” a certain type of profile, e.g : US visitor, non-US visitor, etc. - -==== PropertyMergeStrategyType - -A strategy to resolve how to merge properties when merging profile together. - -==== PropertyType - -Definition for a profile, session, or event property, specifying how possible values are constrained, if the value is -multi-valued (a vector of values as opposed to a scalar value). `PropertyType`s can also be categorized using -systemTags or file system structure, using sub-directories to organize definition files. - -For detailed information about property types, see <<_property_types,Property Types>>. - -==== Rule - -`Rule`s are conditional sets of actions to be executed in response to incoming events. Triggering of rules is guarded -by a condition: the rule is only triggered if the associated condition is satisfied. That condition can test the -event itself, but also the profile or the session. Once a rule triggers, a list of actions can be performed as -consequences. Also, when rules trigger, a specific event is raised so that other parts of Unomi can react accordingly. - -==== Scoring - -`Scoring`s are set of conditions associated with a value to assign to profiles when matching so that the associated -users can be scored along that dimension. Each scoring element is evaluated and matching profiles' scores are -incremented with the associated value. - -==== Segments - -`Segment`s represent dynamically evaluated groups of similar profiles in order to categorize the associated users. -To be considered part of a given segment, users must satisfies the segment’s condition. If they match, users are -automatically added to the segment. Similarly, if at any given point during, they cease to satisfy the segment’s -condition, they are automatically removed from it. - -==== Tag - -`Tag`s are simple labels that are used to classify all other objects inside Unomi. - -==== ValueType - -Definition for values that can be assigned to properties ("primitive" types). - -=== Custom plugins - -Apache Unomi is a pluggeable server that may be extended in many ways. This document assumes you are familiar with the -<<_data_model_overview,Apache Unomi Data Model>> . This document is mostly a reference document on the different things that may -be used inside an extension. If you are looking for complete samples, please see the <<_samples,samples page>>. - -==== Creating a plugin - -An plugin is simply a Maven project, with a Maven pom that looks like this: - -[source] ----- - - - org.apache.unomi - unomi-plugins - ${project.version} - - + 4.0.0 - unomi-plugin-example - Apache Unomi :: Plugins :: Example - A sample example of a Unomi plugin - ${project.version} + com.example + my-unomi-plugin + 1.0.0-SNAPSHOT bundle + + 3.1.0-SNAPSHOT + + + + + + org.apache.unomi + unomi-bom + ${unomi.version} + pom + import + + + + - org.apache.unomi unomi-api - ${project.version} provided @@ -332,14 +148,12 @@ An plugin is simply a Maven project, with a Maven pom that looks like this: org.apache.felix maven-bundle-plugin + 5.1.9 true *;scope=compile|runtime - - sun.misc;resolution:=optional, - * - + * @@ -348,757 +162,417 @@ An plugin is simply a Maven project, with a Maven pom that looks like this: ---- -A plugin may contain many different kinds of Apache Unomi objects, as well as custom OSGi services or anything that -is needed to build your application. +The repository includes a minimal reference: `samples/trainingplugin` (action type + rule + Blueprint executor). -==== Deployment and custom definition +==== Deployment and updates -When you deploy a custom bundle with a custom definition (see "Predefined xxx" chapters under) for the first time, the -definition will automatically be deployed at your bundle start event *if it does not exist*. -After that if you redeploy the same bundle, the definition will not be redeployed, but you can redeploy it manually -using the command `unomi:deploy-definition <bundleId> <fileName>` If you need to modify an existing -definition when deploying the module, see <<_migration_patches,Migration patches>>. +When a bundle starts, predefined JSON definitions are deployed **once** if they do not already exist. +Redeploying the same bundle does not overwrite existing definitions. -==== Predefined segments +To force redeploy: -You may provide pre-defined segments by simply adding a JSON file in the src/main/resources/META-INF/cxs/segments directory of -your Maven project. Here is an example of a pre-defined segment: +* Karaf shell: `unomi:deploy-definition ` — see the shell commands chapter in this manual +* Structural changes to existing definitions: <<_migration_patches,Migration patches>> -[source] +Wildcards are supported, for example `unomi:deploy-definition 175 rules *`. + +==== JSON-only extensions + +===== Predefined segments + +Place JSON under `META-INF/cxs/segments/`: + +[source,json] ---- { "metadata": { "id": "leads", "name": "Leads", "scope": "systemscope", - "description": "You can customize the list below by editing the leads segment.", - "readOnly":true + "description": "Profiles with leadAssignedTo set", + "readOnly": true }, "condition": { + "type": "booleanCondition", "parameterValues": { + "operator": "and", "subConditions": [ { + "type": "profilePropertyCondition", "parameterValues": { "propertyName": "properties.leadAssignedTo", "comparisonOperator": "exists" - }, - "type": "profilePropertyCondition" + } } - ], - "operator" : "and" - }, - "type": "booleanCondition" + ] + } } } ---- -Basically this segment uses a condition to test if the profile has a property `leadAssignedTo` that exists. All profiles -that match this condition will be part of the pre-defined segment. +===== Predefined rules -==== Predefined rules +Place JSON under `META-INF/cxs/rules/`: -You may provide pre-defined rules by simply adding a JSON file in the src/main/resources/META-INF/cxs/rules directory of -your Maven project. Here is an example of a pre-defined rule: - -[source] +[source,json] ---- { - "metadata" : { - "id": "evaluateProfileSegments", - "name": "Evaluate segments", - "description" : "Evaluate segments when a profile is modified", - "readOnly":true - }, - - "condition" : { - "type": "profileUpdatedEventCondition", - "parameterValues": { - } - }, - - "actions" : [ - { - "type": "evaluateProfileSegmentsAction", - "parameterValues": { - } - } - ] - + "metadata": { + "id": "evaluateProfileSegments", + "name": "Evaluate segments", + "description": "Evaluate segments when a profile is modified", + "readOnly": true + }, + "condition": { + "type": "profileUpdatedEventCondition", + "parameterValues": {} + }, + "actions": [ + { + "type": "evaluateProfileSegmentsAction", + "parameterValues": {} + } + ] } ---- -In this example we provide a rule that will execute when a predefined composed condition of type -"profileUpdatedEventCondition" is received. See below to see how predefined composed conditions are declared. -Once the condition is matched, the actions will be executed in sequence. In this example there is only a single -action of type "evaluateProfileSegmentsAction" that is defined so it will be executed by Apache Unomi's rule engine. -You can also see below how custom actions may be defined. - -==== Predefined properties - -By default Apache Unomi comes with a set of pre-defined properties, but in many cases it is useful to add additional -predefined property definitions. You can create property definitions for session or profile properties by creating them -in different directories. - -For session properties you must create a JSON file in the following directory in your Maven project: - -[source] ----- -src/main/resources/META-INF/cxs/properties/sessions ----- - -For profile properties you must create the JSON file inside the directory in your Maven project: - -[source] ----- -src/main/resources/META-INF/cxs/properties/profiles ----- - -Here is an example of a property definition JSON file - -[source] ----- -{ - "metadata": { - "id": "city", - "name": "City", - "systemTags": ["properties", "profileProperties", "contactProfileProperties"] - }, - "type": "string", - "defaultValue": "", - "automaticMappingsFrom": [ ], - "rank": "304.0" -} ----- +===== Predefined property types -==== Predefined child conditions +* Profiles: `META-INF/cxs/properties/profiles/` +* Sessions: `META-INF/cxs/properties/sessions/` -You can define new predefined conditions that are actually conditions inheriting from a parent condition and setting -pre-defined parameter values. You can do this by creating a JSON file in: +See <<_property_types,Property types>> for the full model. -[source] ----- -src/main/resources/META-INF/cxs/conditions ----- +===== Predefined child conditions -Here is an example of a JSON file that defines a profileUpdateEventCondition that inherits from a parent condition of -type eventTypeCondition. +Specialize an existing condition type with `parentCondition` under `META-INF/cxs/conditions/`: -[source] +[source,json] ---- { "metadata": { "id": "profileUpdatedEventCondition", "name": "profileUpdatedEventCondition", - "description": "", - "systemTags": [ - "event", - "eventCondition" - ], + "systemTags": ["event", "eventCondition"], "readOnly": true }, "parentCondition": { "type": "eventTypeCondition", - "parameterValues": { - "eventTypeId": "profileUpdated" - } + "parameterValues": { "eventTypeId": "profileUpdated" } }, - - "parameters": [ - ] + "parameters": [] } ---- -==== Predefined personas - -Personas may also be pre-defined by creating JSON files in the following directory: - -[source] ----- -src/main/resources/META-INF/cxs/personas ----- - -Here is an example of a persona definition JSON file: +===== Predefined personas -[source] ----- -{ - "persona": { - "itemId": "usVisitor", - "properties": { - "description": "Represents a visitor browsing from inside the continental US", - "firstName": "U.S.", - "lastName": "Visitor" - }, - "segments": [] - }, - "sessions": [ - { - "itemId": "aa3b04bd-8f4d-4a07-8e96-d33ffa04d3d9", - "profileId": "usVisitor", - "properties": { - "operatingSystemName": "OS X 10.9 Mavericks", - "sessionCountryName": "United States", - "location": { - "lat":37.422, - "lon":-122.084058 - }, - "userAgentVersion": "37.0.2062.120", - "sessionCountryCode": "US", - "deviceCategory": "Personal computer", - "operatingSystemFamily": "OS X", - "userAgentName": "Chrome", - "sessionCity": "Mountain View" - }, - "timeStamp": "2014-09-18T11:40:54Z", - "lastEventDate": "2014-09-18T11:40:59Z", - "duration": 4790 - } - ] -} ----- - -You can see that it's also possible to define sessions for personas. +Place JSON under `META-INF/cxs/personas/`. Each file can define a persona profile and optional sessions for testing personalization. ==== Custom action types -Custom action types are a powerful way to integrate with external systems by being able to define custom logic that will -be executed by an Apache Unomi rule. An action type is defined by a JSON file created in the following directory: +1. Add `META-INF/cxs/actions/myAction.json` with an `actionExecutor` ID. +2. Register an `ActionExecutor` OSGi service with matching `actionExecutorId`. -[source] ----- -src/main/resources/META-INF/cxs/actions ----- +JSON descriptor (`samples/trainingplugin`): -Here is an example of a JSON action definition: - -[source] +[source,json] ---- { "metadata": { - "id": "addToListsAction", - "name": "addToListsAction", - "description": "", - "systemTags": [ - "demographic", - "availableToEndUser" - ], + "id": "trainingNotifAction", + "name": "trainingNotifAction", "readOnly": true }, - "actionExecutor": "addToLists", - "parameters": [ - { - "id": "listIdentifiers", - "type": "string", - "multivalued": true - } - ] + "actionExecutor": "training", + "parameters": [] } ---- -The `actionExecutor` identifier refers to a service property that is defined in the OSGi Blueprint service registration. -Note that any OSGi service registration may be used, but in these examples we use OSGi Blueprint. The definition for the -above JSON file will be found in a file called `src/main/resources/OSGI-INF/blueprint/blueprint.xml` with the following -content: +Blueprint registration (`samples/trainingplugin`): -[source] +[source,xml] ---- - - - - - - - - - - - - - - - - - - - + + + + + + + + ---- -You can note here the `actionExecutorId` that corresponds to the `actionExecutor` in the JSON file. +For a production-style example with service injection, see the lists extension: +https://github.com/apache/unomi/blob/master/extensions/lists-extension/actions/src/main/java/org/apache/unomi/lists/actions/AddToListsAction.java[`AddToListsAction`] -The implementation of the action is available here : https://github.com/apache/unomi/blob/master/extensions/lists-extension/actions/src/main/java/org/apache/unomi/lists/actions/AddToListsAction.java[org.apache.unomi.lists.actions.AddToListsAction] +Catalog of built-in actions: <<_builtin_action_types,Built-in action types>>. ==== Custom condition types -Custom condition types are different from predefined child conditions because they implement their logic using Java classes. -They are also declared by adding a JSON file into the `conditions` directory: +Custom condition types combine: -[source] ----- -src/main/resources/META-INF/cxs/conditions ----- +* A JSON descriptor in `META-INF/cxs/conditions/` +* A `ConditionEvaluator` OSGi service (required for rule and event evaluation) +* `ConditionESQueryBuilder` / `ConditionOSQueryBuilder` services (required when the condition is used in segments or profile queries) -Here is an example of JSON custom condition type definition: +Unomi 3.0+ uses **engine-neutral** `queryBuilder` IDs in JSON (for example `booleanConditionQueryBuilder`, not `*ESQueryBuilder`). -[source] ----- -{ - "metadata": { - "id": "matchAllCondition", - "name": "matchAllCondition", - "description": "", - "systemTags": [ - "logical", - "profileCondition", - "eventCondition", - "sessionCondition", - "sourceEventCondition" - ], - "readOnly": true - }, - "conditionEvaluator": "matchAllConditionEvaluator", - "queryBuilder": "matchAllConditionQueryBuilder", +Add `validation` blocks on parameters so invalid instances are rejected at save time — see <<_condition_validation,Condition validation>>. - "parameters": [ - ] -} ----- +===== Evaluator registration (Blueprint or DS) -Note the `conditionEvaluator` and the `queryBuilder` values. These reference OSGi service properties that are declared -in an OSGi Blueprint configuration file (other service definitions may also be used such as Declarative Services or even -Java registered services). Here is an example of an OSGi Blueprint definition corresponding to the above JSON condition -type definition file. +The base plugin registers evaluators in Blueprint; advanced conditions use Declarative Services: -[source] +[source,java] ---- -src/main/resources/OSGI-INF/blueprint/blueprint.xml - - - - - - - - - - - - - - - - - - +@Component(service = ConditionEvaluator.class, + property = {"conditionEvaluatorId=sourceEventPropertyConditionEvaluator"}) +public class SourceEventPropertyConditionEvaluator implements ConditionEvaluator { + // ... +} ---- -You can find the implementation of the two classes here : +Full registration examples (evaluator + Elasticsearch and OpenSearch query builders): <<_builtin_condition_types,Plugin registration (Unomi 3.0+)>>. -* https://github.com/apache/unomi/blob/master/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionESQueryBuilder.java[conditions.org.apache.unomi.persistence.elasticsearch.MatchAllConditionESQueryBuilder] -* https://github.com/apache/unomi/blob/master/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionEvaluator.java[org.apache.unomi.plugins.baseplugin.conditions.MatchAllConditionEvaluator] +===== Query builders and the persistence split -==== Implementation Examples +In the Apache Unomi distribution, **core** query builders for built-in conditions live in: -Here's a complete example of implementing custom query builders for both search engines following Apache Unomi 3.0 best practices: +* `persistence-elasticsearch/core` — `unomi-elasticsearch-conditions` feature +* `persistence-opensearch/core` — `unomi-opensearch-conditions` feature -**ElasticSearch Implementation:** -[source,java] ----- -package org.apache.unomi.plugin.elasticsearch; +The base plugin bundle registers evaluators only; it does not register query builders. -import co.elastic.clients.elasticsearch._types.query_dsl.Query; -import org.apache.unomi.api.conditions.Condition; -import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; +For **custom** conditions that participate in segment queries, ship search-engine-specific query builder bundles (see next section). -public class MyCustomQueryBuilder implements ConditionESQueryBuilder { - - @Override - public Query buildQuery(Condition condition, Map context, - ConditionESQueryBuilderDispatcher dispatcher) { - // Get parameters from the condition - String fieldName = (String) condition.getParameter("fieldName"); - String fieldValue = (String) condition.getParameter("fieldValue"); - - // Build Elasticsearch-specific query using the new client - return Query.of(q -> q - .bool(b -> b - .must(m -> m - .term(t -> t - .field(fieldName) - .value(v -> v.stringValue(fieldValue)) - ) - ) - ) - ); - } - - @Override - public long count(Condition condition, Map context, - ConditionESQueryBuilderDispatcher dispatcher) { - // Implement count logic if needed - return 0; - } -} ----- +===== Query builder implementation (Elasticsearch Java API Client) -**OpenSearch Implementation:** [source,java] ---- -package org.apache.unomi.plugin.opensearch; +package com.example.plugin.elasticsearch; -import org.opensearch.client.opensearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; import org.apache.unomi.api.conditions.Condition; -import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder; -import org.opensearch.index.query.QueryBuilder; -import org.opensearch.index.query.QueryBuilders; +import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder; +import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher; + +import java.util.Map; -public class MyCustomQueryBuilder implements ConditionOSQueryBuilder { +public class MyCustomESQueryBuilder implements ConditionESQueryBuilder { @Override public Query buildQuery(Condition condition, Map context, - ConditionOSQueryBuilderDispatcher dispatcher) { - // Get parameters from the condition + ConditionESQueryBuilderDispatcher dispatcher) { String fieldName = (String) condition.getParameter("fieldName"); String fieldValue = (String) condition.getParameter("fieldValue"); - - // Build OpenSearch-specific query - return Query.of(q -> q - .bool(b -> b - .must(m -> m - .term(t -> t - .field(fieldName) - .value(v -> v.stringValue(fieldValue)) - ) - ) - ) - ); - } - - @Override - public long count(Condition condition, Map context, - ConditionOSQueryBuilderDispatcher dispatcher) { - // Implement count logic if needed - return 0; + return Query.of(q -> q.term(t -> t.field(fieldName).value(v -> v.stringValue(fieldValue)))); } } ---- -**OSGi Service Registration for ElasticSearch** (`elasticsearch-bundle/src/main/resources/OSGI-INF/blueprint/blueprint.xml`): -[source,xml] ----- - - - - - - - - - ----- +OpenSearch uses the same pattern with `org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder` and `org.opensearch.client.opensearch._types.query_dsl.Query`. -**OSGi Service Registration for OpenSearch** (`opensearch-bundle/src/main/resources/OSGI-INF/blueprint/blueprint.xml`): -[source,xml] ----- - - - - - - - - - ----- +Shared condition type JSON: -**Condition Type Definition** (shared between both implementations): [source,json] ---- { "metadata": { "id": "myCustomCondition", "name": "My Custom Condition", - "description": "A custom condition for demonstration purposes", - "systemTags": ["custom", "condition"], + "systemTags": ["custom", "condition", "profileCondition"], "readOnly": false }, + "conditionEvaluator": "myCustomConditionEvaluator", "queryBuilder": "myCustomQueryBuilder", "parameters": [ - { - "id": "fieldName", - "type": "string", - "multivalued": false - }, - { - "id": "fieldValue", - "type": "string", - "multivalued": false - } + { "id": "fieldName", "type": "string", "validation": { "required": true } }, + { "id": "fieldValue", "type": "string", "validation": { "required": true } } ] } ---- -==== Project Structure and Packaging - -For plugins that support both Elasticsearch and OpenSearch, follow this recommended project structure: - -``` -my-custom-plugin/ -├── pom.xml # Parent POM -├── my-custom-plugin-common/ # Shared code and interfaces -│ ├── pom.xml -│ └── src/main/java/ -│ └── org/apache/unomi/plugin/ -│ └── common/ -│ ├── MyCustomConditionType.java -│ └── MyCustomConditionEvaluator.java -├── my-custom-plugin-elasticsearch/ # Elasticsearch-specific implementation -│ ├── pom.xml -│ └── src/main/ -│ ├── java/ -│ │ └── org/apache/unomi/plugin/elasticsearch/ -│ │ └── MyCustomQueryBuilder.java -│ └── resources/ -│ └── OSGI-INF/blueprint/ -│ └── blueprint.xml -├── my-custom-plugin-opensearch/ # OpenSearch-specific implementation -│ ├── pom.xml -│ └── src/main/ -│ ├── java/ -│ │ └── org/apache/unomi/plugin/opensearch/ -│ │ └── MyCustomQueryBuilder.java -│ └── resources/ -│ └── OSGI-INF/blueprint/ -│ └── blueprint.xml -└── my-custom-plugin-features/ # Karaf feature definitions - ├── pom.xml - └── src/main/resources/ - └── features.xml -``` - -**Parent POM Configuration:** -[source,xml] +==== Multi-bundle layout for Elasticsearch and OpenSearch + +When your condition needs segment/query support on **both** search engines, use three Maven modules: + +[source] +---- +my-plugin/ +├── my-plugin-common/ # JSON descriptors, evaluators, shared code +├── my-plugin-elasticsearch/ # ConditionESQueryBuilder + ES mappings +├── my-plugin-opensearch/ # ConditionOSQueryBuilder + OS mappings +└── my-plugin-features/ # Karaf features.xml ---- - - org.apache.unomi.plugins - my-custom-plugin - 1.0.0 - pom - - - my-custom-plugin-common - my-custom-plugin-elasticsearch - my-custom-plugin-opensearch - my-custom-plugin-features - - - 3.1.0 - - +Common bundle dependencies: + +[source,xml] +---- + + org.apache.unomi + unomi-api + provided + + + org.apache.unomi + unomi-persistence-spi + provided + ---- -**Elasticsearch Bundle POM:** +Elasticsearch bundle adds: + [source,xml] ---- - - - org.apache.unomi.plugins - my-custom-plugin - 1.0.0 - - - my-custom-plugin-elasticsearch - bundle + + org.apache.unomi + unomi-persistence-elasticsearch-core + provided + +---- - - - org.apache.unomi - unomi-api - ${unomi.version} - - - org.apache.unomi - persistence-elasticsearch-core - ${unomi.version} - - - org.apache.unomi.plugins - my-custom-plugin-common - 1.0.0 - - +OpenSearch bundle adds: - - - - org.apache.felix - maven-bundle-plugin - true - - - my-custom-plugin-elasticsearch - My Custom Plugin - Elasticsearch - org.apache.unomi.plugin.elasticsearch - * - - - - - - +[source,xml] ---- + + org.apache.unomi + unomi-persistence-opensearch-core + provided + +---- + +===== Karaf features -**Karaf Feature Definitions:** +Define **separate** features per search engine. Use the same `queryBuilderId` in both implementations. -`features.xml` (single file with multiple feature definitions): [source,xml] ---- - - - - - - mvn:org.apache.unomi.plugins/my-custom-plugin-common/1.0.0 - mvn:org.apache.unomi.plugins/my-custom-plugin-elasticsearch/1.0.0 - unomi-elasticsearch + + + mvn:com.example/my-plugin-common/1.0.0 + mvn:com.example/my-plugin-elasticsearch/1.0.0 + unomi-elasticsearch-conditions - - - - mvn:org.apache.unomi.plugins/my-custom-plugin-common/1.0.0 - mvn:org.apache.unomi.plugins/my-custom-plugin-opensearch/1.0.0 - unomi-opensearch + + mvn:com.example/my-plugin-common/1.0.0 + mvn:com.example/my-plugin-opensearch/1.0.0 + unomi-opensearch-conditions ---- -==== Best Practices - -1. **Naming Conventions** - - Use generic queryBuilder IDs (e.g., `myCustomQueryBuilder`) instead of search engine-specific ones - - Follow the new naming convention: remove "ES" references, use "QueryBuilder" suffix - - Keep the same queryBuilder ID across both implementations for consistency - -2. **Code Organization** - - Keep shared logic in a common module (condition types, evaluators, utilities) - - Implement search engine specific code only where necessary - - Use interfaces to define common behavior - - Minimize code duplication between implementations - -3. **OSGi Service Registration** - - Register services only in the appropriate bundle (Elasticsearch vs OpenSearch) - - Use the same `queryBuilderId` in service properties for both implementations - - Ensure proper dependency management to avoid conflicts - -4. **Testing Strategy** - - Create test suites for both implementations - - Use Docker containers for integration testing: - ```yaml - services: - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3 - opensearch: - image: opensearchproject/opensearch:3.4.0 - ``` - - Test with both search engines to ensure compatibility - -5. **Deployment Configuration** - - Package search engine specific code in separate bundles - - Use a single feature file with multiple feature definitions for better organization - - Configure the appropriate feature in your deployment based on the selected search engine: - - For Elasticsearch: `feature:install my-custom-plugin-elasticsearch` - - For OpenSearch: `feature:install my-custom-plugin-opensearch` - - Document dependencies and requirements clearly +Install at runtime: -[[_custom_distribution_feature]] -==== Custom Distribution Feature +* Elasticsearch: `feature:install my-plugin-elasticsearch` +* OpenSearch: `feature:install my-plugin-opensearch` -For production deployments, you can create a custom distribution's feature file to automatically include your plugin features in the startup configuration. This approach ensures your plugin is automatically deployed when Apache Unomi starts. - -**Creating a Custom Distribution Feature:** +[[_custom_distribution_feature]] +===== Custom distribution feature -1. **Create the feature file** in a dedicated maven module. You can use the unomi-distribution module as a reference. +For production, add your plugin feature to a custom distribution based on `distribution/src/main/feature/feature.xml`. +Extend `unomi-distribution-elasticsearch` or `unomi-distribution-opensearch` and append your plugin feature **last**. -2. **Define your custom distribution** by extending the default ones: +Elasticsearch example (abbreviated — compare with the real file in the repository): [source,xml] ---- - - - unomi-base - unomi-startup - unomi-elasticsearch-core - unomi-persistence-core - unomi-services - unomi-rest-api - unomi-cxs-lists-extension - unomi-cxs-geonames-extension - unomi-cxs-privacy-extension - unomi-elasticsearch-conditions - unomi-plugins-base - unomi-plugins-request - unomi-plugins-mail - unomi-wab - unomi-web-tracker - unomi-healthcheck-elasticsearch - unomi-router-karaf-feature - unomi-groovy-actions - unomi-rest-ui - unomi-startup-complete - custom-feature - - + + unomi-distribution-elasticsearch + my-plugin-elasticsearch + ---- -**Key Points:** +OpenSearch deployments use `unomi-distribution-opensearch` and `unomi-healthcheck-opensearch` instead of the Elasticsearch healthcheck feature. + +See also <<_building,Building and deploying>> and <<_configuration,Configuration>>. + +==== Additional bundle resources + +===== JSON Schemas for events + +Place event schemas under `META-INF/cxs/schemas/`. See <<_json_schemas,JSON Schemas>>. -- **Feature ordering**: Your custom plugin features should be added at the end of the feature list, after the core Apache Unomi features -- **Search engine specific**: Include only the appropriate feature for your selected search engine -- **Dependency management**: Ensure your plugin features are listed after their dependencies (e.g., after `unomi-elasticsearch-core` or `unomi-opensearch-core`) +===== Index mappings and Painless scripts +* `META-INF/cxs/mappings/` — custom index mappings loaded by the persistence service +* `META-INF/cxs/painless/` — `.painless` scripts used during persistence operations -**Benefits of Custom Distribution Feature:** +===== Expression filters -- **Automatic deployment**: Your plugin is automatically installed when Apache Unomi starts -- **Consistent environments**: Ensures the same features are deployed across all environments -- **Production ready**: No manual feature installation required -- **Version control**: Configuration can be versioned and managed with your deployment +`META-INF/cxs/expressions/` — JSON descriptors for allowed scripting expressions (MVEL and related filters). -**Note**: For more detailed information about custom distributions, including environment-specific examples, see the <<_custom_distribution_feature,Custom Distribution Feature>> section or the <<_configuration,Configuration>> section of the documentation. +===== Migration patches -+ -6. **Migration from Legacy Implementations** - - **DO NOT** use legacy mappings for custom query builders - - Rename existing query builders to follow new naming conventions - - Update condition type definitions to use new queryBuilder IDs - - Test thoroughly after migration +`META-INF/cxs/patches/` — one-time structural updates. See <<_migration_patches,Migration patches>>. -==== Learning from Core Implementation +==== Custom value types and validation + +Register custom parameter types for condition validation by exposing a `ValueTypeValidator` OSGi service. +`DefinitionsService` forwards validators to `ConditionValidationService`. + +Use this when your condition type introduces a non-primitive `type` ID in parameter definitions. + +==== Scheduling background work + +Plugins can register `TaskExecutor` implementations and schedule tasks through `SchedulerService`. +See <<_scheduler,Task scheduler>> for REST APIs, shell commands, tenant-aware executors, and checkpoint patterns. + +==== GraphQL provider extensions + +For GraphQL schema extensions, see `samples/graphql-providers` and <<_graphql_api,GraphQL API>>. + +==== Repository samples + +[cols="2,3", options="header"] +|=== +| Sample | What it demonstrates + +| `samples/trainingplugin` | Minimal action + rule + Blueprint (best starting point) +| `samples/tweet-button-plugin` | Custom action, rule, and web integration +| `samples/login-integration` | Login rule and profile merge +| `samples/groovy-actions` | Groovy action scripts in a bundle +| `samples/graphql-providers` | GraphQL type extensions +|=== + +Manual walkthroughs: <<_samples,Samples>>. + +==== Best practices + +* Prefer Groovy actions or JSON-only bundles before writing Java when possible. +* Import `unomi-bom`; use `provided` scope for Unomi artifacts inside Karaf. +* Use the same `queryBuilder` ID in JSON and in both ES/OS service registrations. +* Put evaluators and JSON descriptors in the **common** bundle; query builders in engine-specific bundles. +* Add `validation` metadata on condition parameters (see <<_condition_validation,Condition validation>>). +* Test segment conditions against both search engines if you ship both query builder bundles. +* Use `explain=true` on context requests to debug condition evaluation — see <<_debugging_conditions,Debugging conditions>>. +* Do not copy legacy `*ESQueryBuilder` class names from Unomi 2.x; follow the 3.0+ naming in <<_builtin_condition_types,Built-in condition types>>. + +===== Integration testing with Docker + +[source,yaml] +---- +services: + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3 + opensearch: + image: opensearchproject/opensearch:3.4.0 +---- -You can study the core persistence implementations as references: +==== Reference implementations in the core codebase -1. ElasticSearch Implementation: - - Location: `persistence-elasticsearch/core` - - Key classes: - * ElasticSearchPersistenceServiceImpl - * ElasticSearchQueryBuilder +Study these modules when building persistence-related plugins: -2. OpenSearch Implementation: - - Location: `persistence-opensearch/core` - - Key classes: - * OpenSearchPersistenceServiceImpl - * OpenSearchQueryBuilder +[cols="2,3", options="header"] +|=== +| Area | Location -These implementations demonstrate: -- How to handle search engine specific features -- Query building patterns -- Mapping file organization -- Error handling and retry mechanisms -- Connection management -- Security configuration +| Elasticsearch persistence | `persistence-elasticsearch/core` +| OpenSearch persistence | `persistence-opensearch/core` +| Built-in condition evaluators | `plugins/baseplugin` +| Advanced evaluators (DS) | `plugins/advanced-conditions` +| Distribution features | `distribution/src/main/feature/feature.xml` +|=== +These show query building patterns, mapping layout, connection handling, and how built-in condition types are split between evaluators (plugins) and query builders (persistence modules). From 2ba78dc2dded2bfd96520177a1c798291e090945 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 09:22:58 +0200 Subject: [PATCH 14/19] UNOMI-960: Prefer Declarative Services in plugin documentation Document DS as the required OSGi registration style for new plugins, replace Blueprint examples, and point readers at maintained modules instead of legacy samples/. --- .../asciidoc/builtin-condition-types.adoc | 55 ++++--- manual/src/main/asciidoc/samples/samples.adoc | 5 +- manual/src/main/asciidoc/writing-plugins.adoc | 154 +++++++++++++----- 3 files changed, 145 insertions(+), 69 deletions(-) diff --git a/manual/src/main/asciidoc/builtin-condition-types.adoc b/manual/src/main/asciidoc/builtin-condition-types.adoc index 2b46ec487..9500d0e50 100644 --- a/manual/src/main/asciidoc/builtin-condition-types.adoc +++ b/manual/src/main/asciidoc/builtin-condition-types.adoc @@ -62,32 +62,39 @@ Some types use `parentCondition` to specialize a base type (for example `eventTy ==== Plugin registration (Unomi 3.0+) -[source,xml] +New plugins use **Declarative Services** (`@Component`). Legacy Blueprint XML remains in some shipped bundles during migration; do not copy it for new code. + +Condition evaluator (see `plugins/advanced-conditions`): + +[source,java] +---- +@Component(service = ConditionEvaluator.class, property = {"conditionEvaluatorId=booleanConditionEvaluator"}) +public class BooleanConditionEvaluator implements ConditionEvaluator { + // ... +} +---- + +Elasticsearch query builder (in your ES-specific bundle): + +[source,java] ---- - - - - - - - - - - - - - - - - - - - - - - - +@Component(service = ConditionESQueryBuilder.class, property = {"queryBuilderId=booleanConditionQueryBuilder"}) +public class BooleanConditionESQueryBuilder implements ConditionESQueryBuilder { + // ... +} +---- + +OpenSearch query builder (in your OS-specific bundle): + +[source,java] ---- +@Component(service = ConditionOSQueryBuilder.class, property = {"queryBuilderId=booleanConditionQueryBuilder"}) +public class BooleanConditionOSQueryBuilder implements ConditionOSQueryBuilder { + // ... +} +---- + +Use the same `queryBuilderId` in JSON, Elasticsearch, and OpenSearch registrations. See <<_writing_plugins,Writing plugins>> for module layout and POM setup. ==== Catalog — base plugin diff --git a/manual/src/main/asciidoc/samples/samples.adoc b/manual/src/main/asciidoc/samples/samples.adoc index e023c1b78..667baa242 100644 --- a/manual/src/main/asciidoc/samples/samples.adoc +++ b/manual/src/main/asciidoc/samples/samples.adoc @@ -14,8 +14,9 @@ [[_samples]] === Samples -Apache Unomi provides the following samples: +Apache Unomi provides the following integration demos in `samples/`. +These are scenario walkthroughs, not maintained plugin templates — for plugin development see <<_writing_plugins,Writing plugins>>. + -* `samples/trainingplugin` — minimal plugin (action + rule + Blueprint); see <<_writing_plugins,Writing plugins>> * <<_twitter_sample,Twitter integration>> * <<_login_sample,Login integration>> diff --git a/manual/src/main/asciidoc/writing-plugins.adoc b/manual/src/main/asciidoc/writing-plugins.adoc index 845b57de3..0cf004e03 100644 --- a/manual/src/main/asciidoc/writing-plugins.adoc +++ b/manual/src/main/asciidoc/writing-plugins.adoc @@ -42,7 +42,6 @@ See also: * <<_builtin_condition_types,Built-in condition types>> — registration patterns for Unomi 3.0+ * <<_builtin_action_types,Built-in action types>> * <<_condition_validation,Condition validation>> — `validation` metadata on condition parameters -* <<_samples,Samples>> — working plugin projects in the repository ==== Types vs instances @@ -81,14 +80,36 @@ Goals and campaigns can be deployed with `unomi:deploy-definition` but are not a Tags are not separate JSON files — use `systemTags` on metadata inside other definitions. -==== OSGi service registration +==== OSGi service registration (Declarative Services) -Plugins register Java implementations as OSGi services. Two styles are used in the codebase: +New plugins must use **OSGi Declarative Services** (`@Component`, `@Reference`) — not Blueprint XML. +Unomi is migrating away from Blueprint; legacy bundles such as `plugins/baseplugin` and parts of `persistence-*` still ship Blueprint wiring, but that is not a template for new code. -* **Blueprint** — `OSGI-INF/blueprint/blueprint.xml` (for example `plugins/baseplugin`, `samples/trainingplugin`) -* **Declarative Services** — `@Component` annotations (for example `plugins/advanced-conditions`) +Enable DS component generation in your bundle POM: -Both are valid. Use Blueprint when you prefer XML wiring; use DS for annotation-driven registration. +[source,xml] +---- + + org.osgi + org.osgi.service.component.annotations + provided + +---- + +[source,xml] +---- + + org.apache.felix + maven-bundle-plugin + + + <_dsannotations>* + + + +---- + +Follow `plugins/advanced-conditions/pom.xml` for a minimal DS-enabled plugin module. Service property keys: @@ -141,6 +162,11 @@ A minimal plugin is a Maven bundle project. Prefer importing `unomi-bom` rather unomi-api provided + + org.osgi + org.osgi.service.component.annotations + provided + @@ -153,6 +179,7 @@ A minimal plugin is a Maven bundle project. Prefer importing `unomi-bom` rather *;scope=compile|runtime + <_dsannotations>* * @@ -162,8 +189,6 @@ A minimal plugin is a Maven bundle project. Prefer importing `unomi-bom` rather ---- -The repository includes a minimal reference: `samples/trainingplugin` (action type + rule + Blueprint executor). - ==== Deployment and updates When a bundle starts, predefined JSON definitions are deployed **once** if they do not already exist. @@ -271,39 +296,50 @@ Place JSON under `META-INF/cxs/personas/`. Each file can define a persona profil ==== Custom action types 1. Add `META-INF/cxs/actions/myAction.json` with an `actionExecutor` ID. -2. Register an `ActionExecutor` OSGi service with matching `actionExecutorId`. +2. Implement `ActionExecutor` and register it with `@Component` and matching `actionExecutorId`. -JSON descriptor (`samples/trainingplugin`): +JSON descriptor (same shape as `extensions/lists-extension/actions/.../addToLists.json`): [source,json] ---- { "metadata": { - "id": "trainingNotifAction", - "name": "trainingNotifAction", - "readOnly": true + "id": "myNotifyAction", + "name": "myNotifyAction", + "readOnly": false }, - "actionExecutor": "training", - "parameters": [] + "actionExecutor": "myNotifyAction", + "parameters": [ + { "id": "message", "type": "string", "multivalued": false } + ] } ---- -Blueprint registration (`samples/trainingplugin`): +Java executor (Declarative Services — write this pattern in your plugin; do not copy Blueprint from older bundles): -[source,xml] +[source,java] ---- - - - - - - - - +@Component(service = ActionExecutor.class, property = {"actionExecutorId=myNotifyAction"}) +public class MyNotifyAction implements ActionExecutor { + + private ProfileService profileService; + + @Reference + public void setProfileService(ProfileService profileService) { + this.profileService = profileService; + } + + @Override + public int execute(Action action, Event event) { + String message = (String) action.getParameter("message"); + // use profileService / event as needed + return EventService.NO_CHANGE; + } +} ---- -For a production-style example with service injection, see the lists extension: -https://github.com/apache/unomi/blob/master/extensions/lists-extension/actions/src/main/java/org/apache/unomi/lists/actions/AddToListsAction.java[`AddToListsAction`] +For a maintained DS registration pattern in the tree, see `graphql/cxs-impl/.../CDPUpdateListsAction.java`. +For action business logic (lists), see `extensions/lists-extension/actions/.../AddToListsAction.java` — migrate its Blueprint wiring to DS if you use it as a starting point. Catalog of built-in actions: <<_builtin_action_types,Built-in action types>>. @@ -319,20 +355,33 @@ Unomi 3.0+ uses **engine-neutral** `queryBuilder` IDs in JSON (for example `bool Add `validation` blocks on parameters so invalid instances are rejected at save time — see <<_condition_validation,Condition validation>>. -===== Evaluator registration (Blueprint or DS) +===== Evaluator registration (Declarative Services) -The base plugin registers evaluators in Blueprint; advanced conditions use Declarative Services: +Reference implementation: `plugins/advanced-conditions` (`SourceEventPropertyConditionEvaluator`, `PastEventConditionEvaluator`). [source,java] ---- @Component(service = ConditionEvaluator.class, property = {"conditionEvaluatorId=sourceEventPropertyConditionEvaluator"}) public class SourceEventPropertyConditionEvaluator implements ConditionEvaluator { - // ... + + private DefinitionsService definitionsService; + + @Reference + public void setDefinitionsService(DefinitionsService definitionsService) { + this.definitionsService = definitionsService; + } + + @Override + public boolean eval(Condition condition, Item item, Map context, + ConditionEvaluatorDispatcher dispatcher) { + // ... + return false; + } } ---- -Full registration examples (evaluator + Elasticsearch and OpenSearch query builders): <<_builtin_condition_types,Plugin registration (Unomi 3.0+)>>. +Registration examples for query builders: <<_builtin_condition_types,Plugin registration (Unomi 3.0+)>>. ===== Query builders and the persistence split @@ -372,6 +421,18 @@ public class MyCustomESQueryBuilder implements ConditionESQueryBuilder { OpenSearch uses the same pattern with `org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder` and `org.opensearch.client.opensearch._types.query_dsl.Query`. +Register the query builder with Declarative Services in your Elasticsearch or OpenSearch bundle: + +[source,java] +---- +@Component(service = ConditionESQueryBuilder.class, property = {"queryBuilderId=myCustomQueryBuilder"}) +public class MyCustomESQueryBuilder implements ConditionESQueryBuilder { + // buildQuery implementation ... +} +---- + +Core persistence modules still contain legacy Blueprint registrations for built-in query builders; new plugin code should use DS as above. + Shared condition type JSON: [source,json] @@ -521,26 +582,33 @@ See <<_scheduler,Task scheduler>> for REST APIs, shell commands, tenant-aware ex ==== GraphQL provider extensions -For GraphQL schema extensions, see `samples/graphql-providers` and <<_graphql_api,GraphQL API>>. +For GraphQL schema extensions, see `graphql/cxs-impl` and <<_graphql_api,GraphQL API>>. + +==== Reference plugins in the codebase -==== Repository samples +Use maintained modules in the main tree — not the older `samples/` demos (those predate DS migration and Blueprint removal). [cols="2,3", options="header"] |=== -| Sample | What it demonstrates - -| `samples/trainingplugin` | Minimal action + rule + Blueprint (best starting point) -| `samples/tweet-button-plugin` | Custom action, rule, and web integration -| `samples/login-integration` | Login rule and profile merge -| `samples/groovy-actions` | Groovy action scripts in a bundle -| `samples/graphql-providers` | GraphQL type extensions +| Module | What to study + +| `plugins/advanced-conditions` | DS `ConditionEvaluator` + `pom.xml` with `_dsannotations` (start here for condition plugins) +| `plugins/kafka-injector` | DS lifecycle (`@Activate`, `@Deactivate`, `@Reference`) +| `extensions/lists-extension/actions` | Action type JSON + `AddToListsAction` logic +| `graphql/cxs-impl/.../actions` | DS `ActionExecutor` registrations +| `persistence-elasticsearch/core` | Query builder class implementations (study `querybuilders/core/`) +| `persistence-opensearch/core` | OpenSearch query builder counterparts |=== -Manual walkthroughs: <<_samples,Samples>>. +The `samples/` directory holds historical integration demos (Twitter button, login page, etc.). +They illustrate end-to-end scenarios but are **not** maintained plugin templates — copy patterns from the modules above instead. + +Scenario walkthroughs that use samples: <<_samples,Samples>>. ==== Best practices * Prefer Groovy actions or JSON-only bundles before writing Java when possible. +* Register OSGi services with **Declarative Services** (`@Component`); do not add new Blueprint XML. * Import `unomi-bom`; use `provided` scope for Unomi artifacts inside Karaf. * Use the same `queryBuilder` ID in JSON and in both ES/OS service registrations. * Put evaluators and JSON descriptors in the **common** bundle; query builders in engine-specific bundles. @@ -570,8 +638,8 @@ Study these modules when building persistence-related plugins: | Elasticsearch persistence | `persistence-elasticsearch/core` | OpenSearch persistence | `persistence-opensearch/core` -| Built-in condition evaluators | `plugins/baseplugin` -| Advanced evaluators (DS) | `plugins/advanced-conditions` +| Built-in condition evaluators (legacy Blueprint) | `plugins/baseplugin` — study evaluators only, not wiring +| Advanced evaluators (DS, preferred) | `plugins/advanced-conditions` | Distribution features | `distribution/src/main/feature/feature.xml` |=== From 17605da861cf9034e521803f6010c6b7abd5249f Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 09:23:18 +0200 Subject: [PATCH 15/19] UNOMI-960: Tier 1 DS documentation (scheduler, CLAUDE.md) Replace Blueprint scheduler wiring with Declarative Services examples and state DS-first OSGi guidance in CLAUDE.md for contributors and agents. --- CLAUDE.md | 6 ++-- manual/src/main/asciidoc/scheduler.adoc | 40 ++++++++++++++++++------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 73593ec7b..032267145 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,8 +17,10 @@ limitations under the License. # Apache Unomi — CLAUDE.md Apache Unomi is a Java/OSGi customer data platform running on Apache Karaf. Multi-module Maven -reactor; most runtime code is packaged as OSGi bundles wired via Blueprint XML -(`OSGI-INF/blueprint/blueprint.xml` in each module), not Spring/CDI. +reactor; runtime code is packaged as OSGi bundles. **New code uses OSGi Declarative Services** +(`@Component`, `@Reference`, `@Activate`). Legacy modules still ship Blueprint XML +(`OSGI-INF/blueprint/blueprint.xml`) during migration — do not add new Blueprint files; see +`CODING_GUIDELINES.md` and `manual/.../writing-plugins.adoc`. ## Dual persistence backends — the #1 thing to know diff --git a/manual/src/main/asciidoc/scheduler.adoc b/manual/src/main/asciidoc/scheduler.adoc index 113bd05bc..f59b42da6 100644 --- a/manual/src/main/asciidoc/scheduler.adoc +++ b/manual/src/main/asciidoc/scheduler.adoc @@ -592,21 +592,41 @@ public class BulkReindexExecutor implements TaskExecutor { After a crash, operators can call `POST /cxs/tasks/{id}/resume` or `unomi:task-retry` depending on the failure mode. -==== OSGi Blueprint wiring +==== OSGi Declarative Services wiring -Reference `SchedulerService` from your plugin blueprint: +Inject `SchedulerService` with `@Reference` in your plugin component. +New plugins use Declarative Services — not Blueprint XML (see <<_writing_plugins,Writing plugins>>). -[source,xml] +[source,java] ---- - +@Component(service = MyPluginService.class) +public class MyPluginService { + + private SchedulerService schedulerService; + private TaskExecutor registeredExecutor; - - - + @Reference + public void setSchedulerService(SchedulerService schedulerService) { + this.schedulerService = schedulerService; + } + + @Activate + public void activate() { + registeredExecutor = new MyTaskExecutor(); + schedulerService.registerTaskExecutor(registeredExecutor); + } + + @Deactivate + public void deactivate() { + if (schedulerService != null && registeredExecutor != null) { + schedulerService.unregisterTaskExecutor(registeredExecutor); + } + } +} ---- -Register the executor in your component's `activate()` method and unregister in `deactivate()` when possible. +Register the executor in `activate()` and unregister in `deactivate()` when possible. +Pattern A above uses the same `@Reference` injection style for `SchedulerService`. === Built-in tasks you will see in production @@ -672,5 +692,5 @@ long failures = schedulerService.getMetric("tasks.failed"); * <<_request_examples,Tenant REST examples>> — creating tenants whose data background tasks will process * <<_shell_commands,Karaf shell>> — operational commands including `unomi:task-*` -* <<_writing_plugins,Writing plugins>> — plugin structure and OSGi wiring +* <<_writing_plugins,Writing plugins>> — DS plugin registration and `META-INF/cxs/` layout * <<_multitenancy,Multitenancy>> — tenant isolation rules From 5d761459b23b1dae0bc88901f8d31392aa1775e3 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 09:54:43 +0200 Subject: [PATCH 16/19] UNOMI-960: Fix manual AsciiDoc build warnings and anchor IDs Use explicit [#id] section anchors for stable HTML/PDF cross-references, resolve duplicate IDs and PlantUML/table issues, and align PDF idseparator with underscore-based links so mvn clean install in manual builds cleanly. --- manual/pom.xml | 2 +- .../src/main/asciidoc/5-min-quickstart.adoc | 2 +- .../main/asciidoc/building-and-deploying.adoc | 4 ++-- .../main/asciidoc/builtin-action-types.adoc | 2 +- .../asciidoc/builtin-condition-types.adoc | 2 +- .../main/asciidoc/builtin-event-types.adoc | 2 +- manual/src/main/asciidoc/clustering.adoc | 2 +- .../main/asciidoc/condition-evaluation.adoc | 2 +- .../main/asciidoc/condition-validation.adoc | 2 +- .../src/main/asciidoc/conditions-guide.adoc | 4 ++-- manual/src/main/asciidoc/configuration.adoc | 2 +- .../connectors/salesforce-connector.adoc | 2 +- manual/src/main/asciidoc/consent-api.adoc | 2 +- manual/src/main/asciidoc/datamodel.adoc | 6 +++--- manual/src/main/asciidoc/foreword.adoc | 2 +- manual/src/main/asciidoc/getting-started.adoc | 4 ++-- .../asciidoc/how-profile-tracking-works.adoc | 2 +- manual/src/main/asciidoc/index.adoc | 21 ++++++++++--------- .../jsonSchema/extend-an-existing-schema.adoc | 2 +- .../asciidoc/jsonSchema/json-schema-api.adoc | 2 +- .../migrations/migrate-2.x-to-3.0.adoc | 3 ++- .../migrations/migrate-3.0-to-3.1.adoc | 2 +- .../migrate-elasticsearch-to-opensearch.adoc | 2 +- .../migrations/migrate-es7-to-es9.adoc | 2 +- .../main/asciidoc/migrations/migrations.adoc | 4 ---- .../migrations/v2-compatibility-mode.adoc | 2 +- .../migrations/writing-migration-scripts.adoc | 2 +- manual/src/main/asciidoc/multitenancy.adoc | 5 +++-- .../main/asciidoc/past-event-conditions.adoc | 5 ++--- manual/src/main/asciidoc/patches.adoc | 2 +- manual/src/main/asciidoc/property-types.adoc | 2 +- .../asciidoc/queries-and-aggregations.adoc | 1 - manual/src/main/asciidoc/recipes.adoc | 2 +- .../src/main/asciidoc/request-examples.adoc | 4 ++-- .../main/asciidoc/samples/login-sample.adoc | 2 +- manual/src/main/asciidoc/samples/samples.adoc | 2 +- .../main/asciidoc/samples/twitter-sample.adoc | 2 +- manual/src/main/asciidoc/scheduler.adoc | 11 +++++----- manual/src/main/asciidoc/shell-commands.adoc | 3 ++- .../asciidoc/upgrades/platform-upgrades.adoc | 2 +- .../upgrades/upgrade-cxf-jackson-jetty.adoc | 2 +- .../upgrades/upgrade-dependency-hygiene.adoc | 2 +- .../upgrades/upgrade-elasticsearch.adoc | 2 +- .../upgrades/upgrade-integration-tests.adoc | 4 ++-- .../main/asciidoc/upgrades/upgrade-java.adoc | 2 +- .../upgrades/upgrade-jira-reference.adoc | 2 +- .../main/asciidoc/upgrades/upgrade-karaf.adoc | 2 +- .../main/asciidoc/upgrades/upgrade-log4j.adoc | 4 ++-- .../asciidoc/upgrades/upgrade-opensearch.adoc | 2 +- .../asciidoc/upgrades/upgrade-overview.adoc | 2 +- .../src/main/asciidoc/useful-unomi-urls.adoc | 2 +- manual/src/main/asciidoc/web-tracker.adoc | 2 +- manual/src/main/asciidoc/whats-new.adoc | 5 +++-- manual/src/main/asciidoc/writing-plugins.adoc | 7 ++++--- 54 files changed, 84 insertions(+), 85 deletions(-) diff --git a/manual/pom.xml b/manual/pom.xml index aa1b41eaf..a3ecce82d 100644 --- a/manual/pom.xml +++ b/manual/pom.xml @@ -115,7 +115,7 @@ true - - + _ true diff --git a/manual/src/main/asciidoc/5-min-quickstart.adoc b/manual/src/main/asciidoc/5-min-quickstart.adoc index bbf4b4c4f..e1aacb07d 100644 --- a/manual/src/main/asciidoc/5-min-quickstart.adoc +++ b/manual/src/main/asciidoc/5-min-quickstart.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_five_minutes_quickstart]] +[#_five_minutes_quickstart] === Quick start with Docker Begin by creating a `docker-compose.yml` file. You can choose between ElasticSearch or OpenSearch: diff --git a/manual/src/main/asciidoc/building-and-deploying.adoc b/manual/src/main/asciidoc/building-and-deploying.adoc index a18013838..ad9987d17 100644 --- a/manual/src/main/asciidoc/building-and-deploying.adoc +++ b/manual/src/main/asciidoc/building-and-deploying.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_building]] +[#_building] === Building Apache Unomi 3.x provides a convenient `build.sh` script that simplifies the build process with @@ -72,7 +72,7 @@ TIP: On a non-English Windows env, the Asciidoctor Maven Plugin may fail to + . The distributions will be available under "package/target" directory. -[[_using_the_build_script]] +[#_using_the_build_script] ==== Using the build.sh script Apache Unomi 3.x provides a unified `build.sh` script that simplifies the build, deployment, and execution process. diff --git a/manual/src/main/asciidoc/builtin-action-types.adoc b/manual/src/main/asciidoc/builtin-action-types.adoc index 222005095..5fd36ea80 100644 --- a/manual/src/main/asciidoc/builtin-action-types.adoc +++ b/manual/src/main/asciidoc/builtin-action-types.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_builtin_action_types]] +[#_builtin_action_types] === Built-in action types Actions run when a rule matches. Each action type references an `actionExecutor` OSGi service. diff --git a/manual/src/main/asciidoc/builtin-condition-types.adoc b/manual/src/main/asciidoc/builtin-condition-types.adoc index 9500d0e50..cce4a776c 100644 --- a/manual/src/main/asciidoc/builtin-condition-types.adoc +++ b/manual/src/main/asciidoc/builtin-condition-types.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_builtin_condition_types]] +[#_builtin_condition_types] === Built-in condition types Apache Unomi ships with built-in condition types in the base plugin and optional extensions. diff --git a/manual/src/main/asciidoc/builtin-event-types.adoc b/manual/src/main/asciidoc/builtin-event-types.adoc index 74ddd7122..800c1b3fe 100644 --- a/manual/src/main/asciidoc/builtin-event-types.adoc +++ b/manual/src/main/asciidoc/builtin-event-types.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_builtin_event_types]] +[#_builtin_event_types] === Built-in Event types Apache Unomi comes with built-in event types, which we describe below. diff --git a/manual/src/main/asciidoc/clustering.adoc b/manual/src/main/asciidoc/clustering.adoc index e7b556da8..6ffd776e4 100644 --- a/manual/src/main/asciidoc/clustering.adoc +++ b/manual/src/main/asciidoc/clustering.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_clustering]] +[#_clustering] === Cluster setup Apache Unomi runs as one or more Karaf nodes that share the same Elasticsearch or OpenSearch backend. diff --git a/manual/src/main/asciidoc/condition-evaluation.adoc b/manual/src/main/asciidoc/condition-evaluation.adoc index 3103b973b..2171d911c 100644 --- a/manual/src/main/asciidoc/condition-evaluation.adoc +++ b/manual/src/main/asciidoc/condition-evaluation.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_condition_evaluation]] +[#_condition_evaluation] === Condition Evaluation System For examples and progressive tutorials, see <<_conditions_guide,Conditions guide>>. diff --git a/manual/src/main/asciidoc/condition-validation.adoc b/manual/src/main/asciidoc/condition-validation.adoc index 93e78f16a..fd4aaebcf 100644 --- a/manual/src/main/asciidoc/condition-validation.adoc +++ b/manual/src/main/asciidoc/condition-validation.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_condition_validation]] +[#_condition_validation] === Condition validation Apache Unomi validates condition instances against their condition type definitions when you save segments, rules, queries, goals, and related items. diff --git a/manual/src/main/asciidoc/conditions-guide.adoc b/manual/src/main/asciidoc/conditions-guide.adoc index 652145bed..f6e8b871c 100644 --- a/manual/src/main/asciidoc/conditions-guide.adoc +++ b/manual/src/main/asciidoc/conditions-guide.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_conditions_guide]] +[#_conditions_guide] === Conditions guide Conditions are the building blocks of segmentation, personalization, rules, queries, campaigns, and goals in Apache Unomi. @@ -338,7 +338,7 @@ To add new condition types, implement a `ConditionEvaluator` and search-engine-s Add `validation` metadata on parameters so invalid instances are rejected at save time — see <<_condition_validation,Condition validation>>. -[[_debugging_conditions]] +[#_debugging_conditions] ==== Debugging conditions When a segment or rule does not behave as expected, use these approaches in order. diff --git a/manual/src/main/asciidoc/configuration.adoc b/manual/src/main/asciidoc/configuration.adoc index 461d0357f..c1318b299 100644 --- a/manual/src/main/asciidoc/configuration.adoc +++ b/manual/src/main/asciidoc/configuration.adoc @@ -1751,7 +1751,7 @@ cd apache-unomi-custom-* unomi:start elasticsearch-prod ---- -[[_health_check]] +[#_health_check] === Health Check Extension The Health Check extension provides a way to check is required Unomi components are 'live'. diff --git a/manual/src/main/asciidoc/connectors/salesforce-connector.adoc b/manual/src/main/asciidoc/connectors/salesforce-connector.adoc index 73998c96e..2ded160cc 100644 --- a/manual/src/main/asciidoc/connectors/salesforce-connector.adoc +++ b/manual/src/main/asciidoc/connectors/salesforce-connector.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_salesforce_connector]] +[#_salesforce_connector] === Salesforce Connector This connectors makes it possible to push and pull data to/from the Salesforce CRM. It can copy information between diff --git a/manual/src/main/asciidoc/consent-api.adoc b/manual/src/main/asciidoc/consent-api.adoc index ad23f7ac5..b9107b0f4 100644 --- a/manual/src/main/asciidoc/consent-api.adoc +++ b/manual/src/main/asciidoc/consent-api.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_consent_api]] +[#_consent_api] === Consent API Starting with Apache Unomi 1.3, a new API for consent management is now available. This API diff --git a/manual/src/main/asciidoc/datamodel.adoc b/manual/src/main/asciidoc/datamodel.adoc index bfe46de2f..f22571bd2 100755 --- a/manual/src/main/asciidoc/datamodel.adoc +++ b/manual/src/main/asciidoc/datamodel.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_data_model_overview]] +[#_data_model_overview] === Data Model Overview Apache Unomi gathers information about users actions, information that is processed and stored by Unomi services. @@ -61,7 +61,7 @@ These properties are further defined by the `Item`’s type definition which exp By defining new types, users specify which properties (including the type of values they accept) are available to items of that specific type. -[[_tenant_entity]] +[#_tenant_entity] === Tenant (Unomi 3.1+) A *tenant* is the top-level isolation boundary in Unomi 3.1. Each tenant owns its own profiles, events, segments, rules, property types, and JSON schemas. Tenant metadata is stored as an `Item` with `itemType` `tenant`. @@ -627,7 +627,7 @@ The visitor’s location is also resolve based on the IP address that was used t } ---- -[[_segment]] +[#_segment] === Segment Segments are used to group profiles together, and are based on conditions that are executed on profiles to determine diff --git a/manual/src/main/asciidoc/foreword.adoc b/manual/src/main/asciidoc/foreword.adoc index 4cacc9233..eaaebb856 100644 --- a/manual/src/main/asciidoc/foreword.adoc +++ b/manual/src/main/asciidoc/foreword.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_foreword]] +[#_foreword] == Foreword This documentation provides a comprehensive guide to Apache Unomi 3.x, a powerful and flexible Customer Data Platform (CDP) that enables organizations to collect, manage, and leverage customer data for personalization and marketing automation. diff --git a/manual/src/main/asciidoc/getting-started.adoc b/manual/src/main/asciidoc/getting-started.adoc index 22fd2788c..ac9fe5b09 100644 --- a/manual/src/main/asciidoc/getting-started.adoc +++ b/manual/src/main/asciidoc/getting-started.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_getting_started_with_unomi]] +[#_getting_started_with_unomi] === Getting started with Unomi We will first get you up and running with an example. We will then lift the corner of the cover somewhat and explain @@ -22,7 +22,7 @@ in greater details what just happened. This document assumes working knowledge of https://git-scm.com/[git] to be able to retrieve the code for Unomi and the example. Additionally, you will require a working Java 17 or above install. Refer to http://www.oracle.com/technetwork/java/javase/[http://www.oracle.com/technetwork/java/javase/] for details on how to download and install Java SE 17 or greater. -[[_jdk_compatibility]] +[#_jdk_compatibility] ===== JDK compatibility Starting with Java 9, Oracle made some big changes to the Java platform releases. This is why Apache Unomi is focused on diff --git a/manual/src/main/asciidoc/how-profile-tracking-works.adoc b/manual/src/main/asciidoc/how-profile-tracking-works.adoc index db3e9ebd4..587ba0f9b 100644 --- a/manual/src/main/asciidoc/how-profile-tracking-works.adoc +++ b/manual/src/main/asciidoc/how-profile-tracking-works.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_how_profile_tracking_works]] +[#_how_profile_tracking_works] === How profile tracking works In this section you will learn how Apache Unomi keeps track of visitors and processes context requests. diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc index c4c797190..91d1723e8 100644 --- a/manual/src/main/asciidoc/index.adoc +++ b/manual/src/main/asciidoc/index.adoc @@ -26,7 +26,7 @@ image::asf_logo_url.png[pdfwidth=35%,align=center] include::foreword.adoc[] -[[_whats_new]] +[#_whats_new] == What's new include::whats-new.adoc[] @@ -45,34 +45,34 @@ include::recipes.adoc[] include::request-examples.adoc[] -[[_configuration]] +[#_configuration] == Configuration include::configuration.adoc[] -[[_multitenancy]] +[#_multitenancy] == Multi-tenancy include::multitenancy.adoc[] -[[_json_schemas]] +[#_json_schemas] == JSON schemas include::jsonSchema/json-schema.adoc[] -[[_graphql_api]] +[#_graphql_api] == GraphQL API include::graphql.adoc[] include::graphql-examples.adoc[] -[[_migrations]] +[#_migrations] == Migrations include::migrations/migrations.adoc[] -[[_conditions]] +[#_conditions] == Conditions include::conditions-guide.adoc[] @@ -83,6 +83,7 @@ include::condition-evaluation.adoc[] include::past-event-conditions.adoc[] +[#_queries_and_aggregations] == Queries and aggregations include::queries-and-aggregations.adoc[] @@ -103,12 +104,12 @@ include::privacy.adoc[] include::clustering.adoc[] -[[_scheduler]] +[#_scheduler] == Task scheduler include::scheduler.adoc[] -[[_health_check_chapter]] +[#_health_check_chapter] == Health check The health check endpoint and providers are documented in the Configuration chapter: <<_health_check,Health check extension>>. @@ -151,7 +152,7 @@ include::web-tracker.adoc[] include::javascript-tracker-guide.adoc[] -[[_integration_samples]] +[#_integration_samples] == Integration samples include::samples/samples.adoc[] diff --git a/manual/src/main/asciidoc/jsonSchema/extend-an-existing-schema.adoc b/manual/src/main/asciidoc/jsonSchema/extend-an-existing-schema.adoc index 204da1dff..6a147577d 100644 --- a/manual/src/main/asciidoc/jsonSchema/extend-an-existing-schema.adoc +++ b/manual/src/main/asciidoc/jsonSchema/extend-an-existing-schema.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_extend_an_existing_schema]] +[#_extend_an_existing_schema] === Extend an existing schema ==== When a extension is needed? diff --git a/manual/src/main/asciidoc/jsonSchema/json-schema-api.adoc b/manual/src/main/asciidoc/jsonSchema/json-schema-api.adoc index 1b908c206..2d4a01440 100644 --- a/manual/src/main/asciidoc/jsonSchema/json-schema-api.adoc +++ b/manual/src/main/asciidoc/jsonSchema/json-schema-api.adoc @@ -71,7 +71,7 @@ curl --location --request POST 'http://localhost:8181/cxs/jsonSchema/query' \ NOTE: Replace `TENANT_ID` and `PRIVATE_KEY` with your actual tenant ID and private API key. You can obtain these when creating a tenant via the Tenant API (which requires system administrator authentication). -[[_create_update_json_schema]] +[#_create_update_json_schema] ==== Create / update a JSON schema to validate an event It’s possible to add or update JSON schema by calling the endpoint `POST {{url}}/cxs/jsonSchema` with the JSON schema in the payload of the request. diff --git a/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc b/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc index 34f7143f0..5dd4e2da1 100644 --- a/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc @@ -12,6 +12,7 @@ // limitations under the License. // +[#_migrate_from_2_x_to_3_0] ==== Migration Overview Apache Unomi 3.0 is a major release that introduces several breaking changes. This document details the steps required to successfully migrate your environment from Apache Unomi 2.x to Apache Unomi 3.0. @@ -265,7 +266,7 @@ When using Docker, you can specify the search engine backend using the distribut Apache Unomi 3.1 introduces official support for OpenSearch as an alternative to Elasticsearch. If you want to migrate from Elasticsearch to OpenSearch, you can use the dedicated migration guide located in the 3.0 → 3.1 migration section. -For detailed instructions on migrating your data from Elasticsearch to OpenSearch, please refer to the <<_migrating_from_elasticsearch_to_opensearch,Elasticsearch to OpenSearch migration guide>>. +For detailed instructions on migrating your data from Elasticsearch to OpenSearch, please refer to the <<_migrate_from_elasticsearch_to_opensearch,Elasticsearch to OpenSearch migration guide>>. ==== Migration Checklist diff --git a/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc b/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc index a4a58c242..a107ce7a7 100644 --- a/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-3.0-to-3.1.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_migrate_from_3_0_to_3_1]] +[#_migrate_from_3_0_to_3_1] === Migration Overview Apache Unomi 3.1 introduces comprehensive multi-tenancy support, enabling complete data isolation between different tenants. This fundamental architectural change requires a new tenant-based authentication model while keeping all API endpoints unchanged. diff --git a/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc b/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc index ffd2a3a76..b68e29ac3 100644 --- a/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-elasticsearch-to-opensearch.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_migrating_from_elasticsearch_to_opensearch]] +[#_migrate_from_elasticsearch_to_opensearch] ==== Migrating from Elasticsearch to OpenSearch :toc: macro :toclevels: 4 diff --git a/manual/src/main/asciidoc/migrations/migrate-es7-to-es9.adoc b/manual/src/main/asciidoc/migrations/migrate-es7-to-es9.adoc index 4afcdf17e..8e03b788f 100644 --- a/manual/src/main/asciidoc/migrations/migrate-es7-to-es9.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-es7-to-es9.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_migrate_from_elasticsearch_7_to_elasticsearch_9]] +[#_migrate_from_elasticsearch_7_to_elasticsearch_9] ==== Migrate from Elasticsearch 7 to Elasticsearch 9 You can use the *remote reindex* API to upgrade directly from Elasticsearch 7 to Elasticsearch 9. This approach runs both clusters in parallel and uses Elasticsearch's remote reindex feature. diff --git a/manual/src/main/asciidoc/migrations/migrations.adoc b/manual/src/main/asciidoc/migrations/migrations.adoc index b096abe8a..b5a24d4c6 100644 --- a/manual/src/main/asciidoc/migrations/migrations.adoc +++ b/manual/src/main/asciidoc/migrations/migrations.adoc @@ -14,16 +14,12 @@ This section contains information and steps to migrate between major Unomi versions. -=== Writing migration scripts - include::writing-migration-scripts.adoc[] === V2/V3 API Compatibility Guide include::v2-v3-compatibility.adoc[] -=== V2 Compatibility Mode - include::v2-compatibility-mode.adoc[] === From version 3.0 to 3.1 diff --git a/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc b/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc index 4e956cd6b..4a7e54f30 100644 --- a/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc +++ b/manual/src/main/asciidoc/migrations/v2-compatibility-mode.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_v2_compatibility_mode]] +[#_v2_compatibility_mode] === V2 compatibility mode This document explains how to use the V2 compatibility mode in Apache Unomi V3, which allows V2 client applications to work with Unomi V3 without requiring API keys. diff --git a/manual/src/main/asciidoc/migrations/writing-migration-scripts.adoc b/manual/src/main/asciidoc/migrations/writing-migration-scripts.adoc index b0615bb5d..64f543ae0 100644 --- a/manual/src/main/asciidoc/migrations/writing-migration-scripts.adoc +++ b/manual/src/main/asciidoc/migrations/writing-migration-scripts.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_writing_migration_scripts]] +[#_writing_migration_scripts] === Writing migration scripts This section is for **contributors** who add or change Groovy migration scripts shipped in the `shell-commands` module. diff --git a/manual/src/main/asciidoc/multitenancy.adoc b/manual/src/main/asciidoc/multitenancy.adoc index a4b3efb1d..e91c30e29 100644 --- a/manual/src/main/asciidoc/multitenancy.adoc +++ b/manual/src/main/asciidoc/multitenancy.adoc @@ -255,11 +255,12 @@ To define custom operations: Example: 1. Add to `etc/org.apache.unomi.security.cfg`: ++ [source,properties] ---- operation.roles.CUSTOM_OPERATION=ROLE_UNOMI_TENANT_ADMIN ---- - ++ 2. Use in your code: [source,java] ---- @@ -770,7 +771,7 @@ curl -X POST http://localhost:8181/cxs/context.json \ }] }' ---- - ++ 2. Verify profile properties were updated: [source,bash] ---- diff --git a/manual/src/main/asciidoc/past-event-conditions.adoc b/manual/src/main/asciidoc/past-event-conditions.adoc index 379d26aff..d038f263b 100644 --- a/manual/src/main/asciidoc/past-event-conditions.adoc +++ b/manual/src/main/asciidoc/past-event-conditions.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_past_event_conditions]] +[#_past_event_conditions] === Past Event Conditions in Apache Unomi :toc: macro :toclevels: 4 @@ -410,8 +410,7 @@ package "Past Event System" { [SegmentServiceImpl] as segmentService } -database "Search index -(Elasticsearch or OpenSearch)" { +database "Search index (Elasticsearch or OpenSearch)" as searchIndex { [Events] as events [Profiles] as profiles } diff --git a/manual/src/main/asciidoc/patches.adoc b/manual/src/main/asciidoc/patches.adoc index 9425df560..7e4d94370 100644 --- a/manual/src/main/asciidoc/patches.adoc +++ b/manual/src/main/asciidoc/patches.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_migration_patches]] +[#_migration_patches] === Migration patches You may provide patches on any predefined items by simply adding a JSON file in : diff --git a/manual/src/main/asciidoc/property-types.adoc b/manual/src/main/asciidoc/property-types.adoc index cccc33b0a..dec203ad3 100644 --- a/manual/src/main/asciidoc/property-types.adoc +++ b/manual/src/main/asciidoc/property-types.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_property_types]] +[#_property_types] == Property Types Property types define the structure and metadata for properties that can be used in profiles and sessions within Apache Unomi. They specify the data type, display hints (ranges, constraints), default values, and other metadata that help inform UIs and enable rich querying capabilities. diff --git a/manual/src/main/asciidoc/queries-and-aggregations.adoc b/manual/src/main/asciidoc/queries-and-aggregations.adoc index 2c9a8a68d..2f028f257 100644 --- a/manual/src/main/asciidoc/queries-and-aggregations.adoc +++ b/manual/src/main/asciidoc/queries-and-aggregations.adoc @@ -11,7 +11,6 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_queries_and_aggregations]] Apache Unomi contains a `query` endpoint that is quite powerful. It provides ways to perform queries that can quickly get result counts, apply metrics such as sum/min/max/avg or even use powerful aggregations. diff --git a/manual/src/main/asciidoc/recipes.adoc b/manual/src/main/asciidoc/recipes.adoc index fae5bf2c4..8f0f8c9ce 100644 --- a/manual/src/main/asciidoc/recipes.adoc +++ b/manual/src/main/asciidoc/recipes.adoc @@ -18,7 +18,7 @@ In this section of the documentation we provide quick recipes focused on helping you achieve a specific result with Apache Unomi. -[[_enabling_debug_mode]] +[#_enabling_debug_mode] ==== Enabling debug mode Although the examples provided in this documentation are correct (they will work "as-is"), diff --git a/manual/src/main/asciidoc/request-examples.adoc b/manual/src/main/asciidoc/request-examples.adoc index abdbbc0ae..e8a8e6415 100644 --- a/manual/src/main/asciidoc/request-examples.adoc +++ b/manual/src/main/asciidoc/request-examples.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_request_examples]] +[#_request_examples] === Request examples ==== Prerequisites @@ -1382,7 +1382,7 @@ Best practices for Groovy actions: 5. Test with various date formats ==== -[[_request_tracing_explain]] +[#_request_tracing_explain] ==== Using the explain parameter for request tracing Apache Unomi provides a powerful request tracing feature through the `explain` query parameter. This feature helps administrators understand how requests are processed internally, including event processing, condition evaluations, and rule executions. diff --git a/manual/src/main/asciidoc/samples/login-sample.adoc b/manual/src/main/asciidoc/samples/login-sample.adoc index ef04b18c8..30b7f1aee 100644 --- a/manual/src/main/asciidoc/samples/login-sample.adoc +++ b/manual/src/main/asciidoc/samples/login-sample.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_login_sample]] +[#_login_sample] === Login sample This samples is an example of what is involved in integrated a login with Apache Unomi. diff --git a/manual/src/main/asciidoc/samples/samples.adoc b/manual/src/main/asciidoc/samples/samples.adoc index 667baa242..a7f0db0b7 100644 --- a/manual/src/main/asciidoc/samples/samples.adoc +++ b/manual/src/main/asciidoc/samples/samples.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_samples]] +[#_samples] === Samples Apache Unomi provides the following integration demos in `samples/`. diff --git a/manual/src/main/asciidoc/samples/twitter-sample.adoc b/manual/src/main/asciidoc/samples/twitter-sample.adoc index 7ac2c65ec..a45955d90 100644 --- a/manual/src/main/asciidoc/samples/twitter-sample.adoc +++ b/manual/src/main/asciidoc/samples/twitter-sample.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_twitter_sample]] +[#_twitter_sample] === Twitter sample ==== Overview diff --git a/manual/src/main/asciidoc/scheduler.adoc b/manual/src/main/asciidoc/scheduler.adoc index f59b42da6..1f5976b42 100644 --- a/manual/src/main/asciidoc/scheduler.adoc +++ b/manual/src/main/asciidoc/scheduler.adoc @@ -12,7 +12,6 @@ // limitations under the License. // -[[_scheduler]] === Cluster-aware task scheduler Apache Unomi 3.1 introduces a built-in **task scheduler** for background work that must run reliably in single-node and clustered deployments. @@ -53,8 +52,8 @@ package "REST / Shell" { [Karaf commands\nunomi:task-*] as Shell } -package "API" { - [SchedulerService] as API +package "Scheduler API" { + [SchedulerService] as SchedAPI [TaskExecutor] as ExecutorAPI [ScheduledTask] as TaskModel } @@ -69,9 +68,9 @@ package "Services (per node)" { database "Persistence\n(OpenSearch / ES)" as DB -REST --> API -Shell --> API -API --> Impl +REST --> SchedAPI +Shell --> SchedAPI +SchedAPI --> Impl Impl --> Exec Impl --> Lock Impl --> Recovery diff --git a/manual/src/main/asciidoc/shell-commands.adoc b/manual/src/main/asciidoc/shell-commands.adoc index 654099053..801a82de8 100644 --- a/manual/src/main/asciidoc/shell-commands.adoc +++ b/manual/src/main/asciidoc/shell-commands.adoc @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // +[#_shell_commands] === SSH Shell Commands Apache Unomi provides its own Apache Karaf Shell commands to make it easy to control the application @@ -284,6 +285,6 @@ These commands manage background tasks created by the cluster-aware scheduler (s |Delete old `COMPLETED` tasks (default: older than 7 days). `-f` skips confirmation. |task-executor -|[true|false] +|boolean |Show whether this node runs tasks and its scheduler `nodeId`. |=== diff --git a/manual/src/main/asciidoc/upgrades/platform-upgrades.adoc b/manual/src/main/asciidoc/upgrades/platform-upgrades.adoc index 1430bc391..6ee34c702 100644 --- a/manual/src/main/asciidoc/upgrades/platform-upgrades.adoc +++ b/manual/src/main/asciidoc/upgrades/platform-upgrades.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_platform_upgrades]] +[#_platform_upgrades] === Platform upgrades This section is for Apache Unomi *maintainers* upgrading runtime platform components: Apache Karaf, JDK, CXF/Jackson/Jetty, Elasticsearch and OpenSearch clients, Log4j, integration tests, and application dependency hygiene. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-cxf-jackson-jetty.adoc b/manual/src/main/asciidoc/upgrades/upgrade-cxf-jackson-jetty.adoc index 85f1d6d6b..2a29e8dd7 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-cxf-jackson-jetty.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-cxf-jackson-jetty.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_cxf_jackson_jetty]] +[#_upgrade_cxf_jackson_jetty] ==== CXF, Jackson, and Jetty (Karaf cascade libraries) Karaf upgrades change the *runtime* versions of several frameworks even when you only bump `${karaf.version}`. Unomi pins some of these in root `pom.xml` and `kar/src/main/feature/feature.xml` so compile-time, feature verify, and OSGi resolution stay aligned. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-dependency-hygiene.adoc b/manual/src/main/asciidoc/upgrades/upgrade-dependency-hygiene.adoc index f362ea1bc..99dd6fc18 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-dependency-hygiene.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-dependency-hygiene.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_dependency_hygiene]] +[#_upgrade_dependency_hygiene] ==== Dependency hygiene (non-platform bumps) Not every version bump is a Karaf or persistence migration. PR2-style hygiene (UNOMI-875 `#795`, `ef10dfe15`) covers *application* dependencies that must *not* break OSGi rules established in platform upgrades. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-elasticsearch.adoc b/manual/src/main/asciidoc/upgrades/upgrade-elasticsearch.adoc index 8673ee107..f1d1d1546 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-elasticsearch.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-elasticsearch.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_elasticsearch]] +[#_upgrade_elasticsearch] ==== Upgrading the Elasticsearch client Unomi does *not* embed the Elasticsearch server. It embeds the *Java API client* and REST transport inside the `unomi-persistence-elasticsearch-core` OSGi bundle, then exports query DSL types for the conditions bundle. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-integration-tests.adoc b/manual/src/main/asciidoc/upgrades/upgrade-integration-tests.adoc index 2e839c36f..1b40307e6 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-integration-tests.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-integration-tests.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_integration_tests]] +[#_upgrade_integration_tests] ==== Integration tests and Docker Platform upgrades are validated by *Pax Exam* integration tests plus *Docker-backed* search engines. Failures often appear in Maven Failsafe output, not in `karaf.log`. @@ -117,7 +117,7 @@ Shipped: `elasticsearch-maven-plugin` replaced with `io.fabric8:docker-maven-plu Always run `docker pull` for both images before bumping version properties. -[[_upgrade_integration_tests_pax_exam_probe]] +[#_upgrade_integration_tests_pax_exam_probe] ===== Pax Exam probe and static initialization (Karaf 4.4.11 / Pax Exam 4.14) After a Karaf bump, integration tests may fail *before Unomi starts*. `karaf.log` can look healthy or incomplete while Failsafe reports hundreds of errors in a few seconds — that pattern almost always means the *PAXEXAM-PROBE* bundle failed to load test classes, not a Karaf startup crash. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-java.adoc b/manual/src/main/asciidoc/upgrades/upgrade-java.adoc index 6838d3f1d..790f8105e 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-java.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-java.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_java]] +[#_upgrade_java] ==== Upgrading the JDK A JDK upgrade is almost always bundled with a *major* Karaf migration. Unomi 3.x baseline is *Java 17* (UNOMI-876, `da9c57fd2`). diff --git a/manual/src/main/asciidoc/upgrades/upgrade-jira-reference.adoc b/manual/src/main/asciidoc/upgrades/upgrade-jira-reference.adoc index 954d4ad65..628c624c7 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-jira-reference.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-jira-reference.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_jira_reference]] +[#_upgrade_jira_reference] ==== JIRA reference (platform upgrades) This section maps Apache Unomi JIRA tickets to the upgrade runbooks in this chapter. Use it when you need *why* a constraint exists (Cellar removal, CXF 3.x ceiling, ES client decoupling) or when tracing work under the Unomi 3 epic. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-karaf.adoc b/manual/src/main/asciidoc/upgrades/upgrade-karaf.adoc index 3a31e4110..c45e5333e 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-karaf.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-karaf.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_karaf]] +[#_upgrade_karaf] ==== Upgrading Apache Karaf Karaf upgrades touch the *whole distribution*, not a single module. Scope depends on whether you are doing a *major*, *minor*, or *patch* Karaf bump. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-log4j.adoc b/manual/src/main/asciidoc/upgrades/upgrade-log4j.adoc index 073effd11..3284e9ef3 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-log4j.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-log4j.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_log4j]] +[#_upgrade_log4j] ==== Upgrading Log4j Unomi uses *pax-logging-log4j2* as the platform logging implementation. Custom appenders live in `extensions/log4j-extension` as an OSGi *fragment* on pax-logging. Persistence bundles must *never embed* log4j JARs and must not call Log4j APIs at runtime. @@ -64,7 +64,7 @@ end note Persistence ES/OS cores must *not* call Log4j APIs at runtime. REST client verbosity is configured in `etc/org.ops4j.pax.logging.cfg` (see <<_upgrade_log4j_rest_client,REST client log levels>>). -[[_upgrade_log4j_rest_client]] +[#_upgrade_log4j_rest_client] ===== REST client log levels Elasticsearch and OpenSearch Java clients log through Apache Commons Logging → SLF4J → pax-logging. Do *not* set levels in persistence `start()` methods or persistence CM configs. diff --git a/manual/src/main/asciidoc/upgrades/upgrade-opensearch.adoc b/manual/src/main/asciidoc/upgrades/upgrade-opensearch.adoc index 7219c3667..887094f83 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-opensearch.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-opensearch.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_upgrade_opensearch]] +[#_upgrade_opensearch] ==== Upgrading the OpenSearch client OpenSearch persistence mirrors Elasticsearch: a fat OSGi bundle embeds the Java client and HTTP stack (UNOMI-828, `078ebceda`). diff --git a/manual/src/main/asciidoc/upgrades/upgrade-overview.adoc b/manual/src/main/asciidoc/upgrades/upgrade-overview.adoc index 75dc91e89..21c3315f1 100644 --- a/manual/src/main/asciidoc/upgrades/upgrade-overview.adoc +++ b/manual/src/main/asciidoc/upgrades/upgrade-overview.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_platform_upgrade_overview]] +[#_platform_upgrade_overview] ==== Overview and shared rules ===== Guide index diff --git a/manual/src/main/asciidoc/useful-unomi-urls.adoc b/manual/src/main/asciidoc/useful-unomi-urls.adoc index 846296c88..e1bc00733 100644 --- a/manual/src/main/asciidoc/useful-unomi-urls.adoc +++ b/manual/src/main/asciidoc/useful-unomi-urls.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_useful_apache_unomi_urls]] +[#_useful_apache_unomi_urls] === Useful Apache Unomi URLs In this section we will list some useful URLs that can be used to quickly access parts of Apache Unomi that can help diff --git a/manual/src/main/asciidoc/web-tracker.adoc b/manual/src/main/asciidoc/web-tracker.adoc index 051036b86..0ec98d0fe 100644 --- a/manual/src/main/asciidoc/web-tracker.adoc +++ b/manual/src/main/asciidoc/web-tracker.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_unomi_web_tracker_reference]] +[#_unomi_web_tracker_reference] === Unomi Web Tracker reference In this section of the documentation, more details are provided about the web tracker provided by Unomi. diff --git a/manual/src/main/asciidoc/whats-new.adoc b/manual/src/main/asciidoc/whats-new.adoc index 850710e54..812dcff4b 100644 --- a/manual/src/main/asciidoc/whats-new.adoc +++ b/manual/src/main/asciidoc/whats-new.adoc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -[[_whats_new_3_1]] +[#_whats_new_3_1] === What's new in Apache Unomi 3.1 Apache Unomi 3.1 builds on the 3.0 platform (Elasticsearch 9 client, Karaf 4.4, Java 17) with operator-focused features for multi-tenant production deployments. @@ -30,7 +30,7 @@ Built-in background job scheduler with persistence, cluster locks, recovery, RES * <<_scheduler,Task scheduler>> -[[_opensearch_support]] +[#_opensearch_support] ==== OpenSearch 3 support Official alternative to Elasticsearch, including secured Docker deployments. @@ -54,6 +54,7 @@ Administrators can trace how context and event-collector requests are processed * <<_request_tracing_explain,Using the explain parameter for request tracing>> +[#_whats_new_condition_validation] ==== Condition validation Segments, rules, and queries are validated against condition type definitions at save time. diff --git a/manual/src/main/asciidoc/writing-plugins.adoc b/manual/src/main/asciidoc/writing-plugins.adoc index 0cf004e03..c49808422 100644 --- a/manual/src/main/asciidoc/writing-plugins.adoc +++ b/manual/src/main/asciidoc/writing-plugins.adoc @@ -12,7 +12,7 @@ // limitations under the License. // -[[_writing_plugins]] +[#_writing_plugins] === Writing plugins Apache Unomi is extensible through OSGi plugins (Karaf bundles). Many use cases do **not** need a plugin: @@ -529,7 +529,7 @@ Install at runtime: * Elasticsearch: `feature:install my-plugin-elasticsearch` * OpenSearch: `feature:install my-plugin-opensearch` -[[_custom_distribution_feature]] +[#_custom_distribution_feature] ===== Custom distribution feature For production, add your plugin feature to a custom distribution based on `distribution/src/main/feature/feature.xml`. @@ -564,7 +564,8 @@ Place event schemas under `META-INF/cxs/schemas/`. See <<_json_schemas,JSON Sche `META-INF/cxs/expressions/` — JSON descriptors for allowed scripting expressions (MVEL and related filters). -===== Migration patches +[#_writing_plugins_cxs_patches] +===== CXS migration patch files `META-INF/cxs/patches/` — one-time structural updates. See <<_migration_patches,Migration patches>>. From f5e11e5444c80da706e892dc3b844bc63573e5c0 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 10:50:02 +0200 Subject: [PATCH 17/19] UNOMI-960: Add Unomi-branded manual HTML/PDF styling and responsive navigation Align the manual with unomi.apache.org (Inter, brand colors, hero cover, site nav/footer) and add responsive UX: burger nav plus bottom TOC sheet through 1023px, with desktop sidebar layout from 1024px. --- manual/pom.xml | 9 +- manual/src/main/asciidoc/docinfo-footer.html | 67 + manual/src/main/asciidoc/docinfo.html | 108 + .../asciidoc/images/apache-unomi-logo.png | Bin 0 -> 28161 bytes .../main/asciidoc/images/unomi-favicon.png | Bin 0 -> 17610 bytes manual/src/main/asciidoc/index.adoc | 54 +- manual/src/main/asciidoc/unomi-manual.css | 3241 +++++++++++++++++ manual/src/theme/images/apache-unomi-logo.png | Bin 0 -> 28161 bytes manual/src/theme/unomi-theme.yml | 284 ++ 9 files changed, 3759 insertions(+), 4 deletions(-) create mode 100644 manual/src/main/asciidoc/docinfo-footer.html create mode 100644 manual/src/main/asciidoc/docinfo.html create mode 100644 manual/src/main/asciidoc/images/apache-unomi-logo.png create mode 100644 manual/src/main/asciidoc/images/unomi-favicon.png create mode 100644 manual/src/main/asciidoc/unomi-manual.css create mode 100644 manual/src/theme/images/apache-unomi-logo.png create mode 100644 manual/src/theme/unomi-theme.yml diff --git a/manual/pom.xml b/manual/pom.xml index a3ecce82d..82c7b4858 100644 --- a/manual/pom.xml +++ b/manual/pom.xml @@ -84,8 +84,11 @@ true - apache.css + unomi-manual.css images + highlightjs + ${project.version} + ${project.version} @@ -107,10 +110,12 @@ pdf ${project.version} + ${project.version} ${project.basedir}/src/theme - apache + unomi ${project.basedir}/src/theme/fonts images + ${project.build.directory}/generated-images font true diff --git a/manual/src/main/asciidoc/docinfo-footer.html b/manual/src/main/asciidoc/docinfo-footer.html new file mode 100644 index 000000000..14f82d1cd --- /dev/null +++ b/manual/src/main/asciidoc/docinfo-footer.html @@ -0,0 +1,67 @@ + +

diff --git a/manual/src/main/asciidoc/docinfo.html b/manual/src/main/asciidoc/docinfo.html new file mode 100644 index 000000000..b2d477a46 --- /dev/null +++ b/manual/src/main/asciidoc/docinfo.html @@ -0,0 +1,108 @@ + + + + + + + diff --git a/manual/src/main/asciidoc/images/apache-unomi-logo.png b/manual/src/main/asciidoc/images/apache-unomi-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3da06424ff17a1d697bf4e07936219060f4da1ba GIT binary patch literal 28161 zcmeIb1yEeu^5~5PcL?rIg1ZxfYj6#L0R|ricL~8Yf#B}$?v@}ScyN~h!7aFb5HO%hStGoC5tzNzM?5P=o6cr><5D5?=ARthrrNopWARyiEj@95{ z@BYfL>%F~uL9mw6vV(v?!us`ugh)ujgMi>KHB;5J*OZszGX#T}07hT~Ad@r5`mQ$w zgn+QKHNemUXisJUG%>Rhq}Xe0r64mi5~R@JlxLB*76qD`Nx9krRa_NR4P7k^d5tK9 zg%Aat`R*Kmfc5|~XON|p9iOux#czK3?v8&oGgFZL*2UgJkV52FKr&5vMKV#aEs%_p ziHp&Yg^Qbvo0o}|lbf4^n}Ljtg_V<;g`Jt5g^`V&kAsDegN^K84+0DB=b5Wob;Y;9#C!2DO^ z-+KO;SA3#iOR(+TX#s`U1(^S-`EORg2jFBkW;X<| zG4il;ax!ul^8gun3^@%LdD%D&jM=!k4FHB5zd@9j|GW8rxE2E&I{aEMcdmagEk7!x(mj}#cE*8YQV+_;57g;a&WP*Ga9e~xqpc)KqGD=19n5sKSKP2oBs$U zWoCDmuP%Sf3^uN>VJztMO?g4pNe8lxI zpL^OzT=#%HEI#6Tn9n`!Bd&Wu9u^;QJ(A939S^04@b>tR0kw2!#%0eM(_#Pu+r zd)h}__kcVsKH_?q&pquUu6sZp79Vjv%;%o=5!XE+4~vhu9_DjT`-tlvkcY)bTo3cP zr+vhA56HvfBd&+}+|xedx(DQ8@e$XZHZHTqNH*Ahx1IN~@@L?@xhseHGS=dDh42b7O5SCUYZ6_}m26r5~@T#_{| zm>d-5>VkktQ}4O8b@eVrk1oyX-Gca4M*F_Z?)QgK@VKv$hkyI?Fl*4C+j z_Ad)21q??yoR9T0n|zB&ozM4@>2ZB zR=cViTQ^05-B*j|pUR8utnJ023yl>oJA|KTVvv4Zs%}g5UWfL4vhD$Ay_`4E4Y2^d zTe8nhjCzG;{gWrPIQ+dA>*sBVfwG)joSl0`A*32N=g(g}AF0o0eB-E>#mq@1>HuGL8eiQZZ&X!OWq#-8IlKmYm8M#}Z3pJYJ)dHF$ zu7)HqYhJMw`+`I1rAK;b>~>&cvdTUepY>#|vB&AXdbQA8{pQRMtI10NWqqWax(UY_ zk1p)|>PcH-E>HJr`EyeSCdh^yIyapCZ_hR_!MlKX1+U36U_xx8ONiLM zdYGNbjJN1b56djk&yqs4er-`$bd)WI)SP@^A&+fS>0ZPA-m)e!HpPy= zz7O#NmgGg0Oi}?tLA!DWUCYYy)S(f%FOwoouuLN#`4y~aP}q9=Zp)=Py(h0y&kFo5 z6jQkFv!j*MfxtOBucGCqeu144UawFGw0&HPi9yZpZ;`s1PC*$CZ|x+TPp2nkNW-28 z5Yw~2yM-k&8!Gk`b>~2gh3M`+HHS$Svmgy?Lj4;1TFPw;(eb#by9Qh8@Y<_1`>fvZ%>q~N+7Zy8OX)+~MWEsr+k=E5$F?&`wUuFUo3%haDL@M^Gct`(QZJGQ%t6D|&(MZ#bw&9ZDbk6nTd}syuA5WT5m&4b9YA09Qc(iFP%1vc1!UV22qtlZzlJL7Q*ru zW+a<$gb&;(Ck4D6Y*l=$j>E(NIh4?;u)%F#gB)ozLTCdsd=t9uO9F!2(;O&cb$W*j z=R1dnxO_j4%di|_H|iniX({53Z_hu)gn^xSIva%!tN2X(^0oF1!}N5qAan;qYbu@h zQpcJ(QiIr1T&9hbr4lo2O_(8SOTo*IQ|~QbgmQOlFXHS z1nST~Il;_+Ekm+B>7h|Az92{Sl!c(Xvs;V@j>ln?L7yVZK(>j%Dfmee_geE$9;TG? zy1<@TVFR061n2&|LENs&=mQ)P4a{OlOF>0`o#`Di_+Xo+W`#-GcJEhbHH?-s%bC?n zBVT9ge^i6)9bUagAttc8QcKzM%6;EW%2?^{$8S;WG1JSYIw`l%<}-%lVG|bXuJpuL z4{2Twgd_@vzF|xefpB+4UJ}K2sCsrYf8uP-i*SY+@nTB$k5$vegK^d7V3aj6a3W!o3qeh$}YW%caZ1~2D&2dFFc(1(n*}A2- z+AA+!%L7?6ux{8o8dcH9#Wosuj2<+9O+=xa^C$KLr5*+c5ziZW&qo<%YG5jvA|
LQo`NDCGTVhU=ZGhB*2nKvX&I9Q%NpW8 zVaS%g*dmw9Nd1tekdPm+XA;Whbn4r*xy%*L$EAB(U*Zggi+Pg0yuBenf#V6$c)3@a zGZ`=Dxv3#2Ysg~jpHeU*ocXZ+(ETN&A#uQt-tak38@GcZ&jwD?#8+#rfiJ)pN_aW-F zuoB3Mn9SH%PAebykF=t9y1JErZ7Y47bo=zoR$wHtZr1eWCAv}tHu2!1jG|2glQ?#) zFvT>@)6w&sgZ zcFeZNiumax3S44)<|*;!m~jPuk=k@FL`vtS2Et~iXy%1+_1UK?tQPp6r%wd-wrOnp z!z!%?JeAnFuN?!)STx5it$z^8tg=a#y7Qxf&$#@SYh)0g7F4wak)9Zl$3PB488QxW z#xRa58hf~_2FrW{!)6-8gq7Gu^8`yJXAtR?ct}hG-a!)(u4o-TRg2L!l# z^+p1tKIk@>jbqzlB|c(Om5JprI&2yOY+j+1n6u8hGTq}oI9fOzbM3F z9Kym>6BSjWYk@)p(XyWO^nObp^A3C#Xag5ke2HF-igiQcd}X#bKPf`hPSkZJTAV0S>2=RD49>AjO~3ddKU$n8=xZ3Dclh8K0Y zjLxiN9nU)}21NV5Pz;n)rqUN-5PBtjdF!WnVA(+@=Byb1b3yD&7!7=Br!Oo=obOLG z3Ax*hVzEY`!{nI?oOgURW@X7oS?viC`4ec>&8{Hz1yK(2H1%nUy{DsoP$6N|<cN{lVkOkSUgagy2Z@IAK%;iJB%D4u{b61M?fhPzm~Yu=Kap zm@E?XGn5_#)%a3g5rd^y3UBEFTPl!fC5$#b%C&&?xq3?d8Jtu(;}i6jaq^ubZLOo8 z)5)>R+AN2D0URvqw|Wv)Sd*VUqZ!3)ZqNovD!B0w+OsMs)^4>%?6rPcv4DOOf-VqO zwEXyRS=SNQF7aI6iwsit2T+UUbH-wR(F%AWthCv8ImxdfN@;@^!O(-5!2*E(iNP2= z|Me_#5e|2$kjG~xx%Mf~dM6_a|6V3q>{K;nmRtLF#80O=3syZrzqV#LV% zi{Tozr?HQifgvG*<~33vRfJ&5-8G4+dK^u!>oHiBH1URw!py`gX-iYA(XtfIQ+4S) z2BULMnAEuodtnawDojfKoY02&1Zm0;848!~%WK0>GjDm6|WkxG)|zlR^zVD&!2NT^5wPYesXFdCTcINqnC->ZZ&}M8d%gEyt%<_doVY8 zPZr#sC=FexeRR)FG(ENAJU5R0+$|f#Rp#Mi`cXS9zE1tn{s$0f1P<*=E2BPgXg`Gxe4_CN86#4so#yC9IvVbv&Nx(Qlm9+?%+p zC^eLmQ?T0n4qj9>1hYwYcM~oz9O5r-nmJnvhiz$~FhMIML^yX@s!ujQU)%(98IL=E z3v=0RF^ns*a#Qys40%hc+E2Vb=3I{z_;a7oTl=GZ>xWI>ft&o`y*FRxr*b7$!o(WR zkEw9Mkn?EUp>T5qWzV>jpI2>%i9|{3WP#NpFS>_gPHx1$cTT|X5=NV8g$!Zfhh0uz zq{QYHZkZC9^eTSCRg*eFr!Q$Sd^dQLD5Y1R?vX9J+`f#Fk!-C3z)+GIzRi!^?q0TD zY%CAi2!s_X8RPsQcu;gzfKTpT3>YS7;=XkZB=l5H)m{wW5z@GJLt|vbiRtuyJI>e3 zWx%bIaKO3vTs(V5GBKZt)TA2*dOFqs(^PdvzMH*BucG#+{S&;53VF46B0hkWt(vR3 zilTKqEVfG=S(NEGLv1!Q z&VtF+!yJ0#ING=C9Q!#b=#6nq7DMN#rJ38k_%r3-d4|TV4pLJWI!V3sK4$SiUz3x1 zo^oBdimo|K{h*2!6YVm5HZdJ<7CN?iOAtZo#Aoek`NUDVk#oX7oUy1~L|(G{yh!^P zmc)5cC#>mb;IS)3gewsyXEFRwU`c#&3 zLg{t4l~sdHAd=oxFOuG@91Z)8(F)iJ_iU;f`szFez$V|3{#Ey#?=~6^5%FznsA+@) zO3*?4C_msoXd&TirY}z`aoqnQIaXeE($wS$u<1e zdZ)SM+$+Fb>X)2KH=*in68p*FA74|jZ*Pb3&w^5)>_Y59c?$W|o>Z34{E)m{sUL8b zGsGhp!3T6MG0oJ%xF7T;U;Tu;X3$+@EB%y_ueB77;?YdbUvnW)Ge8NC9D-V2PAuJX zv-l~$&CTR2K@wg3rCfr1(Uh)KNm4uZ&z5%RuD5tnoL;VVihMd2UIQOI1TMQLsM$6~ z*>S5JJnU2k>II@}PPZ*58UkML*3&rtdF8gxhr z7Fa~^%3{V#%lvBaCbx6{!&<2m&i=L3I}z2WTdrh`h6Yd(&ri04DblDj$Otc|oCCH+ zWg3J+ivcED4l|jST4obGlo~gGwoSu0hn}QQD!0dK z6A$+}DNab9d%f9Wvubu@^fD(}O6~ccUKiH2beS)&)21=y6t>w$bv67M?E^^JsKT82 zxwNmKiv?EpgbzW$PPfJF^9ZohEQ=|ar4GN06tD9)WRFAIR_hB=2qNNJ`2vONWijPXpDdfU=8&mjY0FU^v_io#> z7%W4ikrp+ksV3>F2IsVK7_VPq>z0>(d5xN0itLv=MlFZ;?&BLvppKAVz8+J1Iq^G| z?P1KO;#`yNW5#+eWh?VnpiqQi33RH`>iKL|LgaR0GEx{`wC%#E3RF&siRc$|X=bvu zEpDIIj`bGKMpw{GMzmNG zRHiO`9sx2<<>cd52n?2aShVTw5v@734pi=@v(+&6>FG<3AO?Yj+kXP2)5_GC8)!t?oSP06xucc= zua~d}e9Y?}{q^=-3U^TOd4Ni`MVEtL_E(}9CX1ETQnQtt%iz~Xdd;)v>rgvK3~LUP zz5s(=($7EO#?cY%i=j|K`{xThBum`24DD9t9ptz=?Xb9oUv1g;P&-^-^J12^CfFT) zYP7N}pHVV6sPbxACWrP>WIFaDUy0L+`sSX9w=*ttL0$U!DttF&y+833fqB1{PKg89 zh_OyJUXoaD(wr<+hqkC$Fiod{I}ZIGV+T(-B?u^7&}s1&4I?N`oJ<3_Za zl`j-d#UJZukKfj1+?m2ksF2U5lax7LSD}FMmVPSZqw)Rgs|aQ=aJ}u?$ab!(rYLFG zzHP3xP*26?*t;_xottfK9yen;AVM>zQczmgoaJ_}y+h7<>0 znkjD9mia4Viq&yhLiaiO1O3AdXINeW@(8+VGY$!+r7T|QGZ}9UF3agfo13pXXQiYJ zzynMZZLb$Jg>{FrNG|J_{8Ke!w8DAiMW087-~KdRE43+C_esU+tDgxoRU-JNYSy9=4k)&)to6O?eAKk2Lu&jXImUv0n8ddc-X zY&ws64D?wRHqor>D3Fa%&1Vopnx?5Te2awr!q2caO`2VEQXKvJf&2UOBOIaU$-Rmv zvTER}V1eDGkFd9MF@mJ5sA(IfSo^v!_Q>~TOnLcIJ31(1m99RO(?;5|NA-$g2gs&Q zU^cxJQz+;^He$iS^_8$vqJVk!S>8deyRs#UPDLyGOnKYUMI?Em`JHKTnsQU3DVAEX zn}k9fE7AhT(9!jGiT8H}h;PM6_}#DDFpcvIUsPVX!iX5UU-G$A=RgOhoo@&;^Um63OntX$OpQ^{*gHfpBAh8Fp|B^CV{59SaiAcO*vyK5i{-P54a&^v1Kr!l~l%T?X2)YrG13#Vm89<=ko z1Km2qZKSgj;(Jy&{HrA zh|S<-Q|cze7D#mpU00a{$HC^EgXRpUl;#>|eO)_+UegGzjI=(DNB*i|!|W6h3LZ7m z2i~vxQlIwi51cw3^oRm$tWM%v(@5srq4Of~5@j-|#C$r)+p89}H_>!2$@g6{1X9N+ zM62Kh1V$t$K(pQeejqsTHCL_k>fso145odwkUOz|+V?@(*it$L4t|y{eSTC(@{E3} zN_g@7?Qcnm-Ad2x+&&L<@lysDR-+0l`GlcK#j@$rQp^H6T#RP@pX<+R7}m>+0TR1M zH}mq!XJPe`hIhBM8NT92M3u3+N;Z40&(A@EJv1ag9gdBMSgIeyIN9$nomx`EXLNo~ zKvqpLZT+p4fBp)qs$1 z)QrN^mc{4F-6tf)nOEU_@U7!f?gq!e8d!ETA8*I5W&lezS@>-&lBWljEdw3V!_%Y2>NI1+Jwzxm%Ne$-G zv!_-}AVt0;Ppj=)I+MdeH$Cn&bxk%61qlUG})q z2z`{)n8hm!eSI4aeY&f(!0|VkDGot&BGT_9uv#*U0iHC_$#Z29CY~_1Gx%W=!olew8DO_XYSZ?@I#P_Q zAc|90f+%6d$SZ19U((EB2PLSP_QjcETuQUZ7M4_w2d-32m-wt2m-h^{!E=gZ?s5tG zl5+9t7-@#}T&Z!NZlGz|_YeDe)Dx1lU6rf^;ezy$N@sck*u31%NK=UK4DgC5hFb!GAIT<6ap@BH~ zGqaFF*N2t-?iW?HqzSu5zl)Tg^juP%oXwKCH)Caex+%{3Dv7CnGYYlv!J@`_SF2v@ z8u~pSm;AbWqV1F_FAIKK%Qw?nVIoksyglU(jz5wM?9qGb;>|@d`NHaS)bc3CqnTKP z(EI^Tb4w^!mY9xmK327_tQIgDW8^EACp-aC3|G7&XP)Q=xJ7!?`13mbl!PSXcW-K} zN6RbuMfxK}iNn1l{DtS(DyA9hl> zeGh`On6fvyyRlW6%{c+^fq9A(Je_MxA9WN3CGth!r2u-q?TUtVU_PLTt!r^oVX6kC zYy~SUMH^m^RVanCHgNpf=T4rO9F6Uu!crB^=9t_ye2!)++CUsW^G- z`O{KcM!dMh@#b`;^L^B3cJqpdhuF;`RVU?lk+zJ?C(Rh&Zalj@Kc{frQf!08Eaq3LgsEV8sMD#JU(8K4V&T!ELQ$8PE8_4fw zF!wbbP7fUpl4jwIg5QZ(M<+*CHvNHMUSo|CUX_H4cZ?-lgz!aZ)IT(uf)RDvce^;v zY)Il;f*IvLbj_({cg>h@NE@)V@2bX6a*`Kruv7^2qFgnruyW-_9H~(%cdfE<&vVy} zE|nv6O5rSku>qc%OB}qvC+g>&s8VtaCFoZY&Z@*!SaNRXxBoPp5agmEle6uN=rp(S(#XP$H*Kjjv(ylqm`oIm3~}{0a{FSC)y#rxnvjU8R9dQ z9ax62E}Y}n*_O&1+w&@Q&MKor`{?vDLF?3Afxup2felnSuU3p7%|I#yG_}FR^8CW?F0KRxQPZoHxVRwT2`5x1H*Ie zaMk1lp_k)$jrMkedM?csrrjDl*dm*y0lAFuJBj%a7kAfCqy?|#(Zx>b`4)8TZ65tx zRqm7@AJq^!EwKkxNDM=!<{{-FNv;;+J1znY%~1zR?k46UFtFbxJ0x zp2xg&H_-*!`O;?b;Satg-RU#_pwf*eqSBdhNWKdRU&AsQev(2nH+_K-H3^>l7`i(o zG`M{Cky_o2Bs7Y&4vz=%x1p%YA8+(Cs{x0coX`fKvhD5lXm1(anRvP2tC+BA^>UJK zuV6aJPe9I?TV$nz8W^>V?PH~rn#ivX82&p@S=P((?U&UjmMG( z2r(6gUqE4O_|w{qFINCLQ7?ju&Lh`9er+IXD0>1z$>_ZMJe`#slrqFyx&dm@%M>qx z5jZAWuw(X9>TpzeCDpF)C(e)NVTzzz6oK^LVke|_q>ic=A>#eeX6vzr|;nMsE%1b|jvv=rGGn4gh^2M>O#gM{F zM`8%rLa&!j7bK&I=|qU}2%IBn1ksp@$ocI`ZGL{F(~^l^ikG1mXqD2g(A!?f2REFz z6HX-E^i@V!CM>0UR;kH%73O-}LW?$MCDDAgT90Y8GFr_rMAfa+KUWj?iqp2;3$__5 z_$j6ZsvAHjqm$_cmc?1}Bw_h_&9XOip=-X)SQQn#ZeoBxe%FA>Hi)6wtKW!!bJA0S zCQyBh!DbfFOG)yyIcJ;CXlnh9s965oJkk~L-5wE=do&%r{X4#Li$ySU zCDu;NFijh}x$J5SD84UOP98Ft092J{-wkHuc6wLZA08!^o>D!d7UAfr1toA*wK{N1 ziqfyd@=n!*De6U5qWGQS) zGRJvWy5=Ui9z$BSD4suFX4&UPrmcolf(XHq3TovO#HuxIoTT$2q+Q}?s7V-St0)dN zRxfwbJ*E}-PpG}*bc$)Cn$ru6kut*wDM`FHnTcwE@qC~4`ASvGa5S@sC`h>GY&A_~JXffon) zV9zW^nsRZ^)5Cr!qxH)ND2)hG!XEL3o|903^MX>uF<_`eZE@ZD*Gw0L(y2Lx-LS%gy?${i!8G^|EX*=J2_Rgh) zTyJfbjoDVy31O`eCk63F`E??WUd>l{a|aD&V?fP7wsTcG=tO} z1L@wniSiXLf;sy}s+$`Wcjv^=bLV@7-8olQ^iOS${noDm*yUY2Pz+nc1 z_n%BgCG-qAYF33r>5qZH856wX10S|(@BmII0xRI4P+K_1QuNknpqrU*R1bP%g-SQV?Mw6X^_-(p4Lvt{)mYBi#Ape8jqp?;qHZku&FrT& z;?~u;73OyH-pu1bA(0j;2O+286(fPa+CEV7EIu>P%(mC1er2 zv$O_?x3}wK7p@O*oq_Koirn8QQpE0FoSAhC-X2@^f{vkEQ;dVTVBs;8a^LZ)1zq5= zn8K6F^B!FiQQ{%Yh^PlkZIKu*{P54i;SU#G49K3@ZCa)Dv&HCkT%MBTFAJK#;g9rZ z(SX18J)|;_mrzV6Z!CAaw$3@5)lF{Ps|XFgdS^G=z@6?8L_NU#5@84cQUQPM9ZDTM z8Vye*P%9tojJdga;cZG88*LxbtMm*cKAOp(>584btp7b60j1Z|-06$UPf$rw3l7%X zIRB~{B~wVditL0kNRWHZbEKeROILq4p&AQkQ^9F{KUhPKd{r#b^J*B83_oELcrUG~ zgo-fUW}F$F@4F!4>~$^WI%@5)q{dz1;^OB+hk^{y-Q+=ad$*?_hv9^|vRh+Xk0Q?}?LptdHnEUjZI%N* z%?XT-L61nFhnjvfqD;OTjj6S-W)zyjT(7exDBkZvqtd5`>qg{0zI@+2AWukSvkZw+ zG4;f9&Q&mxYwiQbTV5D7T}pVxXGgTPLk>x2-!tVD?b0D7ae`2HH1H@F5>_`R-50al z2y2FQ0s0BJJ@-(~s(U#%B?osvBfW60MbT z_=1HD*&>-wKH%h-jeFUTlu}ujD9+nL7sfK=wuN~Evl`GnuJ_RXJd>S^`na9T<|(e`xP;s{ zJTCii9829cEfP=YqUQnM9J9tZ{qs-;g$lJ`qLk4#hP2E=9dZNXW%FZ5$);Z%|KNDD z;=cGk^UZ=lV%WgcRoTVGc!lRR+7}~Ka%AYLaZ}=aU2V;@er+s&+7MRp_|8fLYQ!k# z6sA=2xEbYeKSQ|51ArE_ZG9&)>P;xwB$w`(b)Hz}^`e+yST0vOkr{>)eA1 zAg==4`IVsM)Zy5M`T%3M1 zJfA-32Y(3pF$zW{DS>6v@awOP-l@p}3b!x6zs2-)iDZc%K1U-T^TGAL)dx*KRR@(S+UlVlTTc%n|#!@a9WsdKXTo~r-`=5 zSLNcXM6fm_ni6k<#k}EF=C*8SYg)vzuC&pFu+exZD|^3`@DtT8!3Dy=Zh>@a)t%>F zy3e&j;i}u9kk-AG0&*uUx1e^OI?%0!$`Y>8hV-a;546Y=mf)=FF?YOV$C_n^^+9y# zA#@;H;MOe9_IhCj??UAKYBmpz7I8rULld?N>s%Mh?R?~C%@$x9d?&FxNx&!Fh?-MuQ;4WY4GS^`F;pHx`7v+{c z{Sj4^FCEZ7RgdlmKu51(9Xi#Z*D!FT1G{gYN)UpvrC zjQv@ary`CEJ569s#18o-wDx#Co>N}6+~Dr()Qu2KSM31(+jKDxgzsBhv*nDXY;6PPz z0UQ#iF3Pk%y!&xJi}e~IWh-Wczl(+#%#0(tCZY2HD`4@pCCe&w;sO?t^9N_v3H_J1 z;Vh$t&|gw1x^8u&WaWqJJSi#J4|JN-tS_wVo$5(1Y6E%5)UjKV7TpTtd0FoKkr5l&$m!u}PIHI>t$NgUCA=l#J`1TIRHaUDk9V z$#36?N4(7PR>LEv%9WqV$eQMmIpW*&huU-4)v{jn<2AP_y1p^J;&0O@hMC&N+(vOr z(6N;pN?;kX5SVi>U|oxfI&5K(QFwCl!&c6d0A<)4;9Z{!{Q$8!-!0Jd1k0DweRefeY0MuJ3E>oz~UB^>Buax-? zW!MLkC0KzIp}hsy4hLPjPuOiPfo}P=gxBuXySsZ_H%~G5IFZ7p7&9C%MK-LcPijFk zVOKjDM9A?x*!8B!E$TojIqyT(XQ6QDQ{B}c$!s?I+D6CHs49RRio)pTm(&zHA)z%b z5X*%2O$`<-rl0GC+c}7F6Z&jK=vs%nLgqvwIc8ztLR#VcK6}GI6FQk(6Jo(56=F*m zQe&`+n>;``!CKlYUooVI&2Gc9(7`J}qc=0REs6;V%bN+a5i*ul35XmmY}2v`G`OaY o_otCkoT7^$Yv(z;g@f=CVizVI;K%>c9Jc0HuJpr2qf` literal 0 HcmV?d00001 diff --git a/manual/src/main/asciidoc/images/unomi-favicon.png b/manual/src/main/asciidoc/images/unomi-favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..09052eee3aaa2919f12fa579835a3fc84e2dcf8d GIT binary patch literal 17610 zcmeI4c~lcw*1*FiO9LW{s0hZm>x68CkQf5UA}9fy-31{e708gyBuLmAT3Q9Pap|Cl zqPQTUq97mwLZh;{g3Ex4LaV5V+K4M4qEi6`EVaHq-^_QuGjl5EB$fN_yZ6@n-TQ7; z{-`7`@mQ#*Gf4-9Lg~4=I(dOdKk#j=H3s}Nt-ZJzJZOtumr7Bp9=|HzD)!OaPNPus zPw;#KWC2Wf8e1f=WN}0xfTdg@24|yCbUV42#SRB#=nx>3C$z=9DE}RU=5cH>{$wVe zDRu~_h209P`G8S4c;0vWRxh-bEFAe;z9L8bL15;$- zwipLxKy(0e3EEL40nlViYb+aYO+izrmIN||Vnz8InusTmad;ArgvSy|G%GyKiijS1 zVHi5#H@bwwrFl8I421(FTTGZtCZ^$Vk&%&>kt9ozBos%WQmHsR5l19q!5LU-lu*W! zV};UbgHAsCaRQ`l2~RBJiG*mSUsi}HLS~D>C<6_@hSntz4+j!ThuDD>adMUzN3g`> zzGcK=58H?%B>aI>^1j+!}KS2RZvUnBuf3t*7wxNi-Q zvKpv0nwwB6V+q-Sn-c>Zu;lSLG#h|w6$03BvDQ=qfVCoUC|DNPiiEYH5&;6qnoF|c zum>PAnWN3Wa_uByM=19T=z6fVa71j-{m5wn6ub>PgiXP+Su7%$D~b)4#Q{SCT#^li zL*W8c>TsY@&b|udDdB;gkHsG~N4XXb7>rG_B3h9sTxCUd}m z6c&KzeqPHr+R5Ck^B@6{P4BY3neIEEv z%H3b-g584jjv)Xnuk z7abf0Z{5LP4Y=W-4PQ0b;VaR<^UKJJ{+(VSzK|dgAn+oB3!V=|i{OF)ffo^6@O&Uz z1Q!Gdyolg}=L69qxFA5_MFbZ-ABYyg1pxvtBDmoBK(q)h2oQJ?!3EC;qD62)fWV6g zE_gl=ErJUI1YSgN!SjJ=5nK=;@FIc>o)1Ke;DP{w7ZF_Wd>~o`7X%2rh~R?f1JNS5 zAVA5ejcs>vDx%~Feb#$H9kVUAXfU-k#X=6>IkEw5I0_m{nB-SKi$ zWKehKof1L9Hpxn1>s_iev_7`|GWCy%rQ%2R`k@;}k_67M#Vd%4E2Gf5{whyhF zQ*L^@sKaYte(WK=_cO&YD_W1)ZkpvV{>t5zHJ)nYG>;Y4I^BO;KXKU!tZrq+TQ$^M z$2n5NNBhY(zgMnp*G{WC5o3dyJ)e8!-IHDWS2f+gTcjU%W^eU5@2(gAO%^+NRI$D~ zrP#3BVX49Ni&1*F=6uY}T9U6w+16-UB)zX&Uhiq3gk z{8)T#d)rHE6;IVY8kWiPd6Uu(}tgGtGSa-cA{eI63US<9X z!J__)jRJc2CQOrGMfBYKIctJ979{(J`%ckpnvp^OfzLc0WNN0JuYPKFeQJrSZBL%|?uA!nVp4AxjyF)mW$dK=zUlQ%?ZZz?6I{u+Y3<_FrlLK!C+xWVt5NUn z@Qmdi)3sJ!tvcTJ@_mBkz1HdgQ?($CN)z0hI>WLTswFj_F1(}EJi5lKN=Znrny}vM zH+QrS&p0U5kealTJbUqja_@w`XZCp)#sn|C=vg6e;$7ki0*m$;-i|O@OK>t?v;N$N zhFfB@^V)MVSMu{}#%1m3X9sq+G~bB&SkdPaW*%)5n!l`nKM}tb zb!5!b+FOg6neEmYzwLZnkgM1%F#Ji(6 zV;8oSZKj0J4E9YcKijp$wIO!TPSLaM!tleSJ#ilM;x5$&bicOkS=PqOy*9ZZ{qH9X zsER7Tr`neknU6xKaViUVOvI zumF( zEmO&JC0X5(nW%YbbO(T6VmMaZLvyZN@zm_r{G@l@R-w)LTOMEeNV)a8{)tymPR0?V zcUt=&bmHe$-1(*NnA7c)xTW;E)-6dUdqnpS0JqhC5_k6gdHK(4ibajizSb3icXUHy zkGAX?+j?~Q%VK-cib@lkkNuNY>bPiK=^39_SUTx%yPq6Mi;(ZrVmj4&-e|q+3;igV%Mi#%T{~w#TU!+fE zB-sIHB2{-9z1YxJCUN&}Em0&^l*M>Jk4{0Z0K4_#WWq~WLZnYCh}0tc1b`Mpt4UpH-W-bH~k?+O#?T zz58b6&Zu=sI#h%C*x!6)9qE~FJ+&w&+5J>#kim+VwN>mJhTT)OG%mbwHhHz6^6Ccx zPOq(WbD+p?f`zUb!%=N&V*gZ@@m}B6g@-S{U+eES&3O~|rpw~T>Cx{c#4bsu%J%Ao z7b>*hdo9+v@O;_t{&j|rSXo5ks|mgNb}3HV=7*n5KGb7O>X=YZRlG7FsFixjo?bnF z@BEY2^~r@l#Ru-`o5oyoN%i?+#jLHGjlTrO7`|1D?mUur;OP69&e{EG1;?(%(1;eN zc9?or-`{oG*f|=AiA?g+H&~W|?u$NH*?Y3*d^83>w#T%~<7i*l^8zeDpM zM3{QDj&$twh=v-?33gmd@}g6WgTFK`PVe34YIaDG@uJ04BPYh#;>QQ%#=}?}Gnai9VHW=qo&v-JauJ zcC)!lusLvxw!MAw0#3@pDPgBw-pvrjPs*IxeJAIl&LWF|hN*tGf_-l$hd=7PQ0{IT zGe?8{*7?VW$0fT1kgL&ZlV6H=PP>Tvc8RT0}SOj(f2dXjoq@+lrg_0Lwd4{28ZQwhq=*~97Bg5Wj(1cV#x A4gdfE literal 0 HcmV?d00001 diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc index 91d1723e8..5cda16588 100644 --- a/manual/src/main/asciidoc/index.adoc +++ b/manual/src/main/asciidoc/index.adoc @@ -21,8 +21,58 @@ Apache Software Foundation :toc-title: Table of Contents :numbered: :homepage: https://unomi.apache.org - -image::asf_logo_url.png[pdfwidth=35%,align=center] +:docinfo: shared-head,shared-footer +:source-highlighter: highlightjs + +ifndef::backend-pdf[] +[%passthrough,subs=attributes] +++++ +
+ +
+++++ + +[.hero.cover-page] +-- +[.section-label] +Apache Unomi™ + +++++ +

Apache Unomi 3.x
Documentation

+++++ + +[.cover-meta.lead] +Version {revnumber} · link:https://unomi.apache.org[unomi.apache.org] · link:https://github.com/apache/unomi/tree/master/manual/src/main/asciidoc[Edit on GitHub] + +[.cover-asf] +image::asf_logo_url.png[Apache Software Foundation,width=140,align=center] +-- +endif::[] + +ifdef::backend-pdf[] +image::asf_logo_url.png[Apache Software Foundation,pdfwidth=25%,align=center,role=pdf-asf-mark] +endif::[] include::foreword.adoc[] diff --git a/manual/src/main/asciidoc/unomi-manual.css b/manual/src/main/asciidoc/unomi-manual.css new file mode 100644 index 000000000..21be86fbb --- /dev/null +++ b/manual/src/main/asciidoc/unomi-manual.css @@ -0,0 +1,3241 @@ + +@import "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"; + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block +} + +audio, +canvas, +video { + display: inline-block +} + +audio:not([controls]) { + display: none; + height: 0 +} + +script { + display: none !important +} + +html { + font-family: "Droid Serif"; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} + +a { + background: transparent +} + +a:focus { + outline: thin dotted +} + +a:active, +a:hover { + outline: 0 +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, +strong { + font-weight: bold +} + +dfn { + font-style: italic +} + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0 +} + +mark { + background: #ff0; + color: #000 +} + +code, +kbd, +pre, +samp { + font-family: monospace; + font-size: 1em +} + +pre { + white-space: pre-wrap +} + +q { + quotes: "\201C" "\201D" "\2018" "\2019" +} + +small { + font-size: 80% +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sup { + top: -.5em +} + +sub { + bottom: -.25em +} + +img { + border: 0 +} + +svg:not(:root) { + overflow: hidden +} + +figure { + margin: 0 +} + +fieldset { + border: 1px solid silver; + margin: 0 2px; + padding: .35em .625em .75em +} + +legend { + border: 0; + padding: 0 +} + +button, +input, +select, +textarea { + font-family: inherit; + font-size: 100%; + margin: 0 +} + +button, +input { + line-height: normal +} + +button, +select { + text-transform: none +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], +html input[disabled] { + cursor: default +} + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + padding: 0 +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0 +} + +textarea { + overflow: auto; + vertical-align: top +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +*, +*::before, +*::after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box +} + +html, +body { + /*font-size: 100%*/ +} + +body { + background: #fff; + color: #333; + padding: 0; + margin: 0; + font-family: "Droid Serif", "DejaVu Serif", serif; + font-size: 14px; + font-style: normal; + line-height: 1.42857143; + position: relative; + cursor: auto; + tab-size: 4; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased +} + +a:hover { + cursor: pointer +} + +img, +object, +embed { + max-width: 100%; + height: auto +} + +object, +embed { + height: 100% +} + +img { + -ms-interpolation-mode: bicubic +} + +.left { + float: left !important +} + +.right { + float: right !important +} + +.text-left { + text-align: left !important +} + +.text-right { + text-align: right !important +} + +.text-center { + text-align: center !important +} + +.text-justify { + text-align: justify !important +} + +.hide { + display: none +} + +img, +object, +svg { + display: inline-block; + vertical-align: middle +} + +textarea { + height: auto; + min-height: 50px +} + +select { + width: 100% +} + +.center { + margin-left: auto; + margin-right: auto +} + +.stretch { + width: 100% +} + +.subheader, +.admonitionblock td.content>.title, +.audioblock>.title, +.exampleblock>.title, +.imageblock>.title, +.listingblock>.title, +.literalblock>.title, +.stemblock>.title, +.openblock>.title, +.paragraph>.title, +.quoteblock>.title, +table.tableblock>.title, +.verseblock>.title, +.videoblock>.title, +.dlist>.title, +.olist>.title, +.ulist>.title, +.qlist>.title, +.hdlist>.title { + line-height: 1.45; + color: #6754d1; + font-weight: 400; + margin-top: 0; + margin-bottom: .25em +} + +div, +dl, +dt, +dd, +ul, +ol, +li, +h1, +h2, +h3, +#toctitle, +.sidebarblock>.content>.title, +h4, +h5, +h6, +pre, +form, +p, +blockquote, +th, +td { + margin: 0; + padding: 0; + direction: ltr +} + +a { + color: #6754d1; + text-decoration: underline; + line-height: inherit +} + +a:hover, +a:focus { + color: #5243b8; +} + +a img { + border: none +} + +p { + font-family: inherit; + font-weight: 400; + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + text-rendering: optimizeLegibility +} + +p aside { + font-size: .875em; + line-height: 1.35; + font-style: italic +} + +h1, +h2, +h3, +#toctitle, +.sidebarblock>.content>.title, +h4, +h5, +h6 { + font-family: "Open Sans", "DejaVu Sans", sans-serif; + font-weight: bold; + /*font-style: bold;*/ + color: #3d3489; + text-rendering: optimizeLegibility; + margin-top: 1em; + margin-bottom: .5em; + line-height: 1.0125em; + text-transform: none; +} + +h1 small, +h2 small, +h3 small, +#toctitle small, +.sidebarblock>.content>.title small, +h4 small, +h5 small, +h6 small { + font-size: 60%; + color: #3d3489; + line-height: 0 +} + +h1 { + font-size: 2.125em +} + +h2 { + font-size: 1.6875em +} + +h3, +#toctitle, +.sidebarblock>.content>.title { + font-size: 1.375em +} + +h4, +h5 { + font-size: 1.125em +} + +h6 { + font-size: 1em +} + +hr { + border: solid #dddddd; + border-width: 1px 0 0; + clear: both; + margin: 1.25em 0 1.1875em; + height: 0 +} + +em, +i { + font-style: italic; + line-height: inherit +} + +strong, +b { + font-weight: bold; + line-height: inherit +} + +small { + font-size: 60%; + line-height: inherit +} + +code { + font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + font-weight: 400; + color: #6754d1; +} + +ul, +ol, +dl { + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + list-style-position: outside; + font-family: inherit +} + +ul, +ol { + margin-left: 1.5em +} + +ul li ul, +ul li ol { + margin-left: 1.25em; + margin-bottom: 0; + font-size: 1em +} + +ul.square li ul, +ul.circle li ul, +ul.disc li ul { + list-style: inherit +} + +ul.square { + list-style-type: square +} + +ul.circle { + list-style-type: circle +} + +ul.disc { + list-style-type: disc +} + +ol li ul, +ol li ol { + margin-left: 1.25em; + margin-bottom: 0 +} + +dl dt { + margin-bottom: .3125em; + font-weight: bold +} + +dl dd { + margin-bottom: 1.25em +} + +abbr, +acronym { + text-transform: uppercase; + font-size: 90%; + color: rgba(0, 0, 0, .8); + border-bottom: 1px dotted #ddd; + cursor: help +} + +abbr { + text-transform: none +} + +blockquote { + margin: 0 0 1.25em; + padding: .5625em 1.25em 0 1.1875em; + border-left: 1px solid #ddd +} + +blockquote cite { + display: block; + font-size: .9375em; + color: rgba(0, 0, 0, .6) +} + +blockquote cite::before { + content: "\2014 \0020" +} + +blockquote cite a, +blockquote cite a:visited { + color: rgba(0, 0, 0, .6) +} + +blockquote, +blockquote p { + line-height: 1.6; + color: rgba(0, 0, 0, .85) +} + +@media screen and (min-width:768px) { + h1, + h2, + h3, + #toctitle, + .sidebarblock>.content>.title, + h4, + h5, + h6 { + line-height: 1.2 + } + + h1 { + font-size: 2.75em + } + + h2 { + font-size: 2.3125em + } + + h3, + #toctitle, + .sidebarblock>.content>.title { + font-size: 1.6875em + } + + h4 { + font-size: 1.4375em + } + +} + +table { + background: #fff; + margin-bottom: 1.25em; + border: solid 1px #dddddd; +} + +table thead, +table tfoot { + background: #f7f8f7 +} + +table thead tr th, +table thead tr td, +table tfoot tr th, +table tfoot tr td { + padding: .5em .625em .625em; + font-size: inherit; + color: rgba(0, 0, 0, .8); + text-align: left +} + +table tr th, +table tr td { + padding: .5625em .625em; + font-size: inherit; + color: rgba(0, 0, 0, .8) +} + +table tr.even, +table tr.alt, +table tr:nth-of-type(even) { + background: #f8f8f7 +} + +table thead tr th, +table tfoot tr th, +table tbody tr td, +table tr td, +table tfoot tr td { + display: table-cell; + line-height: 1.6 +} + +h1, +h2, +h3, +#toctitle, +.sidebarblock>.content>.title, +h4, +h5, +h6 { + line-height: 1.2; + word-spacing: -.05em +} + +h1 strong, +h2 strong, +h3 strong, +#toctitle strong, +.sidebarblock>.content>.title strong, +h4 strong, +h5 strong, +h6 strong { + font-weight: 400 +} + +.clearfix::before, +.clearfix::after, +.float-group::before, +.float-group::after { + content: " "; + display: table +} + +.clearfix::after, +.float-group::after { + clear: both +} + +*:not(pre)>code { + font-size: .9375em; + font-style: normal !important; + letter-spacing: 0; + padding: .1em .5ex; + word-spacing: -.15em; + background-color: #f7f7f8; + -webkit-border-radius: 4px; + border-radius: 4px; + line-height: 1.45; + text-rendering: optimizeSpeed; + word-wrap: break-word +} + +*:not(pre)>code.nobreak { + word-wrap: normal +} + +*:not(pre)>code.nowrap { + white-space: nowrap +} + +pre, +pre>code { + line-height: 1.45; + color: #6754d1; + font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + font-weight: 400; + text-rendering: optimizeSpeed +} + +em em { + font-style: normal +} + +strong strong { + font-weight: 400 +} + +.keyseq { + color: rgba(51, 51, 51, .8) +} + +kbd { + font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + display: inline-block; + color: rgba(0, 0, 0, .8); + font-size: .65em; + line-height: 1.45; + background-color: #f7f7f7; + border: 1px solid #dddddd; + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em white inset; + box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em #fff inset; + margin: 0 .15em; + padding: .2em .5em; + vertical-align: middle; + position: relative; + top: -.1em; + white-space: nowrap +} + +.keyseq kbd:first-child { + margin-left: 0 +} + +.keyseq kbd:last-child { + margin-right: 0 +} + +.menuseq, +.menuref { + color: #000 +} + +.menuseq b:not(.caret),.menuref { + font-weight: inherit +} + +.menuseq { + word-spacing: -.02em +} + +.menuseq b.caret { + font-size: 1.25em; + line-height: .8 +} + +.menuseq i.caret { + font-weight: bold; + text-align: center; + width: .45em +} + +b.button::before, +b.button::after { + position: relative; + top: -1px; + font-weight: 400 +} + +b.button::before { + content: "["; + padding: 0 3px 0 2px +} + +b.button::after { + content: "]"; + padding: 0 2px 0 3px +} + +p a>code:hover { + color: #6754d1; +} + +#header, +#content, +#footnotes, +#footer { + width: 100%; + margin-left: auto; + margin-right: auto; + margin-top: 0; + margin-bottom: 0; + max-width: 62.5em; + *zoom: 1; + position: relative; + padding-left: .9375em; + padding-right: .9375em +} + +#header::before, +#header::after, +#content::before, +#content::after, +#footnotes::before, +#footnotes::after, +#footer::before, +#footer::after { + content: " "; + display: table +} + +#header::after, +#content::after, +#footnotes::after, +#footer::after { + clear: both +} + +#content { + margin-top: 1.25em +} + +#content::before { + content: none +} + +#header>h1:first-child { + color: #3d3489; + margin-top: 2.25rem; + margin-bottom: 0 +} + +#header>h1:first-child+#toc { + margin-top: 8px; + border-top: 1px solid #dddddd +} + +#header>h1:only-child, +body.toc2 #header>h1:nth-last-child(2) { + border-bottom: 1px solid #dddddd; + padding-bottom: 8px +} + +#header .details { + border-bottom: 1px solid #dddddd; + line-height: 1.45; + padding-top: .25em; + padding-bottom: .25em; + padding-left: .25em; + color: rgba(0, 0, 0, .6); + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -ms-flex-flow: row wrap; + -webkit-flex-flow: row wrap; + flex-flow: row wrap +} + +#header .details span:first-child { + margin-left: -.125em +} + +#header .details span.email a { + color: rgba(0, 0, 0, .85) +} + +#header .details br { + display: none +} + +#header .details br+span::before { + content: "\00a0\2013\00a0" +} + +#header .details br+span.author::before { + content: "\00a0\22c5\00a0"; + color: rgba(0, 0, 0, .85) +} + +#header .details br+span#revremark::before { + content: "\00a0|\00a0" +} + +#header #revnumber { + text-transform: capitalize +} + +#header #revnumber::after { + content: "\00a0" +} + +#content>h1:first-child:not([class]) { + color: rgba(0, 0, 0, .85); + border-bottom: 1px solid #dddddd +; + padding-bottom: 8px; + margin-top: 0; + padding-top: 1rem; + margin-bottom: 1.25rem +} + +#toc { + border-bottom: 1px solid #dddddd; + padding-bottom: .5em +} + +#toc>ul { + margin-left: .125em +} + +#toc ul.sectlevel0>li>a { + font-style: italic +} + +#toc ul.sectlevel0 ul.sectlevel1 { + margin: .5em 0 +} + +#toc ul { + font-family: "Droid Serif", "DejaVu Sans", sans-serif; + list-style-type: none +} + +#toc li { + line-height: 1.3334; + margin-top: .3334em +} + +#toc a { + text-decoration: none +} + +#toc a:active { + text-decoration: underline +} + +#toctitle { + color: #3d3489; + font-size: 1.2em +} + +@media screen and (min-width:768px) { + #toctitle { + font-size: 1.375em + } + + body.toc2 { + padding-left: 15em; + padding-right: 0 + } + + #toc.toc2 { + margin-top: 0 !important; + background-color: #eee; + position: fixed; + width: 15em; + left: 0; + top: 0; + border-right: 1px solid #dddddd; + border-top-width: 0 !important; + border-bottom-width: 0 !important; + z-index: 1000; + padding: 1.25em 1em; + height: 100%; + overflow: auto + } + + #toc.toc2 #toctitle { + margin-top: 0; + margin-bottom: .8rem; + font-size: 1.2em + } + + #toc.toc2>ul { + font-size: .9em; + margin-bottom: 0 + } + + #toc.toc2 ul ul { + margin-left: 0; + padding-left: 1em + } + + #toc.toc2 ul.sectlevel0 ul.sectlevel1 { + padding-left: 0; + margin-top: .5em; + margin-bottom: .5em + } + + body.toc2.toc-right { + padding-left: 0; + padding-right: 15em + } + + body.toc2.toc-right #toc.toc2 { + border-right-width: 0; + border-left: 1px solid #dddddd; + left: auto; + right: 0 + } + +} + +@media screen and (min-width:1280px) { + body.toc2 { + padding-left: 20em; + padding-right: 0 + } + + #toc.toc2 { + width: 20em + } + + #toc.toc2 #toctitle { + font-size: 1.375em + } + + #toc.toc2>ul { + font-size: .95em + } + + #toc.toc2 ul ul { + padding-left: 1.25em + } + + body.toc2.toc-right { + padding-left: 0; + padding-right: 20em + } + +} + +#content #toc { + border-style: solid; + border-width: 1px; + border-color: #dddddd; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f8f8f7; + -webkit-border-radius: 4px; + border-radius: 4px +} + +#content #toc>:first-child { + margin-top: 0 +} + +#content #toc>:last-child { + margin-bottom: 0 +} + +#footer { + max-width: 100%; + background-color: #3d3489; + padding: 1.25em +} + +#footer-text { + color: #fff; + line-height: 1.44 +} + +#content { + margin-bottom: .625em +} + +.sect1 { + padding-bottom: .625em +} + +@media screen and (min-width:768px) { + #content { + margin-bottom: 1.25em + } + + .sect1 { + padding-bottom: 1.25em + } + +} + +.sect1:last-child { + padding-bottom: 0 +} + +.sect1+.sect1 { + border-top: 1px solid #dddddd; +} + +#content h1>a.anchor, +h2>a.anchor, +h3>a.anchor, +#toctitle>a.anchor, +.sidebarblock>.content>.title>a.anchor, +h4>a.anchor, +h5>a.anchor, +h6>a.anchor { + position: absolute; + z-index: 1001; + width: 1.5ex; + margin-left: -1.5ex; + display: block; + text-decoration: none !important; + visibility: hidden; + text-align: center; + font-weight: 400 +} + +#content h1>a.anchor::before, +h2>a.anchor::before, +h3>a.anchor::before, +#toctitle>a.anchor::before, +.sidebarblock>.content>.title>a.anchor::before, +h4>a.anchor::before, +h5>a.anchor::before, +h6>a.anchor::before { + content: "\00A7"; + font-size: .85em; + display: block; + padding-top: .1em +} + +#content h1:hover>a.anchor, +#content h1>a.anchor:hover, +h2:hover>a.anchor, +h2>a.anchor:hover, +h3:hover>a.anchor, +#toctitle:hover>a.anchor, +.sidebarblock>.content>.title:hover>a.anchor, +h3>a.anchor:hover, +#toctitle>a.anchor:hover, +.sidebarblock>.content>.title>a.anchor:hover, +h4:hover>a.anchor, +h4>a.anchor:hover, +h5:hover>a.anchor, +h5>a.anchor:hover, +h6:hover>a.anchor, +h6>a.anchor:hover { + visibility: visible +} + +#content h1>a.link, +h2>a.link, +h3>a.link, +#toctitle>a.link, +.sidebarblock>.content>.title>a.link, +h4>a.link, +h5>a.link, +h6>a.link { + color: #ba3925; + text-decoration: none +} + +#content h1>a.link:hover, +h2>a.link:hover, +h3>a.link:hover, +#toctitle>a.link:hover, +.sidebarblock>.content>.title>a.link:hover, +h4>a.link:hover, +h5>a.link:hover, +h6>a.link:hover { + color: #a53221 +} + +.audioblock, +.imageblock, +.literalblock, +.listingblock, +.stemblock, +.videoblock { + margin-bottom: 1.25em +} + +.admonitionblock td.content>.title, +.audioblock>.title, +.exampleblock>.title, +.imageblock>.title, +.listingblock>.title, +.literalblock>.title, +.stemblock>.title, +.openblock>.title, +.paragraph>.title, +.quoteblock>.title, +table.tableblock>.title, +.verseblock>.title, +.videoblock>.title, +.dlist>.title, +.olist>.title, +.ulist>.title, +.qlist>.title, +.hdlist>.title { + text-rendering: optimizeLegibility; + text-align: left; + font-family: "Droid Serif", "DejaVu Serif", serif; + font-size: 1rem; + font-style: italic +} + +table.tableblock.fit-content>caption.title { + white-space: nowrap; + width: 0 +} + +.paragraph.lead>p, +#preamble>.sectionbody>[class="paragraph"]:first-of-type p { + font-size: 1.21875em; + line-height: 1.6; + color: rgba(0, 0, 0, .85) +} + +table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p { + font-size: inherit +} + +.admonitionblock>table { + border-collapse: separate; + border: 0; + background: none; + width: 100% +} + +.admonitionblock>table td.icon { + text-align: center; + width: 80px +} + +.admonitionblock>table td.icon img { + max-width: none +} + +.admonitionblock>table td.icon .title { + font-weight: bold; + font-family: "Open Sans", "DejaVu Sans", sans-serif; + text-transform: uppercase +} + +.admonitionblock>table td.content { + padding-left: 1.125em; + padding-right: 1.25em; + border-left: 1px solid #dddddd; + color: rgba(0, 0, 0, .6) +} + +.admonitionblock>table td.content>:last-child>:last-child { + margin-bottom: 0 +} + +.exampleblock>.content { + border-style: solid; + border-width: 1px; + border-color: #dddddd; + margin-bottom: 1.25em; + padding: 1.25em; + background: #fff; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.exampleblock>.content>:first-child { + margin-top: 0 +} + +.exampleblock>.content>:last-child { + margin-bottom: 0 +} + +.sidebarblock { + border-style: solid; + border-width: 1px; + border-color: #dddddd; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f8f8f7; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.sidebarblock>:first-child { + margin-top: 0 +} + +.sidebarblock>:last-child { + margin-bottom: 0 +} + +.sidebarblock>.content>.title { + color: #3d3489; + margin-top: 0; + text-align: center +} + +.exampleblock>.content>:last-child>:last-child, +.exampleblock>.content .olist>ol>li:last-child>:last-child, +.exampleblock>.content .ulist>ul>li:last-child>:last-child, +.exampleblock>.content .qlist>ol>li:last-child>:last-child, +.sidebarblock>.content>:last-child>:last-child, +.sidebarblock>.content .olist>ol>li:last-child>:last-child, +.sidebarblock>.content .ulist>ul>li:last-child>:last-child, +.sidebarblock>.content .qlist>ol>li:last-child>:last-child { + margin-bottom: 0 +} + +.literalblock pre, +.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint { + background: #f7f7f8 +} + +.sidebarblock .literalblock pre, +.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint { + background: #f2f1f1 +} + +.literalblock pre, +.literalblock pre[class], +.listingblock pre, +.listingblock pre[class] { + -webkit-border-radius: 4px; + border-radius: 4px; + word-wrap: break-word; + padding: 1em; + font-size: .8125em +} + +.literalblock pre.nowrap, +.literalblock pre[class].nowrap, +.listingblock pre.nowrap, +.listingblock pre[class].nowrap { + overflow-x: auto; + white-space: pre; + word-wrap: normal +} + +@media screen and (min-width:768px) { + .literalblock pre, + .literalblock pre[class], + .listingblock pre, + .listingblock pre[class] { + font-size: .90625em + } + +} + +@media screen and (min-width:1280px) { + .literalblock pre, + .literalblock pre[class], + .listingblock pre, + .listingblock pre[class] { + font-size: 1em + } + +} + +.literalblock.output pre { + color: #f7f7f8; + background-color: rgba(0, 0, 0, .9) +} + +.listingblock pre.highlightjs { + padding: 0 +} + +.listingblock pre.highlightjs>code { + padding: 1em; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.listingblock pre.prettyprint { + border-width: 0 +} + +.listingblock>.content { + position: relative +} + +.listingblock code[data-lang]::before { + display: none; + content: attr(data-lang); + position: absolute; + font-size: .75em; + top: .425rem; + right: .5rem; + line-height: 1; + text-transform: uppercase; + color: #999 +} + +.listingblock:hover code[data-lang]::before { + display: block +} + +.listingblock.terminal pre .command::before { + content: attr(data-prompt); + padding-right: .5em; + color: #999 +} + +.listingblock.terminal pre .command:not([data-prompt])::before { + content: "$" +} + +table.pyhltable { + border-collapse: separate; + border: 0; + margin-bottom: 0; + background: none +} + +table.pyhltable td { + vertical-align: top; + padding-top: 0; + padding-bottom: 0; + line-height: 1.45 +} + +table.pyhltable td.code { + padding-left: .75em; + padding-right: 0 +} + +pre.pygments .lineno, +table.pyhltable td:not(.code) { + color: #999; + padding-left: 0; + padding-right: .5em; + border-right: 1px solid #dddddd; +} + +pre.pygments .lineno { + display: inline-block; + margin-right: .25em +} + +table.pyhltable .linenodiv { + background: none !important; + padding-right: 0 !important +} + +.quoteblock { + margin: 0 1em 1.25em 1.5em; + display: table +} + +.quoteblock>.title { + margin-left: -1.5em; + margin-bottom: .75em +} + +.quoteblock blockquote, +.quoteblock blockquote p { + color: rgba(0, 0, 0, .85); + font-size: 1.05rem; + line-height: 1.75; + word-spacing: .1em; + letter-spacing: 0; + font-style: italic; + text-align: justify +} + +.quoteblock blockquote { + margin: 0; + padding: 0; + border: 0 +} + +.quoteblock blockquote::before { + content: "\201c"; + float: left; + font-size: 2.75em; + font-weight: bold; + line-height: .6em; + margin-left: -.6em; + color: #3d3489; + text-shadow: 0 1px 2px rgba(0, 0, 0, .1) +} + +.quoteblock blockquote>.paragraph:last-child p { + margin-bottom: 0 +} + +.quoteblock .attribution { + margin-top: .5em; + margin-right: .5ex; + text-align: right +} + +.quoteblock .quoteblock { + margin-left: 0; + margin-right: 0; + padding: .5em 0; + border-left: 3px solid rgba(0, 0, 0, .6) +} + +.quoteblock .quoteblock blockquote { + padding: 0 0 0 .75em +} + +.quoteblock .quoteblock blockquote::before { + display: none +} + +.verseblock { + margin: 0 1em 1.25em +} + +.verseblock pre { + font-family: "Open Sans", "DejaVu Sans", sans; + font-size: 1.15rem; + color: rgba(0, 0, 0, .85); + font-weight: 300; + text-rendering: optimizeLegibility +} + +.verseblock pre strong { + font-weight: 400 +} + +.verseblock .attribution { + margin-top: 1.25rem; + margin-left: .5ex +} + +.quoteblock .attribution, +.verseblock .attribution { + font-size: .9375em; + line-height: 1.45; + font-style: italic +} + +.quoteblock .attribution br, +.verseblock .attribution br { + display: none +} + +.quoteblock .attribution cite, +.verseblock .attribution cite { + display: block; + letter-spacing: -.025em; + color: rgba(0, 0, 0, .6) +} + +.quoteblock.abstract { + margin: 0 1em 1.25em; + display: block +} + +.quoteblock.abstract>.title { + margin: 0 0 .375em; + font-size: 1.15em; + text-align: center +} + +.quoteblock.abstract blockquote, +.quoteblock.abstract blockquote p { + word-spacing: 0; + line-height: 1.6 +} + +.quoteblock.abstract blockquote::before, +.quoteblock.abstract p::before { + display: none +} + +table.tableblock { + max-width: 100%; + border-collapse: separate +} + +p.tableblock:last-child { + margin-bottom: 0 +} + +td.tableblock>.content { + margin-bottom: -1.25em +} + +table.tableblock, +th.tableblock, +td.tableblock { + border: 0 solid #dddddd; +} + +table.grid-all>thead>tr>.tableblock, +table.grid-all>tbody>tr>.tableblock { + border-width: 0 1px 1px 0 +} + +table.grid-all>tfoot>tr>.tableblock { + border-width: 1px 1px 0 0 +} + +table.grid-cols>*>tr>.tableblock { + border-width: 0 1px 0 0 +} + +table.grid-rows>thead>tr>.tableblock, +table.grid-rows>tbody>tr>.tableblock { + border-width: 0 0 1px +} + +table.grid-rows>tfoot>tr>.tableblock { + border-width: 1px 0 0 +} + +table.grid-all>*>tr>.tableblock:last-child, +table.grid-cols>*>tr>.tableblock:last-child { + border-right-width: 0 +} + +table.grid-all>tbody>tr:last-child>.tableblock, +table.grid-all>thead:last-child>tr>.tableblock, +table.grid-rows>tbody>tr:last-child>.tableblock, +table.grid-rows>thead:last-child>tr>.tableblock { + border-bottom-width: 0 +} + +table.frame-all { + border-width: 1px +} + +table.frame-sides { + border-width: 0 1px +} + +table.frame-topbot, +table.frame-ends { + border-width: 1px 0 +} + +table.stripes-all tr, +table.stripes-odd tr:nth-of-type(odd) { + background: #f8f8f7 +} + +table.stripes-none tr, +table.stripes-odd tr:nth-of-type(even) { + background: none +} + +th.halign-left, +td.halign-left { + text-align: left +} + +th.halign-right, +td.halign-right { + text-align: right +} + +th.halign-center, +td.halign-center { + text-align: center +} + +th.valign-top, +td.valign-top { + vertical-align: top +} + +th.valign-bottom, +td.valign-bottom { + vertical-align: bottom +} + +th.valign-middle, +td.valign-middle { + vertical-align: middle +} + +table thead th, +table tfoot th { + font-weight: bold +} + +tbody tr th { + display: table-cell; + line-height: 1.6; + background: #f7f8f7 +} + +tbody tr th, +tbody tr th p, +tfoot tr th, +tfoot tr th p { + color: rgba(0, 0, 0, .8); + font-weight: bold +} + +p.tableblock>code:only-child { + background: none; + padding: 0 +} + +p.tableblock { + font-size: 1em +} + +td>div.verse { + white-space: pre +} + +ol { + margin-left: 1.75em +} + +ul li ol { + margin-left: 1.5em +} + +dl dd { + margin-left: 1.125em +} + +dl dd:last-child, +dl dd:last-child>:last-child { + margin-bottom: 0 +} + +ol>li p, +ul>li p, +ul dd, +ol dd, +.olist .olist, +.ulist .ulist, +.ulist .olist, +.olist .ulist { + margin-bottom: .625em +} + +ul.checklist, +ul.none, +ol.none, +ul.no-bullet, +ol.no-bullet, +ol.unnumbered, +ul.unstyled, +ol.unstyled { + list-style-type: none +} + +ul.no-bullet, +ol.no-bullet, +ol.unnumbered { + margin-left: .625em +} + +ul.unstyled, +ol.unstyled { + margin-left: 0 +} + +ul.checklist { + margin-left: .625em +} + +ul.checklist li>p:first-child>.fa-square-o:first-child, +ul.checklist li>p:first-child>.fa-check-square-o:first-child { + width: 1.25em; + font-size: .8em; + position: relative; + bottom: .125em +} + +ul.checklist li>p:first-child>input[type="checkbox"]:first-child { + margin-right: .25em +} + +ul.inline { + display: -ms-flexbox; + display: -webkit-box; + display: flex; + -ms-flex-flow: row wrap; + -webkit-flex-flow: row wrap; + flex-flow: row wrap; + list-style: none; + margin: 0 0 .625em -1.25em +} + +ul.inline>li { + margin-left: 1.25em +} + +.unstyled dl dt { + font-weight: 400; + font-style: normal +} + +ol.arabic { + list-style-type: decimal +} + +ol.decimal { + list-style-type: decimal-leading-zero +} + +ol.loweralpha { + list-style-type: lower-alpha +} + +ol.upperalpha { + list-style-type: upper-alpha +} + +ol.lowerroman { + list-style-type: lower-roman +} + +ol.upperroman { + list-style-type: upper-roman +} + +ol.lowergreek { + list-style-type: lower-greek +} + +.hdlist>table, +.colist>table { + border: 0; + background: none +} + +.hdlist>table>tbody>tr, +.colist>table>tbody>tr { + background: none +} + +td.hdlist1, +td.hdlist2 { + vertical-align: top; + padding: 0 .625em +} + +td.hdlist1 { + font-weight: bold; + padding-bottom: 1.25em +} + +.literalblock+.colist, +.listingblock+.colist { + margin-top: -.5em +} + +.colist td:not([class]):first-child { + padding: .4em .75em 0; + line-height: 1; + vertical-align: top +} + +.colist td:not([class]):first-child img { + max-width: none +} + +.colist td:not([class]):last-child { + padding: .25em 0 +} + +.thumb, +.th { + line-height: 0; + display: inline-block; + border: solid 4px #fff; + -webkit-box-shadow: 0 0 0 1px #ddd; + box-shadow: 0 0 0 1px #ddd +} + +.imageblock.left, +.imageblock[style*="float:left"] { + margin: .25em .625em 1.25em 0 +} + +.imageblock.right, +.imageblock[style*="float:right"] { + margin: .25em 0 1.25em .625em +} + +.imageblock>.title { + margin-bottom: 0 +} + +.imageblock.thumb, +.imageblock.th { + border-width: 6px +} + +.imageblock.thumb>.title, +.imageblock.th>.title { + padding: 0 .125em +} + +.image.left, +.image.right { + margin-top: .25em; + margin-bottom: .25em; + display: inline-block; + line-height: 0 +} + +.image.left { + margin-right: .625em +} + +.image.right { + margin-left: .625em +} + +a.image { + text-decoration: none; + display: inline-block +} + +a.image object { + pointer-events: none +} + +sup.footnote, +sup.footnoteref { + font-size: .875em; + position: static; + vertical-align: super +} + +sup.footnote a, +sup.footnoteref a { + text-decoration: none +} + +sup.footnote a:active, +sup.footnoteref a:active { + text-decoration: underline +} + +#footnotes { + padding-top: .75em; + padding-bottom: .75em; + margin-bottom: .625em +} + +#footnotes hr { + width: 20%; + min-width: 6.25em; + margin: -.25em 0 .75em; + border-width: 1px 0 0 +} + +#footnotes .footnote { + padding: 0 .375em 0 .225em; + line-height: 1.3334; + font-size: .875em; + margin-left: 1.2em; + margin-bottom: .2em +} + +#footnotes .footnote a:first-of-type { + font-weight: bold; + text-decoration: none; + margin-left: -1.05em +} + +#footnotes .footnote:last-of-type { + margin-bottom: 0 +} + +#content #footnotes { + margin-top: -.625em; + margin-bottom: 0; + padding: .75em 0 +} + +.gist .file-data>table { + border: 0; + background: #fff; + width: 100%; + margin-bottom: 0 +} + +.gist .file-data>table td.line-data { + width: 99% +} + +div.unbreakable { + page-break-inside: avoid +} + +.big { + font-size: larger +} + +.small { + font-size: smaller +} + +.underline { + text-decoration: underline +} + +.overline { + text-decoration: overline +} + +.line-through { + text-decoration: line-through +} + +.aqua { + color: #00bfbf +} + +.aqua-background { + background-color: #00fafa +} + +.black { + color: #000 +} + +.black-background { + background-color: #000 +} + +.blue { + color: #0000bf +} + +.blue-background { + background-color: #0000fa +} + +.fuchsia { + color: #bf00bf +} + +.fuchsia-background { + background-color: #fa00fa +} + +.gray { + color: #606060 +} + +.gray-background { + background-color: #7d7d7d +} + +.green { + color: #006000 +} + +.green-background { + background-color: #007d00 +} + +.lime { + color: #00bf00 +} + +.lime-background { + background-color: #00fa00 +} + +.maroon { + color: #600000 +} + +.maroon-background { + background-color: #7d0000 +} + +.navy { + color: #000060 +} + +.navy-background { + background-color: #00007d +} + +.olive { + color: #606000 +} + +.olive-background { + background-color: #7d7d00 +} + +.purple { + color: #600060 +} + +.purple-background { + background-color: #7d007d +} + +.red { + color: #bf0000 +} + +.red-background { + background-color: #fa0000 +} + +.silver { + color: #909090 +} + +.silver-background { + background-color: #bcbcbc +} + +.teal { + color: #006060 +} + +.teal-background { + background-color: #007d7d +} + +.white { + color: #bfbfbf +} + +.white-background { + background-color: #fafafa +} + +.yellow { + color: #bfbf00 +} + +.yellow-background { + background-color: #fafa00 +} + +span.icon>.fa { + cursor: default +} + +a span.icon>.fa { + cursor: inherit +} + +.admonitionblock td.icon [class^="fa icon-"] { + font-size: 2.5em; + text-shadow: 1px 1px 2px rgba(0, 0, 0, .5); + cursor: default +} + +.admonitionblock td.icon .icon-note::before { + content: "\f05a"; + color: #19407c +} + +.admonitionblock td.icon .icon-tip::before { + content: "\f0eb"; + text-shadow: 1px 1px 2px rgba(155, 155, 0, .8); + color: #111 +} + +.admonitionblock td.icon .icon-warning::before { + content: "\f071"; + color: #bf6900 +} + +.admonitionblock td.icon .icon-caution::before { + content: "\f06d"; + color: #bf3400 +} + +.admonitionblock td.icon .icon-important::before { + content: "\f06a"; + color: #bf0000 +} + +.conum[data-value] { + display: inline-block; + color: #fff !important; + background-color: rgba(0, 0, 0, .8); + -webkit-border-radius: 100px; + border-radius: 100px; + text-align: center; + font-size: .75em; + width: 1.67em; + height: 1.67em; + line-height: 1.67em; + font-family: "Open Sans", "DejaVu Sans", sans-serif; + font-style: normal; + font-weight: bold +} + +.conum[data-value] * { + color: #fff !important +} + +.conum[data-value]+b { + display: none +} + +.conum[data-value]::after { + content: attr(data-value) +} + +pre .conum[data-value] { + position: relative; + top: -.125em +} + +b.conum * { + color: inherit !important +} + +.conum:not([data-value]):empty { + display: none +} + +dt, +th.tableblock, +td.content, +div.footnote { + text-rendering: optimizeLegibility +} + +h1, +h2, +p, +td.content, +span.alt { + letter-spacing: -.01em +} + +p strong, +td.content strong, +div.footnote strong { + letter-spacing: -.005em +} + +p, +blockquote, +dt, +td.content, +span.alt { + /*font-size: 1.0625rem*/ +} + +p { + margin-bottom: 1.25rem +} + +.sidebarblock p, +.sidebarblock dt, +.sidebarblock td.content, +p.tableblock { + font-size: 1em +} + +.exampleblock>.content { + background-color: #fffef7; + border-color: #dddddd; + -webkit-box-shadow: 0 1px 4px #e0e0dc; + box-shadow: 0 1px 4px #e0e0dc +} + +.print-only { + display: none !important +} + +@page { + margin: 1.25cm .75cm +} + +@media print { + * { + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important + } + + html { + font-size: 80% + } + + a { + color: inherit !important; + text-decoration: underline !important + } + + a.bare, + a[href^="#"], + a[href^="mailto:"] { + text-decoration: none !important + } + + a[href^="http:"]:not(.bare)::after, + a[href^="https:"]:not(.bare)::after { + content: "("attr(href) ")"; + display: inline-block; + font-size: .875em; + padding-left: .25em + } + + abbr[title]::after { + content: " ("attr(title) ")" + } + + pre, + blockquote, + tr, + img, + object, + svg { + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + svg { + max-width: 100% + } + + p, + blockquote, + dt, + td.content { + font-size: 1em; + orphans: 3; + widows: 3 + } + + h2, + h3, + #toctitle, + .sidebarblock>.content>.title { + page-break-after: avoid + } + + #toc, + .sidebarblock, + .exampleblock>.content { + background: none !important + } + + #toc { + border-bottom: 1px solid #dddddd !important; + padding-bottom: 0 !important + } + + body.book #header { + text-align: center + } + + body.book #header>h1:first-child { + border: 0 !important; + margin: 2.5em 0 1em + } + + body.book #header .details { + border: 0 !important; + display: block; + padding: 0 !important + } + + body.book #header .details span:first-child { + margin-left: 0 !important + } + + body.book #header .details br { + display: block + } + + body.book #header .details br+span::before { + content: none !important + } + + body.book #toc { + border: 0 !important; + text-align: left !important; + padding: 0 !important; + margin: 0 !important + } + + body.book #toc, + body.book #preamble, + body.book h1.sect0, + body.book .sect1>h2 { + page-break-before: always + } + + .listingblock code[data-lang]::before { + display: block + } + + #footer { + padding: 0 .9375em + } + + .hide-on-print { + display: none !important + } + + .print-only { + display: block !important + } + + .hide-for-print { + display: none !important + } + + .show-for-print { + display: inherit !important + } + +} + +@media print, amzn-kf8 { + #header>h1:first-child { + margin-top: 1.25rem + } + + .sect1 { + padding: 0 !important + } + + .sect1+.sect1 { + border: 0 + } + + #footer { + background: none + } + + #footer-text { + color: rgba(0, 0, 0, .6); + font-size: .9em + } + +} + +@media amzn-kf8 { + #header, + #content, + #footnotes, + #footer { + padding: 0 + } + +} + +/* Apache Unomi manual — aligned with unomi.apache.org design system */ +:root { + --unomi-primary: #6d5ce7; + --unomi-primary-dark: #5a48c9; + --unomi-primary-light: #ede9fe; + --unomi-primary-subtle: #f5f3ff; + --unomi-hero-accent: #a78bfa; + --unomi-text: #1e293b; + --unomi-text-muted: #64748b; + --unomi-bg: #ffffff; + --unomi-bg-soft: #f8fafc; + --unomi-border: #e2e8f0; + --unomi-glass: rgba(255, 255, 255, 0.92); + --unomi-on-dark: rgba(255, 255, 255, 0.8); + --unomi-on-dark-muted: rgba(255, 255, 255, 0.75); + --unomi-hero-label: rgba(255, 255, 255, 0.6); + --unomi-hero-link: rgba(255, 255, 255, 0.75); + --unomi-hero-accent-glow: rgba(167, 139, 250, 0.15); + --unomi-gradient-start: #312e81; + --unomi-gradient-mid: #5b21b6; + --unomi-gradient-start-fade: rgba(30, 27, 75, 0.2); + --unomi-gradient-dark: linear-gradient(135deg, var(--unomi-gradient-start) 0%, var(--unomi-gradient-mid) 50%, var(--unomi-primary) 100%); + --unomi-radius-sm: 0.5rem; + --unomi-radius: 1rem; + --unomi-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06); + --unomi-transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1); + --unomi-nav-height: 4.25rem; +} + +html { + scroll-padding-top: calc(var(--unomi-nav-height) + 1rem); +} + +body.book { + font-family: "Inter", system-ui, -apple-system, sans-serif; + color: var(--unomi-text); + background: var(--unomi-bg-soft); + padding-top: 0; + -webkit-font-smoothing: antialiased; +} + +body.book #content, +body.book #preamble { + font-family: "Inter", system-ui, -apple-system, sans-serif; +} + +body.book a { + color: var(--unomi-primary); +} + +body.book a:hover, +body.book a:focus { + color: var(--unomi-primary-dark); +} + +body.book h1, +body.book h2, +body.book h3, +body.book h4, +body.book h5, +body.book h6, +body.book #toctitle { + font-family: "Inter", system-ui, -apple-system, sans-serif !important; + color: var(--unomi-text) !important; + text-transform: none !important; + font-weight: 700 !important; + letter-spacing: -0.02em; +} + +/* Site navbar (matches unomi.apache.org white glass bar) */ +#unomi-site-nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 2000; + height: var(--unomi-nav-height); + box-sizing: border-box; + background: var(--unomi-glass); + -webkit-backdrop-filter: blur(12px); + backdrop-filter: blur(12px); + border-bottom: 1px solid var(--unomi-border); + box-shadow: var(--unomi-shadow-sm); +} + +#unomi-site-nav .unomi-navbar, +#unomi-site-nav .nav-inner { + max-width: 72rem; + margin: 0 auto; + height: 100%; + padding: 0 1.25rem; + display: flex; + align-items: center; + gap: 1rem; + flex-wrap: nowrap; + box-sizing: border-box; + position: relative; +} + +#unomi-site-nav .nav-toggle { + display: none; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 0.3rem; + width: 2.5rem; + height: 2.5rem; + padding: 0; + border: 1px solid var(--unomi-border); + border-radius: var(--unomi-radius-sm); + background: var(--unomi-bg); + color: var(--unomi-text); + cursor: pointer; + flex-shrink: 0; +} + +#unomi-site-nav .nav-toggle-bar { + display: block; + width: 1.1rem; + height: 2px; + background: currentColor; + border-radius: 1px; + transition: transform var(--unomi-transition), opacity var(--unomi-transition); +} + +#unomi-site-nav.nav-open .nav-toggle-bar:nth-child(1) { + transform: translateY(6px) rotate(45deg); +} + +#unomi-site-nav.nav-open .nav-toggle-bar:nth-child(2) { + opacity: 0; +} + +#unomi-site-nav.nav-open .nav-toggle-bar:nth-child(3) { + transform: translateY(-6px) rotate(-45deg); +} + +#unomi-site-nav .nav-menu { + flex: 1; + display: flex; + justify-content: flex-end; + min-width: 0; +} + +#unomi-site-nav .nav-brand { + display: inline-flex; + align-items: center; + text-decoration: none; + margin-right: auto; +} + +#unomi-site-nav .nav-brand img { + display: block; + height: 2.25rem; + width: auto; + transition: opacity var(--unomi-transition); +} + +#unomi-site-nav .nav-brand:hover img { + opacity: 0.8; +} + +#unomi-site-nav .nav-links { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.15rem 0.25rem; + list-style: none; + margin: 0; + padding: 0; +} + +#unomi-site-nav .nav-links a { + color: var(--unomi-text-muted); + text-decoration: none; + font-size: 0.9rem; + font-weight: 500; + padding: 0.5rem 0.85rem; + border-radius: var(--unomi-radius-sm); + transition: color var(--unomi-transition), background var(--unomi-transition); +} + +#unomi-site-nav .nav-links a:hover, +#unomi-site-nav .nav-links a:focus { + color: var(--unomi-primary); + background: var(--unomi-primary-subtle); + text-decoration: none; +} + +#unomi-site-nav .nav-links a.nav-cta { + background: var(--unomi-primary); + color: #fff !important; + border-radius: 999px; + padding: 0.4rem 1rem; + font-weight: 600; + font-size: 0.8rem; + margin-left: 0.5rem; +} + +#unomi-site-nav .nav-links a.nav-cta:hover { + background: var(--unomi-primary-dark); + color: #fff !important; +} + +#unomi-site-nav .nav-version { + color: var(--unomi-text-muted); + background: var(--unomi-bg-soft); + border: 1px solid var(--unomi-border); + border-radius: 999px; + padding: 0.25rem 0.75rem; + font-size: 0.75rem; + font-weight: 600; + white-space: nowrap; +} + +body.book #header > h1:first-child, +body.book #header > .details { + display: none; +} + +/* Hero cover (matches site homepage hero) */ +.cover-page.hero { + background: var(--unomi-gradient-dark); + color: #fff; + text-align: left; + padding: 3.5rem 2rem 3rem; + margin: 0 -1rem 2rem; + position: relative; + overflow: hidden; +} + +.cover-page.hero::before { + content: ""; + position: absolute; + top: -50%; + right: -20%; + width: 60%; + height: 200%; + background: radial-gradient(ellipse, var(--unomi-hero-accent-glow) 0%, transparent 70%); + pointer-events: none; +} + +.cover-page.hero::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 4rem; + background: linear-gradient(to bottom, transparent, var(--unomi-gradient-start-fade)); + pointer-events: none; +} + +.cover-page.hero > .content { + position: relative; + z-index: 1; + max-width: 48rem; + margin: 0 auto; +} + +.cover-page .section-label { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: rgba(255, 255, 255, 0.92) !important; + margin: 0 0 0.75rem; +} + +.cover-page .section-label p { + color: rgba(255, 255, 255, 0.92) !important; +} + +.cover-page .display-title { + font-size: clamp(2rem, 5vw, 3.25rem); + font-weight: 800; + letter-spacing: -0.03em; + line-height: 1.1; + color: #fff !important; + margin: 0 0 1rem; + border: none; +} + +.cover-page .text-hero-accent { + color: #e9d5ff !important; +} + +.cover-page .cover-meta, +.cover-page .lead, +.cover-page .cover-meta p, +.cover-page .lead p { + color: rgba(255, 255, 255, 0.92) !important; + font-size: 1.1rem; + line-height: 1.7; + margin: 0 0 1.5rem; +} + +.cover-page .cover-meta a, +.cover-page .lead a { + color: #fff !important; + text-decoration: underline; + text-underline-offset: 0.15em; +} + +.cover-page .cover-meta a:hover, +.cover-page .lead a:hover { + color: #f5f3ff !important; +} + +.cover-page.hero h1, +.cover-page.hero h2, +.cover-page.hero h3, +.cover-page.hero h4, +.cover-page.hero h5, +.cover-page.hero h6 { + color: #fff !important; +} + +.cover-page .cover-asf { + margin-top: 1.5rem; +} + +.cover-page .cover-asf .imageblock img { + opacity: 0.5; + filter: brightness(0) invert(1); +} + +/* Desktop layout: left sidebar TOC + nav over content column only */ +@media screen and (min-width: 1024px) { + body.book.toc2 #unomi-site-nav { + left: 15em; + right: 0; + width: auto; + } + + body.book.toc2 #toc.toc2 { + top: 0 !important; + height: 100vh !important; + padding-top: 1.25rem; + z-index: 1000; + background: var(--unomi-bg) !important; + border-right: 1px solid var(--unomi-border) !important; + } + + body.book.toc2 #content { + margin-top: 0; + padding-top: var(--unomi-nav-height); + } +} + +@media screen and (min-width: 1280px) { + body.book.toc2 #unomi-site-nav { + left: 20em; + } +} + +body.book.toc2 #toc.toc2 #toctitle { + margin-top: 0.25rem; + margin-bottom: 1rem; + color: var(--unomi-text) !important; + font-size: 1rem !important; + font-weight: 700 !important; +} + +body.book #toc { + border: 1px solid var(--unomi-border); + border-radius: var(--unomi-radius-sm); + background: var(--unomi-bg); + padding: 0.75rem 0.5rem; +} + +body.book #toc .sectlevel1 > li > a { + font-weight: 600; + color: var(--unomi-text); +} + +body.book #toc a:hover { + color: var(--unomi-primary); +} + +body.book #content { + background: var(--unomi-bg); + margin-top: 0 !important; +} + +/* Admonitions */ +.admonitionblock { + border-left: 4px solid var(--unomi-primary); + border-radius: var(--unomi-radius-sm); + background: var(--unomi-primary-subtle); +} + +.admonitionblock td.icon { + color: var(--unomi-primary-dark); +} + +/* Code blocks (dark pre like website) */ +.literalblock pre, +.listingblock pre { + border: none; + border-radius: var(--unomi-radius-sm); + background: var(--unomi-text) !important; + color: var(--unomi-border); +} + +.listingblock pre.highlight { + padding: 0; +} + +.listingblock pre.highlight code { + display: block; + padding: 1rem 1.25rem; + font-family: "JetBrains Mono", "Fira Code", ui-monospace, monospace; + font-size: 0.85rem; +} + +/* Site footer (matches unomi.apache.org dark footer) */ +#unomi-site-footer { + margin-top: 3rem; + padding: 3rem 1.25rem 2rem; + background: #212529; + color: rgba(255, 255, 255, 0.75); +} + +#unomi-site-footer .footer-inner { + max-width: 72rem; + margin: 0 auto; +} + +#unomi-site-footer .footer-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); + gap: 2rem 1.5rem; + margin-bottom: 2rem; +} + +#unomi-site-footer .footer-col-title { + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: rgba(255, 255, 255, 0.5); + margin: 0 0 0.75rem; +} + +#unomi-site-footer .footer-col ul { + list-style: none; + margin: 0; + padding: 0; +} + +#unomi-site-footer .footer-col li { + margin-bottom: 0.5rem; +} + +#unomi-site-footer .footer-link { + color: var(--unomi-hero-link); + text-decoration: none; + font-size: 0.875rem; + transition: color var(--unomi-transition); +} + +#unomi-site-footer .footer-link:hover { + color: #fff; +} + +#unomi-site-footer .footer-divider { + border: none; + border-top: 1px solid rgba(255, 255, 255, 0.15); + margin: 0 0 1.5rem; +} + +#unomi-site-footer .footer-bottom { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +#unomi-site-footer .footer-legal { + margin: 0; + font-size: 0.8rem; + line-height: 1.6; + color: rgba(255, 255, 255, 0.5); +} + +#unomi-site-footer .footer-legal a { + color: var(--unomi-hero-link); +} + +#unomi-site-footer .footer-asf img { + height: 1.875rem; + opacity: 0.5; + filter: brightness(0) invert(1); +} + +@media print { + #unomi-site-nav, + #unomi-site-footer { + display: none !important; + } + + body.book { + padding-top: 0; + background: #fff; + } + + .cover-page.hero { + break-after: page; + margin: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } + + body.book #header > h1:first-child { + display: block; + color: var(--unomi-text) !important; + } +} + +@media screen and (max-width: 1023px) { + :root { + --unomi-nav-height: 3.75rem; + --unomi-toc-bar-height: 3.25rem; + } + + /* Override base AsciiDoc sidebar until desktop width */ + body.toc2 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + #unomi-site-nav { + height: var(--unomi-nav-height); + min-height: 0; + left: 0 !important; + right: 0; + width: 100% !important; + } + + #unomi-site-nav .nav-inner { + height: 100%; + flex-wrap: nowrap; + padding: 0 1rem; + justify-content: flex-start; + gap: 0.5rem; + } + + #unomi-site-nav .nav-brand { + margin-right: auto; + } + + #unomi-site-nav .nav-version { + font-size: 0.7rem; + padding: 0.2rem 0.55rem; + } + + #unomi-site-nav .nav-toggle { + display: inline-flex; + } + + #unomi-site-nav .nav-menu { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + flex: none; + justify-content: stretch; + background: var(--unomi-bg); + border-bottom: 1px solid var(--unomi-border); + box-shadow: var(--unomi-shadow-sm); + padding: 0.5rem 0.75rem 0.75rem; + max-height: calc(100vh - var(--unomi-nav-height)); + overflow-y: auto; + } + + #unomi-site-nav.nav-open .nav-menu { + display: block; + } + + #unomi-site-nav .nav-links { + flex-direction: column; + align-items: stretch; + gap: 0.15rem; + } + + #unomi-site-nav .nav-links a { + display: block; + padding: 0.65rem 0.85rem; + } + + #unomi-site-nav .nav-links a.nav-cta { + margin-left: 0; + margin-top: 0.25rem; + text-align: center; + } + + body.book { + padding-top: var(--unomi-nav-height); + padding-bottom: calc(var(--unomi-toc-bar-height) + env(safe-area-inset-bottom, 0px)); + } + + body.book #content { + padding-top: 0; + } + + body.book #header { + padding: 0; + margin: 0; + min-height: 0; + } + + /* Bottom-anchored TOC bar (full width) */ + body.book #toc.toc2 { + position: fixed !important; + bottom: 0; + left: 0 !important; + right: 0; + top: auto !important; + width: 100% !important; + height: auto !important; + margin: 0; + padding: 0 !important; + padding-bottom: env(safe-area-inset-bottom, 0px) !important; + display: flex; + flex-direction: column-reverse; + max-height: min(85vh, 100%); + border: none; + border-top: 1px solid var(--unomi-border); + border-radius: var(--unomi-radius-sm) var(--unomi-radius-sm) 0 0; + background: var(--unomi-glass); + -webkit-backdrop-filter: blur(12px); + backdrop-filter: blur(12px); + overflow: hidden; + box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.1); + z-index: 1500; + } + + body.book #toc.toc2 #toctitle { + display: flex; + align-items: center; + justify-content: space-between; + min-height: var(--unomi-toc-bar-height); + margin: 0 !important; + padding: 0.75rem 1rem; + font-size: 0.95rem !important; + font-weight: 600 !important; + color: var(--unomi-text) !important; + cursor: pointer; + user-select: none; + flex-shrink: 0; + border-top: 1px solid transparent; + background: var(--unomi-bg); + } + + body.book #toc.toc2.toc-open #toctitle { + border-top-color: var(--unomi-border); + } + + body.book #toc.toc2 #toctitle::after { + content: ""; + width: 0.45rem; + height: 0.45rem; + border-right: 2px solid var(--unomi-text-muted); + border-bottom: 2px solid var(--unomi-text-muted); + transform: rotate(-135deg); + transition: transform var(--unomi-transition); + flex-shrink: 0; + margin-left: 0.5rem; + } + + body.book #toc.toc2.toc-open #toctitle::after { + transform: rotate(45deg); + margin-top: 0; + } + + body.book #toc.toc2:not(.toc-open) > ul { + display: none; + } + + body.book #toc.toc2.toc-open > ul { + display: block; + flex: 1 1 auto; + max-height: calc(85vh - var(--unomi-toc-bar-height) - env(safe-area-inset-bottom, 0px)); + overflow-y: auto; + -webkit-overflow-scrolling: touch; + padding: 0.5rem 0.75rem 0.75rem; + margin: 0; + font-size: 0.9rem; + background: var(--unomi-bg); + border-bottom: 1px solid var(--unomi-border); + } + + #unomi-site-footer { + margin-bottom: calc(var(--unomi-toc-bar-height) + env(safe-area-inset-bottom, 0px)); + } + + .cover-page.hero { + padding: 2.5rem 1.25rem 2rem; + text-align: center; + } +} + +@media screen and (min-width: 1024px) { + #unomi-site-nav .nav-toggle { + display: none !important; + } + + body.book #toc.toc2 { + bottom: auto !important; + padding-bottom: 0 !important; + flex-direction: column !important; + max-height: none !important; + border-radius: 0 !important; + border-top: none !important; + box-shadow: none !important; + z-index: 1000 !important; + } + + body.book { + padding-bottom: 0 !important; + } + + #unomi-site-footer { + margin-bottom: 0 !important; + } + + body.book #toc.toc2 #toctitle { + cursor: default; + pointer-events: none; + } + + body.book #toc.toc2 #toctitle::after { + display: none; + } + + body.book #toc.toc2 > ul { + display: block !important; + } +} +/* Tablet polish: larger touch targets in compact layout */ +@media screen and (min-width: 769px) and (max-width: 1023px) { + :root { + --unomi-nav-height: 4rem; + --unomi-toc-bar-height: 3.5rem; + } + + #unomi-site-nav .nav-inner { + padding: 0 1.5rem; + gap: 0.75rem; + } + + #unomi-site-nav .nav-brand img { + height: 2.5rem; + } + + #unomi-site-nav .nav-toggle { + width: 2.75rem; + height: 2.75rem; + } + + #unomi-site-nav .nav-version { + font-size: 0.8rem; + } + + #unomi-site-nav .nav-links a { + padding: 0.75rem 1rem; + font-size: 1rem; + } + + body.book #toc.toc2 #toctitle { + padding: 0.85rem 1.5rem; + font-size: 1rem !important; + } + + body.book #toc.toc2.toc-open > ul { + max-height: calc(75vh - var(--unomi-toc-bar-height) - env(safe-area-inset-bottom, 0px)); + padding: 0.75rem 1rem 1rem; + font-size: 1rem; + } + + #unomi-site-footer .footer-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .cover-page.hero { + padding: 3rem 2rem 2.5rem; + } +} + diff --git a/manual/src/theme/images/apache-unomi-logo.png b/manual/src/theme/images/apache-unomi-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3da06424ff17a1d697bf4e07936219060f4da1ba GIT binary patch literal 28161 zcmeIb1yEeu^5~5PcL?rIg1ZxfYj6#L0R|ricL~8Yf#B}$?v@}ScyN~h!7aFb5HO%hStGoC5tzNzM?5P=o6cr><5D5?=ARthrrNopWARyiEj@95{ z@BYfL>%F~uL9mw6vV(v?!us`ugh)ujgMi>KHB;5J*OZszGX#T}07hT~Ad@r5`mQ$w zgn+QKHNemUXisJUG%>Rhq}Xe0r64mi5~R@JlxLB*76qD`Nx9krRa_NR4P7k^d5tK9 zg%Aat`R*Kmfc5|~XON|p9iOux#czK3?v8&oGgFZL*2UgJkV52FKr&5vMKV#aEs%_p ziHp&Yg^Qbvo0o}|lbf4^n}Ljtg_V<;g`Jt5g^`V&kAsDegN^K84+0DB=b5Wob;Y;9#C!2DO^ z-+KO;SA3#iOR(+TX#s`U1(^S-`EORg2jFBkW;X<| zG4il;ax!ul^8gun3^@%LdD%D&jM=!k4FHB5zd@9j|GW8rxE2E&I{aEMcdmagEk7!x(mj}#cE*8YQV+_;57g;a&WP*Ga9e~xqpc)KqGD=19n5sKSKP2oBs$U zWoCDmuP%Sf3^uN>VJztMO?g4pNe8lxI zpL^OzT=#%HEI#6Tn9n`!Bd&Wu9u^;QJ(A939S^04@b>tR0kw2!#%0eM(_#Pu+r zd)h}__kcVsKH_?q&pquUu6sZp79Vjv%;%o=5!XE+4~vhu9_DjT`-tlvkcY)bTo3cP zr+vhA56HvfBd&+}+|xedx(DQ8@e$XZHZHTqNH*Ahx1IN~@@L?@xhseHGS=dDh42b7O5SCUYZ6_}m26r5~@T#_{| zm>d-5>VkktQ}4O8b@eVrk1oyX-Gca4M*F_Z?)QgK@VKv$hkyI?Fl*4C+j z_Ad)21q??yoR9T0n|zB&ozM4@>2ZB zR=cViTQ^05-B*j|pUR8utnJ023yl>oJA|KTVvv4Zs%}g5UWfL4vhD$Ay_`4E4Y2^d zTe8nhjCzG;{gWrPIQ+dA>*sBVfwG)joSl0`A*32N=g(g}AF0o0eB-E>#mq@1>HuGL8eiQZZ&X!OWq#-8IlKmYm8M#}Z3pJYJ)dHF$ zu7)HqYhJMw`+`I1rAK;b>~>&cvdTUepY>#|vB&AXdbQA8{pQRMtI10NWqqWax(UY_ zk1p)|>PcH-E>HJr`EyeSCdh^yIyapCZ_hR_!MlKX1+U36U_xx8ONiLM zdYGNbjJN1b56djk&yqs4er-`$bd)WI)SP@^A&+fS>0ZPA-m)e!HpPy= zz7O#NmgGg0Oi}?tLA!DWUCYYy)S(f%FOwoouuLN#`4y~aP}q9=Zp)=Py(h0y&kFo5 z6jQkFv!j*MfxtOBucGCqeu144UawFGw0&HPi9yZpZ;`s1PC*$CZ|x+TPp2nkNW-28 z5Yw~2yM-k&8!Gk`b>~2gh3M`+HHS$Svmgy?Lj4;1TFPw;(eb#by9Qh8@Y<_1`>fvZ%>q~N+7Zy8OX)+~MWEsr+k=E5$F?&`wUuFUo3%haDL@M^Gct`(QZJGQ%t6D|&(MZ#bw&9ZDbk6nTd}syuA5WT5m&4b9YA09Qc(iFP%1vc1!UV22qtlZzlJL7Q*ru zW+a<$gb&;(Ck4D6Y*l=$j>E(NIh4?;u)%F#gB)ozLTCdsd=t9uO9F!2(;O&cb$W*j z=R1dnxO_j4%di|_H|iniX({53Z_hu)gn^xSIva%!tN2X(^0oF1!}N5qAan;qYbu@h zQpcJ(QiIr1T&9hbr4lo2O_(8SOTo*IQ|~QbgmQOlFXHS z1nST~Il;_+Ekm+B>7h|Az92{Sl!c(Xvs;V@j>ln?L7yVZK(>j%Dfmee_geE$9;TG? zy1<@TVFR061n2&|LENs&=mQ)P4a{OlOF>0`o#`Di_+Xo+W`#-GcJEhbHH?-s%bC?n zBVT9ge^i6)9bUagAttc8QcKzM%6;EW%2?^{$8S;WG1JSYIw`l%<}-%lVG|bXuJpuL z4{2Twgd_@vzF|xefpB+4UJ}K2sCsrYf8uP-i*SY+@nTB$k5$vegK^d7V3aj6a3W!o3qeh$}YW%caZ1~2D&2dFFc(1(n*}A2- z+AA+!%L7?6ux{8o8dcH9#Wosuj2<+9O+=xa^C$KLr5*+c5ziZW&qo<%YG5jvA|
LQo`NDCGTVhU=ZGhB*2nKvX&I9Q%NpW8 zVaS%g*dmw9Nd1tekdPm+XA;Whbn4r*xy%*L$EAB(U*Zggi+Pg0yuBenf#V6$c)3@a zGZ`=Dxv3#2Ysg~jpHeU*ocXZ+(ETN&A#uQt-tak38@GcZ&jwD?#8+#rfiJ)pN_aW-F zuoB3Mn9SH%PAebykF=t9y1JErZ7Y47bo=zoR$wHtZr1eWCAv}tHu2!1jG|2glQ?#) zFvT>@)6w&sgZ zcFeZNiumax3S44)<|*;!m~jPuk=k@FL`vtS2Et~iXy%1+_1UK?tQPp6r%wd-wrOnp z!z!%?JeAnFuN?!)STx5it$z^8tg=a#y7Qxf&$#@SYh)0g7F4wak)9Zl$3PB488QxW z#xRa58hf~_2FrW{!)6-8gq7Gu^8`yJXAtR?ct}hG-a!)(u4o-TRg2L!l# z^+p1tKIk@>jbqzlB|c(Om5JprI&2yOY+j+1n6u8hGTq}oI9fOzbM3F z9Kym>6BSjWYk@)p(XyWO^nObp^A3C#Xag5ke2HF-igiQcd}X#bKPf`hPSkZJTAV0S>2=RD49>AjO~3ddKU$n8=xZ3Dclh8K0Y zjLxiN9nU)}21NV5Pz;n)rqUN-5PBtjdF!WnVA(+@=Byb1b3yD&7!7=Br!Oo=obOLG z3Ax*hVzEY`!{nI?oOgURW@X7oS?viC`4ec>&8{Hz1yK(2H1%nUy{DsoP$6N|<cN{lVkOkSUgagy2Z@IAK%;iJB%D4u{b61M?fhPzm~Yu=Kap zm@E?XGn5_#)%a3g5rd^y3UBEFTPl!fC5$#b%C&&?xq3?d8Jtu(;}i6jaq^ubZLOo8 z)5)>R+AN2D0URvqw|Wv)Sd*VUqZ!3)ZqNovD!B0w+OsMs)^4>%?6rPcv4DOOf-VqO zwEXyRS=SNQF7aI6iwsit2T+UUbH-wR(F%AWthCv8ImxdfN@;@^!O(-5!2*E(iNP2= z|Me_#5e|2$kjG~xx%Mf~dM6_a|6V3q>{K;nmRtLF#80O=3syZrzqV#LV% zi{Tozr?HQifgvG*<~33vRfJ&5-8G4+dK^u!>oHiBH1URw!py`gX-iYA(XtfIQ+4S) z2BULMnAEuodtnawDojfKoY02&1Zm0;848!~%WK0>GjDm6|WkxG)|zlR^zVD&!2NT^5wPYesXFdCTcINqnC->ZZ&}M8d%gEyt%<_doVY8 zPZr#sC=FexeRR)FG(ENAJU5R0+$|f#Rp#Mi`cXS9zE1tn{s$0f1P<*=E2BPgXg`Gxe4_CN86#4so#yC9IvVbv&Nx(Qlm9+?%+p zC^eLmQ?T0n4qj9>1hYwYcM~oz9O5r-nmJnvhiz$~FhMIML^yX@s!ujQU)%(98IL=E z3v=0RF^ns*a#Qys40%hc+E2Vb=3I{z_;a7oTl=GZ>xWI>ft&o`y*FRxr*b7$!o(WR zkEw9Mkn?EUp>T5qWzV>jpI2>%i9|{3WP#NpFS>_gPHx1$cTT|X5=NV8g$!Zfhh0uz zq{QYHZkZC9^eTSCRg*eFr!Q$Sd^dQLD5Y1R?vX9J+`f#Fk!-C3z)+GIzRi!^?q0TD zY%CAi2!s_X8RPsQcu;gzfKTpT3>YS7;=XkZB=l5H)m{wW5z@GJLt|vbiRtuyJI>e3 zWx%bIaKO3vTs(V5GBKZt)TA2*dOFqs(^PdvzMH*BucG#+{S&;53VF46B0hkWt(vR3 zilTKqEVfG=S(NEGLv1!Q z&VtF+!yJ0#ING=C9Q!#b=#6nq7DMN#rJ38k_%r3-d4|TV4pLJWI!V3sK4$SiUz3x1 zo^oBdimo|K{h*2!6YVm5HZdJ<7CN?iOAtZo#Aoek`NUDVk#oX7oUy1~L|(G{yh!^P zmc)5cC#>mb;IS)3gewsyXEFRwU`c#&3 zLg{t4l~sdHAd=oxFOuG@91Z)8(F)iJ_iU;f`szFez$V|3{#Ey#?=~6^5%FznsA+@) zO3*?4C_msoXd&TirY}z`aoqnQIaXeE($wS$u<1e zdZ)SM+$+Fb>X)2KH=*in68p*FA74|jZ*Pb3&w^5)>_Y59c?$W|o>Z34{E)m{sUL8b zGsGhp!3T6MG0oJ%xF7T;U;Tu;X3$+@EB%y_ueB77;?YdbUvnW)Ge8NC9D-V2PAuJX zv-l~$&CTR2K@wg3rCfr1(Uh)KNm4uZ&z5%RuD5tnoL;VVihMd2UIQOI1TMQLsM$6~ z*>S5JJnU2k>II@}PPZ*58UkML*3&rtdF8gxhr z7Fa~^%3{V#%lvBaCbx6{!&<2m&i=L3I}z2WTdrh`h6Yd(&ri04DblDj$Otc|oCCH+ zWg3J+ivcED4l|jST4obGlo~gGwoSu0hn}QQD!0dK z6A$+}DNab9d%f9Wvubu@^fD(}O6~ccUKiH2beS)&)21=y6t>w$bv67M?E^^JsKT82 zxwNmKiv?EpgbzW$PPfJF^9ZohEQ=|ar4GN06tD9)WRFAIR_hB=2qNNJ`2vONWijPXpDdfU=8&mjY0FU^v_io#> z7%W4ikrp+ksV3>F2IsVK7_VPq>z0>(d5xN0itLv=MlFZ;?&BLvppKAVz8+J1Iq^G| z?P1KO;#`yNW5#+eWh?VnpiqQi33RH`>iKL|LgaR0GEx{`wC%#E3RF&siRc$|X=bvu zEpDIIj`bGKMpw{GMzmNG zRHiO`9sx2<<>cd52n?2aShVTw5v@734pi=@v(+&6>FG<3AO?Yj+kXP2)5_GC8)!t?oSP06xucc= zua~d}e9Y?}{q^=-3U^TOd4Ni`MVEtL_E(}9CX1ETQnQtt%iz~Xdd;)v>rgvK3~LUP zz5s(=($7EO#?cY%i=j|K`{xThBum`24DD9t9ptz=?Xb9oUv1g;P&-^-^J12^CfFT) zYP7N}pHVV6sPbxACWrP>WIFaDUy0L+`sSX9w=*ttL0$U!DttF&y+833fqB1{PKg89 zh_OyJUXoaD(wr<+hqkC$Fiod{I}ZIGV+T(-B?u^7&}s1&4I?N`oJ<3_Za zl`j-d#UJZukKfj1+?m2ksF2U5lax7LSD}FMmVPSZqw)Rgs|aQ=aJ}u?$ab!(rYLFG zzHP3xP*26?*t;_xottfK9yen;AVM>zQczmgoaJ_}y+h7<>0 znkjD9mia4Viq&yhLiaiO1O3AdXINeW@(8+VGY$!+r7T|QGZ}9UF3agfo13pXXQiYJ zzynMZZLb$Jg>{FrNG|J_{8Ke!w8DAiMW087-~KdRE43+C_esU+tDgxoRU-JNYSy9=4k)&)to6O?eAKk2Lu&jXImUv0n8ddc-X zY&ws64D?wRHqor>D3Fa%&1Vopnx?5Te2awr!q2caO`2VEQXKvJf&2UOBOIaU$-Rmv zvTER}V1eDGkFd9MF@mJ5sA(IfSo^v!_Q>~TOnLcIJ31(1m99RO(?;5|NA-$g2gs&Q zU^cxJQz+;^He$iS^_8$vqJVk!S>8deyRs#UPDLyGOnKYUMI?Em`JHKTnsQU3DVAEX zn}k9fE7AhT(9!jGiT8H}h;PM6_}#DDFpcvIUsPVX!iX5UU-G$A=RgOhoo@&;^Um63OntX$OpQ^{*gHfpBAh8Fp|B^CV{59SaiAcO*vyK5i{-P54a&^v1Kr!l~l%T?X2)YrG13#Vm89<=ko z1Km2qZKSgj;(Jy&{HrA zh|S<-Q|cze7D#mpU00a{$HC^EgXRpUl;#>|eO)_+UegGzjI=(DNB*i|!|W6h3LZ7m z2i~vxQlIwi51cw3^oRm$tWM%v(@5srq4Of~5@j-|#C$r)+p89}H_>!2$@g6{1X9N+ zM62Kh1V$t$K(pQeejqsTHCL_k>fso145odwkUOz|+V?@(*it$L4t|y{eSTC(@{E3} zN_g@7?Qcnm-Ad2x+&&L<@lysDR-+0l`GlcK#j@$rQp^H6T#RP@pX<+R7}m>+0TR1M zH}mq!XJPe`hIhBM8NT92M3u3+N;Z40&(A@EJv1ag9gdBMSgIeyIN9$nomx`EXLNo~ zKvqpLZT+p4fBp)qs$1 z)QrN^mc{4F-6tf)nOEU_@U7!f?gq!e8d!ETA8*I5W&lezS@>-&lBWljEdw3V!_%Y2>NI1+Jwzxm%Ne$-G zv!_-}AVt0;Ppj=)I+MdeH$Cn&bxk%61qlUG})q z2z`{)n8hm!eSI4aeY&f(!0|VkDGot&BGT_9uv#*U0iHC_$#Z29CY~_1Gx%W=!olew8DO_XYSZ?@I#P_Q zAc|90f+%6d$SZ19U((EB2PLSP_QjcETuQUZ7M4_w2d-32m-wt2m-h^{!E=gZ?s5tG zl5+9t7-@#}T&Z!NZlGz|_YeDe)Dx1lU6rf^;ezy$N@sck*u31%NK=UK4DgC5hFb!GAIT<6ap@BH~ zGqaFF*N2t-?iW?HqzSu5zl)Tg^juP%oXwKCH)Caex+%{3Dv7CnGYYlv!J@`_SF2v@ z8u~pSm;AbWqV1F_FAIKK%Qw?nVIoksyglU(jz5wM?9qGb;>|@d`NHaS)bc3CqnTKP z(EI^Tb4w^!mY9xmK327_tQIgDW8^EACp-aC3|G7&XP)Q=xJ7!?`13mbl!PSXcW-K} zN6RbuMfxK}iNn1l{DtS(DyA9hl> zeGh`On6fvyyRlW6%{c+^fq9A(Je_MxA9WN3CGth!r2u-q?TUtVU_PLTt!r^oVX6kC zYy~SUMH^m^RVanCHgNpf=T4rO9F6Uu!crB^=9t_ye2!)++CUsW^G- z`O{KcM!dMh@#b`;^L^B3cJqpdhuF;`RVU?lk+zJ?C(Rh&Zalj@Kc{frQf!08Eaq3LgsEV8sMD#JU(8K4V&T!ELQ$8PE8_4fw zF!wbbP7fUpl4jwIg5QZ(M<+*CHvNHMUSo|CUX_H4cZ?-lgz!aZ)IT(uf)RDvce^;v zY)Il;f*IvLbj_({cg>h@NE@)V@2bX6a*`Kruv7^2qFgnruyW-_9H~(%cdfE<&vVy} zE|nv6O5rSku>qc%OB}qvC+g>&s8VtaCFoZY&Z@*!SaNRXxBoPp5agmEle6uN=rp(S(#XP$H*Kjjv(ylqm`oIm3~}{0a{FSC)y#rxnvjU8R9dQ z9ax62E}Y}n*_O&1+w&@Q&MKor`{?vDLF?3Afxup2felnSuU3p7%|I#yG_}FR^8CW?F0KRxQPZoHxVRwT2`5x1H*Ie zaMk1lp_k)$jrMkedM?csrrjDl*dm*y0lAFuJBj%a7kAfCqy?|#(Zx>b`4)8TZ65tx zRqm7@AJq^!EwKkxNDM=!<{{-FNv;;+J1znY%~1zR?k46UFtFbxJ0x zp2xg&H_-*!`O;?b;Satg-RU#_pwf*eqSBdhNWKdRU&AsQev(2nH+_K-H3^>l7`i(o zG`M{Cky_o2Bs7Y&4vz=%x1p%YA8+(Cs{x0coX`fKvhD5lXm1(anRvP2tC+BA^>UJK zuV6aJPe9I?TV$nz8W^>V?PH~rn#ivX82&p@S=P((?U&UjmMG( z2r(6gUqE4O_|w{qFINCLQ7?ju&Lh`9er+IXD0>1z$>_ZMJe`#slrqFyx&dm@%M>qx z5jZAWuw(X9>TpzeCDpF)C(e)NVTzzz6oK^LVke|_q>ic=A>#eeX6vzr|;nMsE%1b|jvv=rGGn4gh^2M>O#gM{F zM`8%rLa&!j7bK&I=|qU}2%IBn1ksp@$ocI`ZGL{F(~^l^ikG1mXqD2g(A!?f2REFz z6HX-E^i@V!CM>0UR;kH%73O-}LW?$MCDDAgT90Y8GFr_rMAfa+KUWj?iqp2;3$__5 z_$j6ZsvAHjqm$_cmc?1}Bw_h_&9XOip=-X)SQQn#ZeoBxe%FA>Hi)6wtKW!!bJA0S zCQyBh!DbfFOG)yyIcJ;CXlnh9s965oJkk~L-5wE=do&%r{X4#Li$ySU zCDu;NFijh}x$J5SD84UOP98Ft092J{-wkHuc6wLZA08!^o>D!d7UAfr1toA*wK{N1 ziqfyd@=n!*De6U5qWGQS) zGRJvWy5=Ui9z$BSD4suFX4&UPrmcolf(XHq3TovO#HuxIoTT$2q+Q}?s7V-St0)dN zRxfwbJ*E}-PpG}*bc$)Cn$ru6kut*wDM`FHnTcwE@qC~4`ASvGa5S@sC`h>GY&A_~JXffon) zV9zW^nsRZ^)5Cr!qxH)ND2)hG!XEL3o|903^MX>uF<_`eZE@ZD*Gw0L(y2Lx-LS%gy?${i!8G^|EX*=J2_Rgh) zTyJfbjoDVy31O`eCk63F`E??WUd>l{a|aD&V?fP7wsTcG=tO} z1L@wniSiXLf;sy}s+$`Wcjv^=bLV@7-8olQ^iOS${noDm*yUY2Pz+nc1 z_n%BgCG-qAYF33r>5qZH856wX10S|(@BmII0xRI4P+K_1QuNknpqrU*R1bP%g-SQV?Mw6X^_-(p4Lvt{)mYBi#Ape8jqp?;qHZku&FrT& z;?~u;73OyH-pu1bA(0j;2O+286(fPa+CEV7EIu>P%(mC1er2 zv$O_?x3}wK7p@O*oq_Koirn8QQpE0FoSAhC-X2@^f{vkEQ;dVTVBs;8a^LZ)1zq5= zn8K6F^B!FiQQ{%Yh^PlkZIKu*{P54i;SU#G49K3@ZCa)Dv&HCkT%MBTFAJK#;g9rZ z(SX18J)|;_mrzV6Z!CAaw$3@5)lF{Ps|XFgdS^G=z@6?8L_NU#5@84cQUQPM9ZDTM z8Vye*P%9tojJdga;cZG88*LxbtMm*cKAOp(>584btp7b60j1Z|-06$UPf$rw3l7%X zIRB~{B~wVditL0kNRWHZbEKeROILq4p&AQkQ^9F{KUhPKd{r#b^J*B83_oELcrUG~ zgo-fUW}F$F@4F!4>~$^WI%@5)q{dz1;^OB+hk^{y-Q+=ad$*?_hv9^|vRh+Xk0Q?}?LptdHnEUjZI%N* z%?XT-L61nFhnjvfqD;OTjj6S-W)zyjT(7exDBkZvqtd5`>qg{0zI@+2AWukSvkZw+ zG4;f9&Q&mxYwiQbTV5D7T}pVxXGgTPLk>x2-!tVD?b0D7ae`2HH1H@F5>_`R-50al z2y2FQ0s0BJJ@-(~s(U#%B?osvBfW60MbT z_=1HD*&>-wKH%h-jeFUTlu}ujD9+nL7sfK=wuN~Evl`GnuJ_RXJd>S^`na9T<|(e`xP;s{ zJTCii9829cEfP=YqUQnM9J9tZ{qs-;g$lJ`qLk4#hP2E=9dZNXW%FZ5$);Z%|KNDD z;=cGk^UZ=lV%WgcRoTVGc!lRR+7}~Ka%AYLaZ}=aU2V;@er+s&+7MRp_|8fLYQ!k# z6sA=2xEbYeKSQ|51ArE_ZG9&)>P;xwB$w`(b)Hz}^`e+yST0vOkr{>)eA1 zAg==4`IVsM)Zy5M`T%3M1 zJfA-32Y(3pF$zW{DS>6v@awOP-l@p}3b!x6zs2-)iDZc%K1U-T^TGAL)dx*KRR@(S+UlVlTTc%n|#!@a9WsdKXTo~r-`=5 zSLNcXM6fm_ni6k<#k}EF=C*8SYg)vzuC&pFu+exZD|^3`@DtT8!3Dy=Zh>@a)t%>F zy3e&j;i}u9kk-AG0&*uUx1e^OI?%0!$`Y>8hV-a;546Y=mf)=FF?YOV$C_n^^+9y# zA#@;H;MOe9_IhCj??UAKYBmpz7I8rULld?N>s%Mh?R?~C%@$x9d?&FxNx&!Fh?-MuQ;4WY4GS^`F;pHx`7v+{c z{Sj4^FCEZ7RgdlmKu51(9Xi#Z*D!FT1G{gYN)UpvrC zjQv@ary`CEJ569s#18o-wDx#Co>N}6+~Dr()Qu2KSM31(+jKDxgzsBhv*nDXY;6PPz z0UQ#iF3Pk%y!&xJi}e~IWh-Wczl(+#%#0(tCZY2HD`4@pCCe&w;sO?t^9N_v3H_J1 z;Vh$t&|gw1x^8u&WaWqJJSi#J4|JN-tS_wVo$5(1Y6E%5)UjKV7TpTtd0FoKkr5l&$m!u}PIHI>t$NgUCA=l#J`1TIRHaUDk9V z$#36?N4(7PR>LEv%9WqV$eQMmIpW*&huU-4)v{jn<2AP_y1p^J;&0O@hMC&N+(vOr z(6N;pN?;kX5SVi>U|oxfI&5K(QFwCl!&c6d0A<)4;9Z{!{Q$8!-!0Jd1k0DweRefeY0MuJ3E>oz~UB^>Buax-? zW!MLkC0KzIp}hsy4hLPjPuOiPfo}P=gxBuXySsZ_H%~G5IFZ7p7&9C%MK-LcPijFk zVOKjDM9A?x*!8B!E$TojIqyT(XQ6QDQ{B}c$!s?I+D6CHs49RRio)pTm(&zHA)z%b z5X*%2O$`<-rl0GC+c}7F6Z&jK=vs%nLgqvwIc8ztLR#VcK6}GI6FQk(6Jo(56=F*m zQe&`+n>;``!CKlYUooVI&2Gc9(7`J}qc=0REs6;V%bN+a5i*ul35XmmY}2v`G`OaY o_otCkoT7^$Yv(z;g@f=CVizVI;K%>c9Jc0HuJpr2qf` literal 0 HcmV?d00001 diff --git a/manual/src/theme/unomi-theme.yml b/manual/src/theme/unomi-theme.yml new file mode 100644 index 000000000..ff3bf2b7c --- /dev/null +++ b/manual/src/theme/unomi-theme.yml @@ -0,0 +1,284 @@ +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +font: + catalog: + #OpenSans + OpenSans: + normal: OpenSans-Regular.ttf + italic: OpenSans-Italic.ttf + bold: OpenSans-Bold.ttf + bold_italic: OpenSans-BoldItalic.ttf + #DroidSerif + DroidSerif: + normal: DroidSerif-Regular.ttf + italic: DroidSerif-Italic.ttf + bold: DroidSerif-Bold.ttf + bold_italic: DroidSerif-BoldItalic.ttf + +page: + background_color: #ffffff + layout: portrait + margin: [0.5in, 0.67in, 0.67in, 0.67in] + # margin_inner and margin_outer keys are used for recto/verso print margins when media=prepress + margin_inner: 0.75in + margin_outer: 0.59in + size: A4 +base: + align: justify + # color as hex string (leading # is optional) + font_color: #333333 + # color as RGB array + #font_color: [51, 51, 51] + # color as CMYK array (approximated) + #font_color: [0, 0, 0, 0.92] + #font_color: [0, 0, 0, 92%] + font_family: DroidSerif + # choose one of these font_size/line_height_length combinations + #font_size: 14 + #line_height_length: 20 + #font_size: 11.25 + #line_height_length: 18 + #font_size: 11.2 + #line_height_length: 16 + font_size: 10 + line_height_length: 15 + #font_size: 11.25 + #line_height_length: 18 + line_height: $base_line_height_length / $base_font_size + font_size_large: round($base_font_size * 1.25) + font_size_small: round($base_font_size * 0.85) + font_size_min: $base_font_size * 0.75 + font_style: normal + border_color: #cccccc + border_radius: 0 + border_width: 0.5 +# FIXME vertical_rhythm is weird; we should think in terms of ems +#vertical_rhythm: $base_line_height_length * 2 / 3 +# correct line height for Noto Serif metrics (comes with built-in line height) +vertical_rhythm: $base_line_height_length +horizontal_rhythm: $base_line_height_length +# QUESTION should vertical_spacing be block_spacing instead? +vertical_spacing: $vertical_rhythm +link: + font_color: #6d5ce7 +# literal is currently used for inline monospaced in prose and table cells +literal: + font_color: #6d5ce7 + font_family: DroidSerif +menu_caret_content: " \u203a " +heading: + align: left + font_color: #1e293b + #font_color: $base_font_color + font_family: OpenSans + font_style: bold + text_transform: none + # h1 is used for part titles (book doctype only) + h1_font_size: floor($base_font_size * 2.6) + # h2 is used for chapter titles (book doctype only) + h2_font_size: floor($base_font_size * 2.15) + h3_font_size: round($base_font_size * 1.7) + h4_font_size: $base_font_size_large + h5_font_size: $base_font_size + h6_font_size: $base_font_size_small + #line_height: 1.4 + # correct line height for Noto Serif metrics (comes with built-in line height) + line_height: 1 + margin_top: $vertical_rhythm * 0.4 + margin_bottom: $vertical_rhythm * 0.9 +title_page: + align: center + logo: + top: 8% + align: center + image: image:images/apache-unomi-logo.png[pdfwidth=50%,align=center] + title: + top: 42% + font_size: $heading_h1_font_size + font_family: $heading_font_family + font_style: bold + text_transform: none + font_color: $heading_font_color + line_height: 0.9 + subtitle: + font_size: $heading_h3_font_size + font_family: $heading_font_family + font_style: bold_italic + font_color: $heading_font_color + line_height: 1 + authors: + margin_top: $base_font_size * 1.25 + font_size: $base_font_size_large + font_color: $heading_font_color + font_family: $heading_font_family + revision: + margin_top: $base_font_size * 1.25 +block: + margin_top: 0 + margin_bottom: $vertical_rhythm +caption: + align: left + font_size: $base_font_size * 0.95 + font_style: italic + # FIXME perhaps set line_height instead of / in addition to margins? + margin_inside: $vertical_rhythm / 3 + #margin_inside: $vertical_rhythm / 4 + margin_outside: 0 +lead: + font_size: $base_font_size_large + line_height: 1.4 +abstract: + font_color: #5c6266 + font_size: $lead_font_size + line_height: $lead_line_height + font_style: italic + first_line_font_style: bold + title: + align: center + font_color: $heading_font_color + font_family: $heading_font_family + font_size: $heading_h4_font_size + font_style: $heading_font_style +admonition: + column_rule_color: $base_border_color + column_rule_width: $base_border_width + padding: [0, $horizontal_rhythm, 0, $horizontal_rhythm] + #icon: + # tip: + # name: fa-lightbulb-o + # stroke_color: 111111 + # size: 24 + label: + text_transform: uppercase + font_style: bold +blockquote: + font_color: $base_font_color + font_size: $base_font_size_large + border_color: $base_border_color + border_width: 5 + # FIXME disable negative padding bottom once margin collapsing is implemented + padding: [0, $horizontal_rhythm, $block_margin_bottom * -0.75, $horizontal_rhythm + $blockquote_border_width / 2] + cite_font_size: $base_font_size_small + cite_font_color: #999999 +# code is used for source blocks (perhaps change to source or listing?) +code: + font_color: #5c4cdb + font_family: $literal_font_family + font_size: ceil($base_font_size) + padding: $code_font_size + line_height: 1.25 + # line_gap is an experimental property to control how a background color is applied to an inline block element + line_gap: 3.8 + background_color: #f5f5f5 + border_color: #f5f5f5 + border_radius: $base_border_radius + border_width: 0.75 +conum: + font_family: OpenSans + font_color: $literal_font_color + font_size: $base_font_size + line_height: 4 / 3 +example: + border_color: #f5f5f5 + border_radius: $base_border_radius + border_width: 0.75 + background_color: #f5f5f5 + # FIXME reenable padding bottom once margin collapsing is implemented + padding: [$vertical_rhythm, $horizontal_rhythm, 0, $horizontal_rhythm] +image: + align: left +prose: + margin_top: $block_margin_top + margin_bottom: $block_margin_bottom +sidebar: + background_color: #eeeeee + border_color: #e1e1e1 + border_radius: $base_border_radius + border_width: $base_border_width + # FIXME reenable padding bottom once margin collapsing is implemented + padding: [$vertical_rhythm, $vertical_rhythm * 1.25, 0, $vertical_rhythm * 1.25] + title: + align: center + font_color: $heading_font_color + font_family: $heading_font_family + font_size: $heading_h4_font_size + font_style: $heading_font_style +thematic_break: + border_color: $base_border_color + border_style: solid + border_width: $base_border_width + margin_top: $vertical_rhythm * 0.5 + margin_bottom: $vertical_rhythm * 1.5 +description_list: + term_font_style: bold + term_spacing: $vertical_rhythm / 4 + description_indent: $horizontal_rhythm * 1.25 +outline_list: + indent: $horizontal_rhythm * 1.5 + #marker_font_color: 404040 + # NOTE outline_list_item_spacing applies to list items that do not have complex content + item_spacing: $vertical_rhythm / 2 +table: + background_color: #ffffff + font_color: $base_font_color + #head_background_color: + head_font_color: $base_font_color + head_font_style: bold + #body_background_color: + body_stripe_background_color: #f9f9f9 + foot_background_color: #f5f5f5 + border_color: #dddddd + border_width: $base_border_width + cell_padding: 3 +toc: + indent: $horizontal_rhythm + line_height: 1.4 + dot_leader: + #content: ". " + font_color: #a9a9a9 + #levels: 2 3 +# NOTE in addition to footer, header is also supported +footer: + font_size: $base_font_size_small + # NOTE if background_color is set, background and border will span width of page + border_color: #dddddd + border_width: 0.25 + height: $base_line_height_length * 2.5 + line_height: 1 + padding: [$base_line_height_length / 2, 1, 0, 1] + vertical_align: top + #image_vertical_align: or + # additional attributes for content: + # * {page-count} + # * {page-number} + # * {document-title} + # * {document-subtitle} + # * {chapter-title} + # * {section-title} + # * {section-or-chapter-title} + recto: + #columns: "<50% =0% >50%" + right: + #content: '{page-number}' + #content: '{section-or-chapter-title} - {page-number}' + content: '{document-title} - {page-number}' + #center: + # content: '{page-number}' + verso: + #columns: $footer_recto_columns + left: + content: $footer_recto_right_content + #content: '{page-number} - {chapter-title}' + #center: +# content: '{page-number}' \ No newline at end of file From 82f2e518e1fb68d3abacaf0159d9dec4953cde91 Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 16:18:31 +0200 Subject: [PATCH 18/19] UNOMI-960: Fix Javadoc rendering artifacts and misleading docs from review Split merged comment lines that were rendering stray asterisks mid-sentence, restore missing @ in several {@code}/{@link} tags, fix constructor/setter indentation in two REST endpoints, clarify that TenantSecurityService isn't actually wired into the OSGi runtime, and stop showing Blueprint-registered built-in condition classes as if they used Declarative Services. --- .../org/apache/unomi/api/PropertyType.java | 3 +- .../api/services/DefinitionsService.java | 3 +- .../unomi/api/services/GoalsService.java | 9 ++++-- .../unomi/api/services/ProfileService.java | 3 +- .../asciidoc/builtin-condition-types.adoc | 14 ++++----- .../persistence/spi/PersistenceService.java | 15 ++++++---- .../endpoints/ProfileServiceEndPoint.java | 29 +++++++++++++------ .../rest/endpoints/RulesServiceEndPoint.java | 3 +- .../endpoints/ScoringServiceEndPoint.java | 7 +++-- .../endpoints/SegmentServiceEndPoint.java | 7 +++-- .../impl/tenants/TenantSecurityService.java | 16 +++++++--- 11 files changed, 71 insertions(+), 38 deletions(-) diff --git a/api/src/main/java/org/apache/unomi/api/PropertyType.java b/api/src/main/java/org/apache/unomi/api/PropertyType.java index c24f7b837..25b3117d8 100644 --- a/api/src/main/java/org/apache/unomi/api/PropertyType.java +++ b/api/src/main/java/org/apache/unomi/api/PropertyType.java @@ -269,7 +269,8 @@ public void setMultivalued(Boolean multivalued) { /** * Whether properties with this type are marked as protected. Protected properties can be displayed but their value cannot be changed. - * The { protected} flag name may change to { readOnly} in a future release. * + * The {@code protected} flag name may change to {@code readOnly} in a future release. + * * @return {@code true} if properties of this type are protected, {@code false} otherwise */ public Boolean isProtected() { diff --git a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java index ea0bd538f..dd88845c7 100644 --- a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java +++ b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java @@ -212,7 +212,8 @@ public interface DefinitionsService { /** * Finds the first nested condition tagged with the given tag in a root condition tree. * - * Deprecated helper that may move out of this service in a future release. * + * Deprecated helper that may move out of this service in a future release. + * * @param rootCondition condition tree to walk * @param tag tag used to select a condition * @return first matching condition, or {@code null} if none diff --git a/api/src/main/java/org/apache/unomi/api/services/GoalsService.java b/api/src/main/java/org/apache/unomi/api/services/GoalsService.java index 955619f2a..a4d938153 100644 --- a/api/src/main/java/org/apache/unomi/api/services/GoalsService.java +++ b/api/src/main/java/org/apache/unomi/api/services/GoalsService.java @@ -62,7 +62,8 @@ public interface GoalsService { /** * Saves a goal and creates associated rules when the goal is enabled. * - * The { setGoal} name is historical; a { saveGoal} alias may be added later. * + * The {@code setGoal} name is historical; a {@code saveGoal} alias may be added later. + * * @param goal goal to save */ void setGoal(Goal goal); @@ -133,7 +134,8 @@ public interface GoalsService { /** * Saves a campaign and creates associated rules when the campaign is enabled. * - * The { setCampaign} name is historical; a { saveCampaign} alias may be added later. * + * The {@code setCampaign} name is historical; a {@code saveCampaign} alias may be added later. + * * @param campaign campaign to save */ void setCampaign(Campaign campaign); @@ -156,7 +158,8 @@ public interface GoalsService { /** * Saves a campaign event. * - * The { setEvent} name is historical; a { saveCampaignEvent} alias may be added later. * + * The {@code setEvent} name is historical; a {@code saveCampaignEvent} alias may be added later. + * * @param event campaign event to save */ void setCampaignEvent(CampaignEvent event); 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 5ca14f39e..3b6b8c94e 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 @@ -73,7 +73,8 @@ public interface ProfileService { /** * Finds profiles with a given property value, ordered and paged. * - * Prefer { #search(Query)} for new code; this overload remains for backward compatibility. * + * Prefer {@link #search(Query)} for new code; this overload remains for backward compatibility. + * * @param propertyName property name to match * @param propertyValue required property value * @param offset zero-based index of the first result diff --git a/manual/src/main/asciidoc/builtin-condition-types.adoc b/manual/src/main/asciidoc/builtin-condition-types.adoc index cce4a776c..b4e56a321 100644 --- a/manual/src/main/asciidoc/builtin-condition-types.adoc +++ b/manual/src/main/asciidoc/builtin-condition-types.adoc @@ -62,14 +62,14 @@ Some types use `parentCondition` to specialize a base type (for example `eventTy ==== Plugin registration (Unomi 3.0+) -New plugins use **Declarative Services** (`@Component`). Legacy Blueprint XML remains in some shipped bundles during migration; do not copy it for new code. +New plugins use **Declarative Services** (`@Component`). Legacy Blueprint XML remains in some shipped bundles during migration; do not copy it for new code. In particular, the built-in `BooleanConditionEvaluator`, `BooleanConditionESQueryBuilder`, and `BooleanConditionOSQueryBuilder` classes are themselves still registered via Blueprint XML today — the snippets below illustrate how a **new** condition type should be registered, not the current state of those specific built-in classes. Condition evaluator (see `plugins/advanced-conditions`): [source,java] ---- -@Component(service = ConditionEvaluator.class, property = {"conditionEvaluatorId=booleanConditionEvaluator"}) -public class BooleanConditionEvaluator implements ConditionEvaluator { +@Component(service = ConditionEvaluator.class, property = {"conditionEvaluatorId=myCustomConditionEvaluator"}) +public class MyCustomConditionEvaluator implements ConditionEvaluator { // ... } ---- @@ -78,8 +78,8 @@ Elasticsearch query builder (in your ES-specific bundle): [source,java] ---- -@Component(service = ConditionESQueryBuilder.class, property = {"queryBuilderId=booleanConditionQueryBuilder"}) -public class BooleanConditionESQueryBuilder implements ConditionESQueryBuilder { +@Component(service = ConditionESQueryBuilder.class, property = {"queryBuilderId=myCustomConditionQueryBuilder"}) +public class MyCustomConditionESQueryBuilder implements ConditionESQueryBuilder { // ... } ---- @@ -88,8 +88,8 @@ OpenSearch query builder (in your OS-specific bundle): [source,java] ---- -@Component(service = ConditionOSQueryBuilder.class, property = {"queryBuilderId=booleanConditionQueryBuilder"}) -public class BooleanConditionOSQueryBuilder implements ConditionOSQueryBuilder { +@Component(service = ConditionOSQueryBuilder.class, property = {"queryBuilderId=myCustomConditionQueryBuilder"}) +public class MyCustomConditionOSQueryBuilder implements ConditionOSQueryBuilder { // ... } ---- diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java index 6080be64d..28b534198 100644 --- a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java +++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java @@ -53,7 +53,8 @@ public interface PersistenceService { /** * Loads a paged slice of all items of the given type. *

- * Future API versions may replace these parameters with a { Query} object. * + * Future API versions may replace these parameters with a {@link Query} object. + * * @param the item type * @param clazz the {@link Item} subclass to load * @param offset zero-based index of the first result @@ -66,7 +67,8 @@ public interface PersistenceService { /** * Loads a paged slice of all items of the given type, optionally using a scroll query. *

- * Future API versions may replace these parameters with a { Query} object. * + * Future API versions may replace these parameters with a {@link Query} object. + * * @param the item type * @param clazz the {@link Item} subclass to load * @param offset zero-based index of the first result @@ -370,7 +372,8 @@ default CustomItem loadCustomItem(String itemId, String customItemType) { /** * Checks whether the specified item satisfies the provided condition. *

- * The method name may change in a future release. * + * The method name may change in a future release. + * * @param query the condition we're testing the specified item against * @param item the item we're checking against the specified condition * @return {@code true} if the item satisfies the condition, {@code false} otherwise @@ -731,7 +734,8 @@ default void refreshIndex(Class clazz) { /** * Creates an index with for the specified item type in the persistence engine. *

- * These low-level index operations may be removed from the public API in a future release. * + * These low-level index operations may be removed from the public API in a future release. + * * @param itemType the item type * @return {@code true} if the operation was successful, {@code false} otherwise */ @@ -740,7 +744,8 @@ default void refreshIndex(Class clazz) { /** * Removes the index for the specified item type. *

- * These low-level index operations may be removed from the public API in a future release. * + * These low-level index operations may be removed from the public API in a future release. + * * @param itemType the item type * @return {@code true} if the operation was successful, {@code false} otherwise */ diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java index 8c536b234..b03208e87 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java @@ -480,7 +480,8 @@ public boolean matchCondition(Condition condition, Profile profile, Session sess /** * Returns property types already in use for the given item type and tag. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * * @param tag the tag or system tag to match * @param isSystemTag whether {@code tag} is a system tag * @param itemType the item type name from the class {@code ITEM_TYPE} field @@ -508,7 +509,8 @@ public Collection getExistingProperties(@QueryParam("tag") String /** * Returns all known property types grouped by target. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * * @param language the requested locale for property descriptions (currently unused) * @return target name to property type mappings */ @@ -521,7 +523,8 @@ public Map> getPropertyTypes(@HeaderParam("Acce /** * Returns the property type for the given property ID. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * * @param propertyId the property identifier * @param language the requested locale for property descriptions (currently unused) * @return the property type @@ -535,7 +538,8 @@ public PropertyType getPropertyType(@PathParam("propertyId") String propertyId, /** * Returns property types for the given target. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * * @param target the property target name * @param language the requested locale for property descriptions (currently unused) * @return property types for the target @@ -549,7 +553,9 @@ public Collection getPropertyTypesByTarget(@PathParam("target") St /** * Returns property types that match any of the given tags. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * Tags are passed as a comma-separated path segment for backward compatibility. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * Tags are passed as a comma-separated path segment for backward compatibility. + * * @param tags comma-separated tag identifiers * @param language the requested locale for property descriptions (currently unused) * @return matching property types @@ -568,7 +574,9 @@ public Collection getPropertyTypeByTag(@PathParam("tags") String t /** * Returns property types that match any of the given system tags. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * Tags are passed as a comma-separated path segment for backward compatibility. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * Tags are passed as a comma-separated path segment for backward compatibility. + * * @param tags comma-separated system tag identifiers * @param language the requested locale for property descriptions (currently unused) * @return matching property types @@ -587,7 +595,8 @@ public Collection getPropertyTypeBySystemTag(@PathParam("tags") St /** * Persists the specified property type in the context server. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * * @param property the property type to persist * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed */ @@ -600,7 +609,8 @@ public boolean setPropertyType(PropertyType property) { /** * Persists the specified properties type in the context server. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * * @param properties the properties type to persist * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed */ @@ -617,7 +627,8 @@ public boolean setPropertyTypes(List properties) { /** * Deletes the property type identified by the specified identifier. * - * Property-type lookup helpers that may move to a dedicated endpoint in a future release. * + * Property-type lookup helpers that may move to a dedicated endpoint in a future release. + * * @param propertyId the identifier of the property type to delete * @return {@code true} if the property type was properly deleted, {@code false} otherwise */ diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java index a98f8afb4..a908d5373 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java @@ -172,7 +172,8 @@ public void removeRule(@PathParam("ruleId") String ruleId) { } /** - * Deprecated maintenance endpoint kept for backward compatibility. * + * Deprecated maintenance endpoint kept for backward compatibility. + * * @deprecated As of version 1.1.0-incubating, not needed anymore */ @Deprecated diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java index 91f145a52..8d78bf84b 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java @@ -55,7 +55,7 @@ public class ScoringServiceEndPoint { /** * Creates the scoring service endpoint. */ - public ScoringServiceEndPoint() { + public ScoringServiceEndPoint() { LOGGER.info("Initializing scoring service endpoint..."); } @@ -64,7 +64,7 @@ public ScoringServiceEndPoint() { * * @param segmentService the segment service */ - public void setSegmentService(SegmentService segmentService) { + public void setSegmentService(SegmentService segmentService) { this.segmentService = segmentService; } @@ -164,7 +164,8 @@ public DependentMetadata getScoringDependentMetadata(@PathParam("scoringID") Str } /** - * Deprecated maintenance endpoint kept for backward compatibility. * + * Deprecated maintenance endpoint kept for backward compatibility. + * * @deprecated As of version 1.1.0-incubating, not needed anymore */ @Deprecated diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java index a42460608..ab4529011 100644 --- a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java @@ -55,7 +55,7 @@ public class SegmentServiceEndPoint { /** * Creates the segment service endpoint. */ - public SegmentServiceEndPoint() { + public SegmentServiceEndPoint() { LOGGER.info("Initializing segment service endpoint..."); } @@ -64,7 +64,7 @@ public SegmentServiceEndPoint() { * * @param segmentService the segment service */ - public void setSegmentService(SegmentService segmentService) { + public void setSegmentService(SegmentService segmentService) { this.segmentService = segmentService; } @@ -188,7 +188,8 @@ public DependentMetadata removeSegmentDefinition(@PathParam("segmentID") String } /** - * Deprecated maintenance endpoint kept for backward compatibility. * + * Deprecated maintenance endpoint kept for backward compatibility. + * * @deprecated As of version 1.1.0-incubating, not needed anymore */ @Deprecated diff --git a/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java b/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java index 201a01fe9..6d40b745e 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java +++ b/services/src/main/java/org/apache/unomi/services/impl/tenants/TenantSecurityService.java @@ -21,8 +21,13 @@ import org.slf4j.LoggerFactory; /** - * Core tenant security service that handles tenant-specific security operations. + * Tenant security helper intended to handle tenant-specific security operations. * Rate limiting and IP filtering are handled by Apache CXF. + *

+ * This class is not currently registered as an OSGi service (no {@code @Component} + * annotation, no Blueprint bean) and is not instantiated anywhere in the runtime. + * Live API key validation for incoming requests is performed instead by + * {@code org.apache.unomi.rest.authentication.AuthenticationFilter}. */ public class TenantSecurityService { private static final Logger logger = LoggerFactory.getLogger(TenantSecurityService.class); @@ -39,7 +44,8 @@ public void setConfigAdmin(ConfigurationAdmin configAdmin) { } /** - * Blueprint activate hook; loads tenant security configuration. + * Activation hook that loads tenant security configuration. + * Not currently invoked by the OSGi container; see the class-level note. */ public void activate() { loadSecurityConfigurations(); @@ -63,8 +69,10 @@ public boolean validateRequest(String tenantId, String apiKey) { } private boolean validateApiKey(String tenantId, String apiKey) { - // Implementation of API key validation - return true; // Stub: full API key validation is handled by CXF tenant filters + // TODO: Implement actual validation. This method is currently unreachable since + // TenantSecurityService is not wired into the OSGi runtime (see class-level Javadoc); + // do not treat this stub as evidence that API key validation happens here. + return true; } private void loadSecurityConfigurations() { From 63dfe87e1b0dd8f69e9ab2bf026dbc6e8bdb267d Mon Sep 17 00:00:00 2001 From: Serge Huber Date: Sun, 12 Jul 2026 16:38:03 +0200 Subject: [PATCH 19/19] UNOMI-960: Fix broken @link references breaking javadoc doclint ProfileService#search(Query) doesn't exist (real signature takes a Class too), and PersistenceService's Query links weren't resolvable since the class isn't imported there. Both failed doclint's reference check. --- .../java/org/apache/unomi/api/services/ProfileService.java | 2 +- .../org/apache/unomi/persistence/spi/PersistenceService.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 3b6b8c94e..0c1041415 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 @@ -73,7 +73,7 @@ public interface ProfileService { /** * Finds profiles with a given property value, ordered and paged. * - * Prefer {@link #search(Query)} for new code; this overload remains for backward compatibility. + * Prefer {@link #search(Query, Class)} for new code; this overload remains for backward compatibility. * * @param propertyName property name to match * @param propertyValue required property value diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java index 28b534198..f9e457fc8 100644 --- a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java +++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java @@ -53,7 +53,7 @@ public interface PersistenceService { /** * Loads a paged slice of all items of the given type. *

- * Future API versions may replace these parameters with a {@link Query} object. + * Future API versions may replace these parameters with a {@link org.apache.unomi.api.query.Query} object. * * @param the item type * @param clazz the {@link Item} subclass to load @@ -67,7 +67,7 @@ public interface PersistenceService { /** * Loads a paged slice of all items of the given type, optionally using a scroll query. *

- * Future API versions may replace these parameters with a {@link Query} object. + * Future API versions may replace these parameters with a {@link org.apache.unomi.api.query.Query} object. * * @param the item type * @param clazz the {@link Item} subclass to load