From 97112b11c8b2523f6efcec269ee5d2e3eac463f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vanesa=20Smo=C4=BEakov=C3=A1?= Date: Thu, 9 Apr 2026 07:49:24 +0200 Subject: [PATCH] MID-11008 Use logical Clock in business/UI-facing timestamp paths --- .../component/DeadlinePanel.java | 7 +- .../helpers/CampaignProcessingHelper.java | 2 +- .../admin/simulation/SimulationsGuiUtil.java | 5 +- .../panel/SimulationResultPanel.java | 5 +- .../panel/SimulationResultsPanel.java | 3 +- .../dto/CertCampaignListItemDto.java | 2 +- .../dto/CertCaseOrWorkItemDto.java | 2 +- .../admin/server/TaskExecutionProgress.java | 3 +- .../midpoint/schema/util/MiscSchemaUtil.java | 4 +- .../schema/util/task/TaskTypeUtil.java | 20 +- .../actions/CompleteWorkItemsAction.java | 2 +- .../actions/DelegateWorkItemsAction.java | 2 +- .../impl/engine/helpers/WorkItemHelper.java | 5 +- .../impl/AccCertCaseOperationsHelper.java | 2 +- .../impl/AccCertCloserHelper.java | 2 +- .../impl/AccCertOpenerHelper.java | 2 +- ...AccessCertificationStageManagementRun.java | 2 +- .../certification/test/TestClockUsage.java | 327 ++++++++++++++++++ ...cation-of-eroot-user-assignments-clock.xml | 40 +++ .../certification-impl/testng-integration.xml | 1 + .../functions/BasicExpressionFunctions.java | 2 +- .../model/common/util/DefaultColumnUtils.java | 4 +- .../impl/mining/RoleAnalysisServiceImpl.java | 20 +- .../impl/mining/utils/RoleAnalysisUtils.java | 14 +- .../notifiers/SimpleCampaignNotifier.java | 7 +- .../SimpleCampaignStageNotifier.java | 6 +- .../SimpleCaseManagementNotifier.java | 10 +- .../notifiers/SimpleFocalObjectNotifier.java | 6 +- .../SimpleResourceObjectNotifier.java | 6 +- .../notifiers/SimpleReviewerNotifier.java | 4 +- .../impl/notifiers/SimpleTaskNotifier.java | 6 +- .../report/impl/ReportManagerImpl.java | 2 +- .../impl/controller/ColumnDataConverter.java | 6 +- .../wf/impl/processors/ModelHelper.java | 10 +- .../wf/impl/processors/StartInstruction.java | 16 +- .../primary/PcpStartInstruction.java | 17 +- .../primary/PrimaryChangeProcessor.java | 9 +- .../entitlements/AddAssociationAspect.java | 2 +- .../policy/AssignmentPolicyAspectPart.java | 2 +- .../policy/ObjectPolicyAspectPart.java | 2 +- .../impl/resources/ResourceUpdater.java | 12 +- release-notes.adoc | 1 + .../run/state/ActivityStatistics.java | 2 +- .../actions/ActionsExecutedCollectorImpl.java | 7 +- .../common/util/OperationExecutionWriter.java | 13 +- 45 files changed, 534 insertions(+), 90 deletions(-) create mode 100644 model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestClockUsage.java create mode 100644 model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments-clock.xml diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/component/DeadlinePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/component/DeadlinePanel.java index 42af0750843..b0e82bd5967 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/component/DeadlinePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/component/DeadlinePanel.java @@ -7,8 +7,11 @@ package com.evolveum.midpoint.gui.impl.page.admin.certification.component; import java.io.Serial; +import java.time.Instant; import java.time.LocalDate; +import java.time.ZoneId; import java.time.temporal.ChronoUnit; +import java.util.Date; import javax.xml.datatype.XMLGregorianCalendar; import org.apache.commons.lang3.StringUtils; @@ -153,7 +156,9 @@ private int getDaysToDeadline() { deadline.getYear(), deadline.getMonth(), deadline.getDay()); - LocalDate now = LocalDate.now(); + LocalDate now = Instant.ofEpochMilli(getPageBase().getClock().currentTimeMillis()) + .atZone(ZoneId.systemDefault()) + .toLocalDate(); return (int) ChronoUnit.DAYS.between(now, deadlineLocalDate); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/helpers/CampaignProcessingHelper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/helpers/CampaignProcessingHelper.java index de32e9c617f..d902e81c196 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/helpers/CampaignProcessingHelper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/certification/helpers/CampaignProcessingHelper.java @@ -239,7 +239,7 @@ public static String computeDeadlineAsString(AccessCertificationCampaignType cam if (end == null) { return ""; } else { - long delta = XmlTypeConverter.toMillis(end) - System.currentTimeMillis(); + long delta = XmlTypeConverter.toMillis(end) - page.getClock().currentTimeMillis(); // round to hours; we always round down long precision = 3600000L; // 1 hour diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/SimulationsGuiUtil.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/SimulationsGuiUtil.java index f0dee2cb1dd..8b96b1dd6fd 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/SimulationsGuiUtil.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/SimulationsGuiUtil.java @@ -352,7 +352,8 @@ public static ObjectProcessingStateType builtInMetricToProcessingState(BuiltInSi } } - public static @Nullable String createResultDurationText(@NotNull SimulationResultType result, Component panel) { + public static @Nullable String createResultDurationText( + @NotNull SimulationResultType result, Component panel, long now) { XMLGregorianCalendar start = result.getStartTimestamp(); if (start == null) { return panel.getString("SimulationResultsPanel.notStartedYet"); @@ -360,7 +361,7 @@ public static ObjectProcessingStateType builtInMetricToProcessingState(BuiltInSi XMLGregorianCalendar end = result.getEndTimestamp(); if (end == null) { - end = MiscUtil.asXMLGregorianCalendar(new Date()); + end = MiscUtil.asXMLGregorianCalendar(new Date(now)); } long duration = (end != null ? end.toGregorianCalendar().getTimeInMillis() : 0) - start.toGregorianCalendar().getTimeInMillis(); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultPanel.java index 5f9b2583f04..58f7ae42153 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultPanel.java @@ -146,7 +146,10 @@ protected List load() { list.add(new DetailsTableItem(createStringResource("PageSimulationResult.endTimestamp"), () -> LocalizationUtil.translateMessage(ValueDisplayUtil.toStringValue(new PrismPropertyValueImpl<>(getModelObject().getEndTimestamp()))))); list.add(new DetailsTableItem(createStringResource("PageSimulationResult.finishedIn"), - () -> createResultDurationText(getModelObject(), SimulationResultPanel.this))); + () -> createResultDurationText( + getModelObject(), + SimulationResultPanel.this, + getPageBase().getClock().currentTimeMillis()))); list.add(new DetailsTableItem(createStringResource("PageSimulationResult.rootTask"), null) { @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultsPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultsPanel.java index ab4ad37c549..6efe0ed7190 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultsPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/simulation/panel/SimulationResultsPanel.java @@ -273,7 +273,8 @@ private IModel createConfirmationMessage(ColumnMenuAction, String>> createDefaultColumns() { List, String>> columns = super.createDefaultColumns(); - columns.add(new LambdaColumn<>(createStringResource("ProcessedObjectsPanel.duration"), row -> createResultDurationText(row.getValue(), this))); + columns.add(new LambdaColumn<>(createStringResource("ProcessedObjectsPanel.duration"), + row -> createResultDurationText(row.getValue(), this, getPageBase().getClock().currentTimeMillis()))); columns.add(new AbstractColumn<>(createStringResource("ProcessedObjectsPanel.executionState")) { @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCampaignListItemDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCampaignListItemDto.java index 8fc91858e46..42cd88a5980 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCampaignListItemDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCampaignListItemDto.java @@ -102,7 +102,7 @@ private String computeDeadlineAsString(PageBase page) { if (end == null) { return ""; } else { - long delta = XmlTypeConverter.toMillis(end) - System.currentTimeMillis(); + long delta = XmlTypeConverter.toMillis(end) - page.getClock().currentTimeMillis(); // round to hours; we always round down long precision = 3600000L; // 1 hour diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCaseOrWorkItemDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCaseOrWorkItemDto.java index 4447718e0ef..ec0beb1f287 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCaseOrWorkItemDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertCaseOrWorkItemDto.java @@ -175,7 +175,7 @@ private String computeDeadlineAsString(PageBase page) { if (deadline == null) { return ""; } else { - long delta = XmlTypeConverter.toMillis(deadline) - System.currentTimeMillis(); + long delta = XmlTypeConverter.toMillis(deadline) - page.getClock().currentTimeMillis(); // round to hours; we always round down long precision = 3600000L; // 1 hour diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskExecutionProgress.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskExecutionProgress.java index baf1396cab6..78dc16f62d5 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskExecutionProgress.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskExecutionProgress.java @@ -194,7 +194,8 @@ private static String createExecutionStateMessage(TaskInformation info, PageBase case RUNNABLE: case RUNNING_OR_RUNNABLE: List localizationObjects = new ArrayList<>(); - String key = TaskTypeUtil.createScheduledToRunAgain(task, localizationObjects); + String key = TaskTypeUtil.createScheduledToRunAgain( + task, page.getClock().currentTimeMillis(), localizationObjects); return LocalizationUtil.translate(key, localizationObjects.toArray()); case WAITING: case SUSPENDED: diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MiscSchemaUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MiscSchemaUtil.java index 2e5d091aca6..19bc2508310 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MiscSchemaUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/MiscSchemaUtil.java @@ -85,10 +85,8 @@ public static ImportOptionsType getDefaultImportOptions() { return options; } - public static CachingMetadataType generateCachingMetadata() { + public static CachingMetadataType generateCachingMetadata(@NotNull XMLGregorianCalendar xmlGregorianCalendarNow) { CachingMetadataType cmd = new CachingMetadataType(); - XMLGregorianCalendar xmlGregorianCalendarNow = - XmlTypeConverter.createXMLGregorianCalendar(System.currentTimeMillis()); cmd.setRetrievalTimestamp(xmlGregorianCalendarNow); cmd.setSerialNumber(generateSerialNumber()); return cmd; diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/task/TaskTypeUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/task/TaskTypeUtil.java index d354f2a63ba..0f8e8e45d50 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/task/TaskTypeUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/task/TaskTypeUtil.java @@ -33,10 +33,10 @@ public class TaskTypeUtil { * Returns key for localization representing execution status of a task, additionally populates {@code localizationObject} * with additional parameters for the localization key. */ - public static String createScheduledToRunAgain(TaskType task, List localizationObject) { + public static String createScheduledToRunAgain(TaskType task, long now, List localizationObject) { boolean runnable = task.getSchedulingState() == TaskSchedulingStateType.READY; - Long scheduledAfter = getScheduledToStartAgain(task); - Long retryAfter = runnable ? getRetryAfter(task) : null; + Long scheduledAfter = getScheduledToStartAgain(task, now); + Long retryAfter = runnable ? getRetryAfter(task, now) : null; if (scheduledAfter == null) { if (retryAfter == null || retryAfter <= 0) { @@ -73,14 +73,12 @@ public static String createScheduledToRunAgain(TaskType task, List local return key; } - private static Long getRetryAfter(TaskType task) { + private static Long getRetryAfter(TaskType task, long now) { XMLGregorianCalendar nextRun = task.getNextRetryTimestamp(); - return nextRun != null ? (XmlTypeConverter.toMillis(nextRun) - System.currentTimeMillis()) : null; + return nextRun != null ? (XmlTypeConverter.toMillis(nextRun) - now) : null; } - public static Long getScheduledToStartAgain(TaskType task) { - long current = System.currentTimeMillis(); - + public static Long getScheduledToStartAgain(TaskType task, long now) { if (task.getNodeAsObserved() != null && task.getSchedulingState() != TaskSchedulingStateType.SUSPENDED) { if (!isTaskRecurring(task)) { return null; @@ -95,9 +93,9 @@ public static Long getScheduledToStartAgain(TaskType task) { return null; } - if (nextRunStartTimeLong > current + 1000) { - return nextRunStartTimeLong - System.currentTimeMillis(); - } else if (nextRunStartTimeLong < current - 60000) { + if (nextRunStartTimeLong > now + 1000) { + return nextRunStartTimeLong - now; + } else if (nextRunStartTimeLong < now - 60000) { return ALREADY_PASSED; } else { return NOW; diff --git a/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/CompleteWorkItemsAction.java b/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/CompleteWorkItemsAction.java index cd4b3bde094..18a03adcf5f 100644 --- a/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/CompleteWorkItemsAction.java +++ b/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/CompleteWorkItemsAction.java @@ -171,7 +171,7 @@ private void completeOrCancelWorkItem(@NotNull CaseWorkItemType workItem, @Nulla private void updateCaseHistory(@NotNull CaseWorkItemType workItem, @NotNull AbstractWorkItemOutputType output) { WorkItemId workItemId = WorkItemId.create(operation.getCaseOidRequired(), workItem.getId()); WorkItemCompletionEventType event = new WorkItemCompletionEventType(PrismContext.get()); - fillInWorkItemEvent(event, operation.getPrincipal(), workItemId, workItem); + fillInWorkItemEvent(event, operation.getPrincipal(), workItemId, workItem, beans.clock.currentTimeMillis()); event.setCause(request.getCauseInformation()); event.setOutput(output); operation.addCaseHistoryEvent(event); diff --git a/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/DelegateWorkItemsAction.java b/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/DelegateWorkItemsAction.java index 7ac5aa9e3c9..164229b56a2 100644 --- a/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/DelegateWorkItemsAction.java +++ b/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/actions/DelegateWorkItemsAction.java @@ -137,7 +137,7 @@ private void executeDelegation( WorkItemId workItemId = operation.createWorkItemId(workItem); - WorkItemHelper.fillInWorkItemEvent(event, operation.getPrincipal(), workItemId, workItem); + WorkItemHelper.fillInWorkItemEvent(event, operation.getPrincipal(), workItemId, workItem, beans.clock.currentTimeMillis()); operation.addCaseHistoryEvent(event); ApprovalStageDefinitionType level = operation.getCurrentStageDefinition(); diff --git a/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/helpers/WorkItemHelper.java b/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/helpers/WorkItemHelper.java index 241c74fd102..13868ef3a52 100644 --- a/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/helpers/WorkItemHelper.java +++ b/model/cases-impl/src/main/java/com/evolveum/midpoint/cases/impl/engine/helpers/WorkItemHelper.java @@ -27,12 +27,13 @@ public static void fillInWorkItemEvent( WorkItemEventType event, MidPointPrincipal currentUser, WorkItemId workItemId, - CaseWorkItemType workItem) { + CaseWorkItemType workItem, + long now) { if (currentUser != null) { event.setInitiatorRef(ObjectTypeUtil.createObjectRef(currentUser.getFocus())); event.setAttorneyRef(ObjectTypeUtil.createObjectRef(currentUser.getAttorney())); } - event.setTimestamp(XmlTypeConverter.createXMLGregorianCalendar(new Date())); + event.setTimestamp(XmlTypeConverter.createXMLGregorianCalendar(new Date(now))); event.setExternalWorkItemId(workItemId.asString()); event.setWorkItemId(workItemId.id); event.setOriginalAssigneeRef(workItem.getOriginalAssigneeRef()); diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java index 5fd893247a7..d30ee558be8 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java @@ -136,7 +136,7 @@ public void markCaseAsRemedied(@NotNull String campaignOid, long caseId, Task ta throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { PropertyDelta remediedDelta = prismContext.deltaFactory().property().createModificationReplaceProperty( ItemPath.create(F_CASE, caseId, AccessCertificationCaseType.F_REMEDIED_TIMESTAMP), - generalHelper.getCampaignObjectDefinition(), XmlTypeConverter.createXMLGregorianCalendar(new Date())); + generalHelper.getCampaignObjectDefinition(), clock.currentTimeXMLGregorianCalendar()); updateHelper.modifyObjectPreAuthorized(AccessCertificationCampaignType.class, campaignOid, Collections.singletonList(remediedDelta), task, parentResult); diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCloserHelper.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCloserHelper.java index ed08803fea3..b26e6fa1235 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCloserHelper.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCloserHelper.java @@ -257,7 +257,7 @@ private void cleanupCampaignsByDate(Duration maxAge, Task task, OperationResult if (maxAge.getSign() > 0) { maxAge = maxAge.negate(); } - Date deleteCampaignsFinishedUpTo = new Date(); + Date deleteCampaignsFinishedUpTo = new Date(clock.currentTimeMillis()); maxAge.addTo(deleteCampaignsFinishedUpTo); LOGGER.debug("Starting cleanup for closed certification campaigns deleting up to {} (max age '{}').", deleteCampaignsFinishedUpTo, maxAge); diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertOpenerHelper.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertOpenerHelper.java index a28b7b38b9f..7499f9f9d8f 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertOpenerHelper.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertOpenerHelper.java @@ -509,7 +509,7 @@ private List> createDeltasToRecordStageOpen(AccessCertificationCa for (Duration beforeDeadline : stageDef.getNotifyBeforeDeadline()) { final XMLGregorianCalendar beforeEnd = CloneUtil.clone(stageDeadline); beforeEnd.add(beforeDeadline.negate()); - if (XmlTypeConverter.toMillis(beforeEnd) > System.currentTimeMillis()) { + if (XmlTypeConverter.toMillis(beforeEnd) > clock.currentTimeMillis()) { final TriggerType triggerBeforeEnd = new TriggerType(); triggerBeforeEnd.setHandlerUri(AccessCertificationCloseStageApproachingTriggerHandler.HANDLER_URI); triggerBeforeEnd.setTimestamp(beforeEnd); diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/task/AccessCertificationStageManagementRun.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/task/AccessCertificationStageManagementRun.java index 0d127309eec..c5495f76228 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/task/AccessCertificationStageManagementRun.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/task/AccessCertificationStageManagementRun.java @@ -177,7 +177,7 @@ private List> createDeltasToRecordStageOpen(AccessCertificationCa for (Duration beforeDeadline : stageDef.getNotifyBeforeDeadline()) { final XMLGregorianCalendar beforeEnd = CloneUtil.clone(stageDeadline); beforeEnd.add(beforeDeadline.negate()); - if (XmlTypeConverter.toMillis(beforeEnd) > System.currentTimeMillis()) { + if (XmlTypeConverter.toMillis(beforeEnd) > getActivityHandler().getModelBeans().clock.currentTimeMillis()) { final TriggerType triggerBeforeEnd = new TriggerType(); triggerBeforeEnd.setHandlerUri(AccessCertificationCloseStageApproachingTriggerHandler.HANDLER_URI); triggerBeforeEnd.setTimestamp(beforeEnd); diff --git a/model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestClockUsage.java b/model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestClockUsage.java new file mode 100644 index 00000000000..ddd5d0b4c4e --- /dev/null +++ b/model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestClockUsage.java @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2026 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.certification.test; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertTrue; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.evolveum.midpoint.certification.impl.AccCertCaseOperationsHelper; +import com.evolveum.midpoint.model.test.CommonInitialObjects; +import com.evolveum.midpoint.notifications.api.NotificationManager; +import com.evolveum.midpoint.notifications.api.transports.Message; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationStageType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivityAffectedObjectsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.BasicObjectSetType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CertificationStartCampaignWorkDefinitionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskAffectedObjectsType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkDefinitionsType; + +@ContextConfiguration(locations = { "classpath:ctx-certification-test-main.xml" }) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class TestClockUsage extends AbstractUninitializedCertificationTest { + + private static final File CERT_DEF_USER_ASSIGNMENT_BASIC_FILE = + new File(COMMON_DIR, "certification-of-eroot-user-assignments.xml"); + private static final File CERT_DEF_USER_ASSIGNMENT_CLOCK_FILE = + new File(COMMON_DIR, "certification-of-eroot-user-assignments-clock.xml"); + + private static final File ORGS_AND_USERS_FILE = new File(COMMON_DIR, "orgs-and-users.xml"); + private static final File USER_BOB_FILE = new File(COMMON_DIR, "user-bob.xml"); + private static final File USER_JACK_FILE = new File(COMMON_DIR, "user-jack.xml"); + private static final File USER_ADMINISTRATOR_FILE = new File(COMMON_DIR, "user-administrator.xml"); + private static final File ROLE_REVIEWER_FILE = new File(COMMON_DIR, "role-reviewer.xml"); + private static final File ROLE_EROOT_USER_ASSIGNMENT_CAMPAIGN_OWNER_FILE = + new File(COMMON_DIR, "role-eroot-user-assignment-campaign-owner.xml"); + private static final File METAROLE_CXO_FILE = new File(COMMON_DIR, "metarole-cxo.xml"); + private static final File ROLE_CEO_FILE = new File(COMMON_DIR, "role-ceo.xml"); + private static final File ROLE_COO_FILE = new File(COMMON_DIR, "role-coo.xml"); + private static final File ROLE_CTO_FILE = new File(COMMON_DIR, "role-cto.xml"); + + private static final String USER_JACK_OID = "c0c010c0-d34d-b33f-f00d-111111111111"; + private static final String ROLE_CEO_OID = "00000000-d34d-b33f-f00d-000000000001"; + + @Autowired private AccCertCaseOperationsHelper operationsHelper; + @Autowired private NotificationManager notificationManager; + + private AccessCertificationDefinitionType certificationDefinition; + private AccessCertificationDefinitionType clockAwareCertificationDefinition; + + @Override + protected File getUserAdministratorFile() { + return USER_ADMINISTRATOR_FILE; + } + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + CommonInitialObjects.addCertificationTasks(this, initTask, initResult); + + repoAddObjectFromFile(METAROLE_CXO_FILE, RoleType.class, initResult); + repoAddObjectFromFile(ROLE_CEO_FILE, RoleType.class, initResult); + repoAddObjectFromFile(ROLE_COO_FILE, RoleType.class, initResult); + repoAddObjectFromFile(ROLE_CTO_FILE, RoleType.class, initResult); + repoAddObjectFromFile(ROLE_REVIEWER_FILE, RoleType.class, initResult); + repoAddObjectFromFile(ROLE_EROOT_USER_ASSIGNMENT_CAMPAIGN_OWNER_FILE, RoleType.class, initResult); + + repoAddObjectsFromFile(ORGS_AND_USERS_FILE, RoleType.class, initResult); + repoAddObjectFromFile(USER_BOB_FILE, UserType.class, initResult); + repoAddObjectFromFile(USER_JACK_FILE, UserType.class, initResult); + + certificationDefinition = repoAddObjectFromFile( + CERT_DEF_USER_ASSIGNMENT_BASIC_FILE, + AccessCertificationDefinitionType.class, + initResult).asObjectable(); + clockAwareCertificationDefinition = repoAddObjectFromFile( + CERT_DEF_USER_ASSIGNMENT_CLOCK_FILE, + AccessCertificationDefinitionType.class, + initResult).asObjectable(); + + notificationManager.setDisabled(false); + } + + @BeforeMethod + public void resetClock() { + clock.resetOverride(); + dummyTransport.clearMessages(); + } + + @AfterMethod + public void cleanupClock() { + clock.resetOverride(); + } + + @Test + public void test100CertificationCaseOperationsUseClockForRemediedTimestamp() throws Exception { + Task task = getTestTask(); + OperationResult result = task.getResult(); + login(userAdministrator.asPrismObject()); + + AccessCertificationCampaignType campaign = + certificationService.createCampaign(certificationDefinition.getOid(), task, result); + String campaignOid = campaign.getOid(); + + XMLGregorianCalendar startedAfter = clock.currentTimeXMLGregorianCalendar(); + certificationService.openNextStage(campaignOid, task, result); + result.computeStatus(); + TestUtil.assertInProgressOrSuccess(result); + + List> tasks = getFirstStageTasks(campaignOid, startedAfter, result); + assertEquals("Unexpected number of related tasks", 1, tasks.size()); + waitForTaskFinish(tasks.get(0).getOid()); + + AccessCertificationCaseType aCase = findCase( + queryHelper.searchCases(campaignOid, null, result), + USER_JACK_OID, + ROLE_CEO_OID); + + XMLGregorianCalendar overriddenNow = + XmlTypeConverter.createXMLGregorianCalendar("2026-04-09T10:15:30.000+02:00"); + clock.override(overriddenNow); + + operationsHelper.markCaseAsRemedied(campaignOid, aCase.getId(), task, result); + + AccessCertificationCaseType updatedCase = findCase( + queryHelper.searchCases(campaignOid, null, result), + USER_JACK_OID, + ROLE_CEO_OID); + + assertEquals( + "Remedied timestamp should follow Clock", + 0, + XmlTypeConverter.compareMillis(overriddenNow, updatedCase.getRemediedTimestamp())); + } + + @Test + public void test110CampaignStageOpenUsesClockForTimestampsAndTriggers() throws Exception { + Task task = getTestTask(); + OperationResult result = task.getResult(); + login(userAdministrator.asPrismObject()); + + XMLGregorianCalendar overriddenNow = + XmlTypeConverter.createXMLGregorianCalendar("2000-01-01T10:15:30.000+01:00"); + clock.override(overriddenNow); + + AccessCertificationCampaignType campaign = + certificationService.createCampaign(clockAwareCertificationDefinition.getOid(), task, result); + String campaignOid = campaign.getOid(); + + XMLGregorianCalendar startedAfter = clock.currentTimeXMLGregorianCalendar(); + certificationService.openNextStage(campaignOid, task, result); + result.computeStatus(); + TestUtil.assertInProgressOrSuccess(result); + + List> tasks = getFirstStageTasks(campaignOid, startedAfter, result); + assertEquals("Unexpected number of related tasks", 1, tasks.size()); + waitForTaskFinish(tasks.get(0).getOid()); + + AccessCertificationCampaignType openedCampaign = getObject(AccessCertificationCampaignType.class, campaignOid).asObjectable(); + AccessCertificationStageType stage = openedCampaign.getStage().get(0); + + assertEquals( + "Campaign start timestamp should follow Clock", + 0, + XmlTypeConverter.compareMillis(overriddenNow, openedCampaign.getStartTimestamp())); + assertEquals( + "Stage start timestamp should follow Clock", + 0, + XmlTypeConverter.compareMillis(overriddenNow, stage.getStartTimestamp())); + + assertNotNull("Campaign triggers should be present", openedCampaign.getTrigger()); + assertEquals("Wrong number of campaign triggers", 2, openedCampaign.getTrigger().size()); + assertTrue( + "Expected notify-before-deadline trigger to be created from logical time", + openedCampaign.getTrigger().stream() + .map(TriggerType::getHandlerUri) + .anyMatch(uri -> uri != null && uri.contains("close-stage-approaching"))); + } + + @Test + public void test120ReviewerNotificationUsesClockForRemainingTime() throws Exception { + Task task = getTestTask(); + OperationResult result = task.getResult(); + login(userAdministrator.asPrismObject()); + + XMLGregorianCalendar overriddenNow = + XmlTypeConverter.createXMLGregorianCalendar("2000-01-01T10:15:30.000+01:00"); + clock.override(overriddenNow); + + AccessCertificationCampaignType campaign = + certificationService.createCampaign(certificationDefinition.getOid(), task, result); + String campaignOid = campaign.getOid(); + + XMLGregorianCalendar startedAfter = clock.currentTimeXMLGregorianCalendar(); + certificationService.openNextStage(campaignOid, task, result); + result.computeStatus(); + TestUtil.assertInProgressOrSuccess(result); + + List> tasks = getFirstStageTasks(campaignOid, startedAfter, result); + assertEquals("Unexpected number of related tasks", 1, tasks.size()); + waitForTaskFinish(tasks.get(0).getOid()); + + List messages = dummyTransport.getMessages("dummy:simpleReviewerNotifier"); + assertTrue("Expected reviewer notifications to be sent", !messages.isEmpty()); + assertTrue( + "Reviewer notification should compute remaining time from Clock", + messages.stream() + .map(Message::getBody) + .filter(body -> body != null && body.contains("The stage ends in ")) + .anyMatch(body -> body.contains("14 day"))); + assertTrue( + "Reviewer notification should not treat the stage as already expired", + messages.stream() + .map(Message::getBody) + .filter(body -> body != null) + .noneMatch(body -> body.contains("The stage should have ended"))); + } + + private AccessCertificationCaseType findCase( + List caseList, String subjectOid, String targetOid) { + return caseList.stream() + .filter(aCase -> aCase.getTargetRef() != null) + .filter(aCase -> targetOid.equals(aCase.getTargetRef().getOid())) + .filter(aCase -> aCase.getObjectRef() != null) + .filter(aCase -> subjectOid.equals(aCase.getObjectRef().getOid())) + .findAny() + .orElse(null); + } + + private List> getFirstStageTasks( + String campaignOid, XMLGregorianCalendar startTime, OperationResult result) throws Exception { + if (isNativeRepository()) { + ObjectQuery query = prismContext.queryFor(TaskType.class) + .item(TaskType.F_ARCHETYPE_REF).ref(SystemObjectsType.ARCHETYPE_CERTIFICATION_START_CAMPAIGN_TASK.value()) + .and() + .item( + ItemPath.create( + TaskType.F_AFFECTED_OBJECTS, + TaskAffectedObjectsType.F_ACTIVITY, + ActivityAffectedObjectsType.F_OBJECTS, + BasicObjectSetType.F_OBJECT_REF)) + .ref(campaignOid) + .and() + .item( + ItemPath.create( + TaskType.F_AFFECTED_OBJECTS, + TaskAffectedObjectsType.F_ACTIVITY, + ActivityAffectedObjectsType.F_OBJECTS, + BasicObjectSetType.F_TYPE)) + .eq(AccessCertificationCampaignType.COMPLEX_TYPE) + .and() + .block() + .item(TaskType.F_LAST_RUN_START_TIMESTAMP).isNull() + .or() + .item(TaskType.F_LAST_RUN_START_TIMESTAMP).ge(startTime) + .endBlock() + .build(); + return taskManager.searchObjects(TaskType.class, query, null, result); + } + + ObjectQuery query = prismContext.queryFor(TaskType.class) + .item(TaskType.F_ARCHETYPE_REF).ref(SystemObjectsType.ARCHETYPE_CERTIFICATION_START_CAMPAIGN_TASK.value()) + .and() + .block() + .item(TaskType.F_LAST_RUN_START_TIMESTAMP).ge(startTime) + .endBlock() + .build(); + + List> tasks = taskManager.searchObjects(TaskType.class, query, null, result); + List> matching = new ArrayList<>(); + for (PrismObject task : tasks) { + if (campaignOidMatch(task, campaignOid)) { + matching.add(task); + } + } + return matching; + } + + private boolean campaignOidMatch(PrismObject task, String campaignOid) { + var activity = task.asObjectable().getActivity(); + if (activity == null) { + return false; + } + WorkDefinitionsType work = activity.getWork(); + if (work == null) { + return false; + } + CertificationStartCampaignWorkDefinitionType startCampaign = work.getCertificationStartCampaign(); + if (startCampaign == null) { + return false; + } + ObjectReferenceType campaign = startCampaign.getCertificationCampaignRef(); + return campaign != null && campaignOid.equals(campaign.getOid()); + } +} diff --git a/model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments-clock.xml b/model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments-clock.xml new file mode 100644 index 00000000000..9b39af308c2 --- /dev/null +++ b/model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments-clock.xml @@ -0,0 +1,40 @@ + + + + + Clock Usage Certification (ERoot only) + Certification definition for clock usage regression tests. + http://midpoint.evolveum.com/xml/ns/public/certification/handlers-3#direct-assignment + + UserType + + + + 00000000-8888-6666-0000-300000000000 + SUBTREE + + + + default + + + + + + + 1 + P14D + P13D + + + + + diff --git a/model/certification-impl/testng-integration.xml b/model/certification-impl/testng-integration.xml index 478d0f0ca22..0ff757363da 100644 --- a/model/certification-impl/testng-integration.xml +++ b/model/certification-impl/testng-integration.xml @@ -14,6 +14,7 @@ + diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java index 7972c89f6f9..a3dea054b63 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java @@ -889,7 +889,7 @@ public XMLGregorianCalendar currentDateTime() { } public XMLGregorianCalendar fromNow(String timeSpec) { - return XmlTypeConverter.fromNow(timeSpec); + return XmlTypeConverter.fromNow(clock.currentTimeMillis(), timeSpec); } public XMLGregorianCalendar addDuration(XMLGregorianCalendar now, Duration duration) { diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java index 429c9e1bb27..ef24c4bbe95 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/DefaultColumnUtils.java @@ -278,7 +278,7 @@ private static void createColumns(GuiObjectListViewTyp } public static String processSpecialColumn( - ItemPath itemPath, Object object, LocalizationService localization) { + ItemPath itemPath, Object object, long now, LocalizationService localization) { if (itemPath == null) { return null; } @@ -303,7 +303,7 @@ public static String processSpecialColumn( return ""; } else if (itemPath.equivalent(TaskType.F_SCHEDULE)) { List localizationObject = new ArrayList<>(); - String key = TaskTypeUtil.createScheduledToRunAgain(task, localizationObject); + String key = TaskTypeUtil.createScheduledToRunAgain(task, now, localizationObject); Object[] params = localizationObject.isEmpty() ? null : localizationObject.toArray(); return localization.translate(key, params, Locale.getDefault(), key); } else if (itemPath.equivalent(TaskType.F_PROGRESS)) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/RoleAnalysisServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/RoleAnalysisServiceImpl.java index 052f8db1b74..26f85253c4a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/RoleAnalysisServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/RoleAnalysisServiceImpl.java @@ -35,6 +35,7 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.common.outlier.OutlierExplanationResolver; +import com.evolveum.midpoint.common.Clock; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; @@ -106,6 +107,7 @@ public class RoleAnalysisServiceImpl implements RoleAnalysisService { transient @Autowired ModelService modelService; transient @Autowired RepositoryService repositoryService; transient @Autowired SchemaService schemaService; + transient @Autowired Clock clock; //poc transient @Autowired RelationRegistry relationRegistry; @@ -1246,7 +1248,9 @@ public void executeClusteringTask( submitSessionOperationStatus(modelService, session, taskOid, - focus, LOGGER, task, result + focus, + clock.currentTimeXMLGregorianCalendar(), + LOGGER, task, result ); } catch (CommonException e) { @@ -1309,6 +1313,7 @@ public void executeDetectionTask( taskOid, RoleAnalysisOperationType.DETECTION, focus, + clock.currentTimeXMLGregorianCalendar(), LOGGER, task, result @@ -1367,7 +1372,10 @@ public void executeRoleAnalysisRoleMigrationTask( submitClusterOperationStatus(modelService, cluster, taskOid, - RoleAnalysisOperationType.MIGRATION, focus, LOGGER, task, result + RoleAnalysisOperationType.MIGRATION, + focus, + clock.currentTimeXMLGregorianCalendar(), + LOGGER, task, result ); switchRoleToActiveLifeState(modelService, roleObject, LOGGER, task, result); @@ -1436,7 +1444,7 @@ public void executeRoleMigrationProcess( Collection> collection = new ArrayList<>(); RoleAnalysisOperationStatusType newStatus = updateRoleAnalysisOperationStatus( - repositoryService, roleAnalysisOperationStatus, false, LOGGER, result + repositoryService, roleAnalysisOperationStatus, clock.currentTimeXMLGregorianCalendar(), false, LOGGER, result ); if (newStatus != null) { @@ -1515,7 +1523,8 @@ public void executeRoleMigrationProcess( return RoleAnalysisObjectState.STABLE.getDisplayString(); } - RoleAnalysisOperationStatusType newStatus = updateRoleAnalysisOperationStatus(repositoryService, operationStatus, true, LOGGER, result); + RoleAnalysisOperationStatusType newStatus = updateRoleAnalysisOperationStatus( + repositoryService, operationStatus, clock.currentTimeXMLGregorianCalendar(), true, LOGGER, result); if (newStatus != null) { @@ -1667,7 +1676,8 @@ public void setCandidateRoleOpStatus( } @NotNull RoleAnalysisOperationStatusType operationStatus = buildOpExecution( - taskOid, operationResultStatusType, message, operationType, createTimestamp, focus); + taskOid, operationResultStatusType, message, operationType, createTimestamp, + clock.currentTimeXMLGregorianCalendar(), focus); try { ObjectDelta delta = PrismContext.get().deltaFor(RoleAnalysisClusterType.class) .item(RoleAnalysisClusterType.F_CANDIDATE_ROLES.append( diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/utils/RoleAnalysisUtils.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/utils/RoleAnalysisUtils.java index f092ee85389..4f20841629a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/utils/RoleAnalysisUtils.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/mining/utils/RoleAnalysisUtils.java @@ -11,7 +11,6 @@ import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; @@ -38,6 +37,7 @@ public class RoleAnalysisUtils { public static @Nullable RoleAnalysisOperationStatusType updateRoleAnalysisOperationStatus( @NotNull RepositoryService repositoryService, @NotNull RoleAnalysisOperationStatusType status, + @NotNull XMLGregorianCalendar now, boolean isSession, @NotNull Trace logger, @NotNull OperationResult result) { @@ -76,7 +76,7 @@ public class RoleAnalysisUtils { } else { status.setMessage(updateClusterStateMessage(taskType)); } - status.setModifyTimestamp(XmlTypeConverter.createXMLGregorianCalendar(new Date())); + status.setModifyTimestamp(now); status.setStatus(taskType.getResultStatus()); return status; @@ -133,12 +133,12 @@ public static RoleAnalysisOperationStatusType buildOpExecution( String message, RoleAnalysisOperationType operationType, XMLGregorianCalendar createTimestamp, + @NotNull XMLGregorianCalendar now, @Nullable FocusType owner) { RoleAnalysisOperationStatusType operationExecutionType = new RoleAnalysisOperationStatusType(); - XMLGregorianCalendar xmlGregorianCalendar = XmlTypeConverter.createXMLGregorianCalendar(new Date()); if (createTimestamp == null) { - createTimestamp = xmlGregorianCalendar; + createTimestamp = now; } if (owner != null) { @@ -149,7 +149,7 @@ public static RoleAnalysisOperationStatusType buildOpExecution( } operationExecutionType.createTimestamp(createTimestamp); - operationExecutionType.modifyTimestamp(xmlGregorianCalendar); + operationExecutionType.modifyTimestamp(now); operationExecutionType.setStatus(operationResultStatusType); operationExecutionType.setOperationChannel(operationType); operationExecutionType.setTaskRef( @@ -168,6 +168,7 @@ public static void submitClusterOperationStatus( @NotNull String taskOid, @NotNull RoleAnalysisOperationType operationChannel, @NotNull FocusType initiator, + @NotNull XMLGregorianCalendar now, Trace logger, @NotNull Task task, @NotNull OperationResult result) { @@ -178,6 +179,7 @@ public static void submitClusterOperationStatus( null, operationChannel, null, + now, initiator); try { @@ -200,6 +202,7 @@ public static void submitSessionOperationStatus( @NotNull PrismObject cluster, @NotNull String taskOid, @NotNull FocusType initiator, + @NotNull XMLGregorianCalendar now, @NotNull Trace logger, @NotNull Task task, @NotNull OperationResult result) { @@ -210,6 +213,7 @@ public static void submitSessionOperationStatus( null, RoleAnalysisOperationType.CLUSTERING, null, + now, initiator); try { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java index 07630104843..63182401937 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignNotifier.java @@ -8,6 +8,7 @@ import java.util.Date; +import com.evolveum.midpoint.common.Clock; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -30,8 +31,8 @@ public class SimpleCampaignNotifier extends AbstractGeneralNotifier getEventType() { @@ -92,7 +93,7 @@ protected String getBody( } body.append(".\n\n"); - body.append("Time: ").append(new Date()); // the event is generated in the real time + body.append("Time: ").append(new Date(clock.currentTimeMillis())); // the event is generated in the real time body.append("\nRequester: ").append(formatRequester(event, result)); body.append("\nOperation status: ").append(certHelper.formatStatus(event)); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java index 4ebcbb721e3..5f0fc54bd8f 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCampaignStageNotifier.java @@ -6,6 +6,7 @@ package com.evolveum.midpoint.notifications.impl.notifiers; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.notifications.api.EventProcessingContext; import com.evolveum.midpoint.notifications.api.events.CertCampaignStageEvent; import com.evolveum.midpoint.notifications.impl.helpers.CertHelper; @@ -33,6 +34,7 @@ public class SimpleCampaignStageNotifier extends AbstractGeneralNotifier 0) { body.append("\n\nStage ends in "); body.append(DurationFormatUtils.formatDurationWords(delta, true, true)); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCaseManagementNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCaseManagementNotifier.java index b603d3ecc6a..7ddb9c4cf6e 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCaseManagementNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleCaseManagementNotifier.java @@ -6,6 +6,7 @@ package com.evolveum.midpoint.notifications.impl.notifiers; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.notifications.api.EventProcessingContext; import com.evolveum.midpoint.notifications.api.events.CaseEvent; import com.evolveum.midpoint.notifications.api.events.CaseManagementEvent; @@ -26,6 +27,7 @@ import org.apache.commons.lang3.time.DurationFormatUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.xml.datatype.XMLGregorianCalendar; @@ -42,6 +44,8 @@ public class SimpleCaseManagementNotifier extends AbstractGeneralNotifier getEventType() { return CaseManagementEvent.class; @@ -126,7 +130,7 @@ private String getSubjectFromWorkItemEvent(WorkItemEventImpl event) { String rv = "Work item will be automatically " + getOperationPastTenseVerb(event.getOperationKind()); if (event.getTimeBefore() != null) { // should always be rv += " in " + DurationFormatUtils.formatDurationWords( - event.getTimeBefore().getTimeInMillis(new Date()), true, true); + event.getTimeBefore().getTimeInMillis(new Date(clock.currentTimeMillis())), true, true); } return rv; } else { @@ -164,7 +168,7 @@ protected String getBody( } else { appendResultInformation(body, event, true); } - body.append("\nNotification created on: ").append(new Date()).append("\n\n"); + body.append("\nNotification created on: ").append(new Date(clock.currentTimeMillis())).append("\n\n"); if (techInfo) { body.append("----------------------------------------\n"); @@ -225,7 +229,7 @@ private void appendDeadlineInformation(StringBuilder sb, WorkItemEventImpl event private void appendDeadlineInformation(StringBuilder sb, AbstractWorkItemType workItem) { XMLGregorianCalendar deadline = workItem.getDeadline(); - long before = XmlTypeConverter.toMillis(deadline) - System.currentTimeMillis(); + long before = XmlTypeConverter.toMillis(deadline) - clock.currentTimeMillis(); long beforeRounded = Math.round((double) before / 60000.0) * 60000L; String beforeWords = DurationFormatUtils.formatDurationWords(Math.abs(beforeRounded), true, true); String beforePhrase; diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java index bf5fa92ddad..0e9f730b2dd 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleFocalObjectNotifier.java @@ -10,8 +10,10 @@ import java.util.Date; +import com.evolveum.midpoint.common.Clock; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.evolveum.midpoint.model.api.context.ModelContext; @@ -41,6 +43,8 @@ public class SimpleFocalObjectNotifier extends AbstractGeneralNotifier getEventType() { return ModelEvent.class; @@ -134,7 +138,7 @@ protected String getBody(ConfigurationItem getEventType() { return ResourceObjectEvent.class; @@ -106,7 +110,7 @@ protected String getBody( body.append(userOrOwner).append(": unknown\n"); } } - body.append("Notification created on: ").append(new Date()).append("\n\n"); + body.append("Notification created on: ").append(new Date(clock.currentTimeMillis())).append("\n\n"); body.append("Resource: ").append(event.getResourceName()).append(" (oid ").append(event.getResourceOid()).append(")\n"); boolean named; if (rod.getCurrentShadow() != null && rod.getCurrentShadow().asObjectable().getName() != null) { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleReviewerNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleReviewerNotifier.java index be36847776f..ca0fa23fe8b 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleReviewerNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleReviewerNotifier.java @@ -6,6 +6,7 @@ package com.evolveum.midpoint.notifications.impl.notifiers; +import com.evolveum.midpoint.common.Clock; import org.apache.commons.lang3.time.DurationFormatUtils; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +35,7 @@ public class SimpleReviewerNotifier extends AbstractGeneralNotifier 0) { if (event.isModify()) { body.append("\n\nThis is to notify you that the stage ends in "); diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleTaskNotifier.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleTaskNotifier.java index 5ebf83ca91b..81434e8d51c 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleTaskNotifier.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/notifiers/SimpleTaskNotifier.java @@ -8,6 +8,7 @@ import com.evolveum.midpoint.notifications.api.EventProcessingContext; import com.evolveum.midpoint.notifications.api.events.TaskEvent; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.schema.config.ConfigurationItem; @@ -19,6 +20,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Date; @@ -28,6 +30,8 @@ public class SimpleTaskNotifier extends AbstractGeneralNotifier getEventType() { return TaskEvent.class; @@ -80,7 +84,7 @@ protected String getBody( } body.append("Progress: ").append(event.getProgress()).append("\n"); body.append("\n"); - body.append("Notification created on: ").append(new Date()).append("\n\n"); + body.append("Notification created on: ").append(new Date(clock.currentTimeMillis())).append("\n\n"); PrismObject taskOwner = task.getOwner(result); if (taskOwner != null) { diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportManagerImpl.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportManagerImpl.java index a0f1a8100c7..b54ef92534a 100644 --- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportManagerImpl.java +++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportManagerImpl.java @@ -164,7 +164,7 @@ public void cleanupReports(CleanupPolicyType cleanupPolicy, RunningTask task, Op if (duration.getSign() > 0) { duration = duration.negate(); } - Date deleteReportOutputsTo = new Date(); + Date deleteReportOutputsTo = new Date(reportService.getClock().currentTimeMillis()); duration.addTo(deleteReportOutputsTo); LOGGER.info("Starting cleanup for report outputs deleting up to {} (duration '{}').", diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/ColumnDataConverter.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/ColumnDataConverter.java index 0c002f41d09..4ef40521ad2 100644 --- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/ColumnDataConverter.java +++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/controller/ColumnDataConverter.java @@ -134,7 +134,11 @@ List convertColumn(@NotNull GuiObjectColumnType column) { } if (DefaultColumnUtils.isSpecialColumn(itemPath, record)) { return MiscUtil.singletonOrEmptyList( - DefaultColumnUtils.processSpecialColumn(itemPath, record, reportService.getLocalizationService())); + DefaultColumnUtils.processSpecialColumn( + itemPath, + record, + reportService.getClock().currentTimeMillis(), + reportService.getLocalizationService())); } return prettyPrintValues(dataValues); } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ModelHelper.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ModelHelper.java index 49f5d785ac9..786fad6f1ff 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ModelHelper.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/ModelHelper.java @@ -8,12 +8,15 @@ import static com.evolveum.midpoint.prism.PrismObject.asPrismObject; +import java.time.Instant; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.List; import java.util.Locale; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; import org.jetbrains.annotations.NotNull; @@ -48,6 +51,7 @@ public class ModelHelper { @Autowired private LocalizationService localizationService; @Autowired private PrismContext prismContext; @Autowired private MiscHelper miscHelper; + @Autowired private Clock clock; @Autowired @Qualifier("cacheRepositoryService") private RepositoryService repositoryService; private static final String APPROVING_AND_EXECUTING_KEY = "ApprovingAndExecuting."; @@ -68,7 +72,7 @@ public StartInstruction createInstructionForRoot( ModelContext contextForRootCase, OperationResult result) throws SchemaException { StartInstruction instruction = - StartInstruction.create(changeProcessor, SystemObjectsType.ARCHETYPE_OPERATION_REQUEST.value()); + StartInstruction.create(changeProcessor, SystemObjectsType.ARCHETYPE_OPERATION_REQUEST.value(), clock); instruction.setModelContext(contextForRootCase); LocalizableMessage rootCaseName = determineRootCaseName(ctx); @@ -107,7 +111,9 @@ private LocalizableMessage determineRootCaseName(ModelInvocationContext ctx) ObjectType focus = ctx.getFocusObjectNewOrOld(); String time = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) .withLocale(Locale.getDefault()) - .format(ZonedDateTime.now()); + .format(ZonedDateTime.ofInstant( + Instant.ofEpochMilli(clock.currentTimeMillis()), + ZoneId.systemDefault())); return new LocalizableMessageBuilder() .key(APPROVING_AND_EXECUTING_KEY + operationKey) diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/StartInstruction.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/StartInstruction.java index 76e310f68b3..2f16722ea29 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/StartInstruction.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/StartInstruction.java @@ -6,6 +6,7 @@ package com.evolveum.midpoint.wf.impl.processors; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.model.api.context.ModelContext; import com.evolveum.midpoint.model.api.context.ModelState; import com.evolveum.midpoint.model.impl.lens.LensContext; @@ -31,7 +32,6 @@ import java.util.*; -import static com.evolveum.midpoint.prism.xml.XmlTypeConverter.createXMLGregorianCalendar; import static com.evolveum.midpoint.schema.util.ObjectTypeUtil.createObjectRef; /** @@ -46,20 +46,22 @@ public class StartInstruction implements DebugDumpable { protected final CaseType aCase; private final ChangeProcessor changeProcessor; + private final Clock clock; //region Constructors - protected StartInstruction(@NotNull ChangeProcessor changeProcessor, @NotNull String archetypeOid) { + protected StartInstruction(@NotNull ChangeProcessor changeProcessor, @NotNull String archetypeOid, @NotNull Clock clock) { this.changeProcessor = changeProcessor; + this.clock = clock; aCase = new CaseType(); ObjectReferenceType approvalArchetypeRef = ObjectTypeUtil.createObjectRef(archetypeOid, ObjectTypes.ARCHETYPE); aCase.getArchetypeRef().add(approvalArchetypeRef.clone()); aCase.beginAssignment().targetRef(approvalArchetypeRef).end(); ValueMetadataTypeUtil.getOrCreateStorageMetadata(aCase) - .setCreateTimestamp(createXMLGregorianCalendar()); + .setCreateTimestamp(clock.currentTimeXMLGregorianCalendar()); } - public static StartInstruction create(ChangeProcessor changeProcessor, @NotNull String archetypeOid) { - return new StartInstruction(changeProcessor, archetypeOid); + public static StartInstruction create(ChangeProcessor changeProcessor, @NotNull String archetypeOid, @NotNull Clock clock) { + return new StartInstruction(changeProcessor, archetypeOid, clock); } //endregion @@ -68,6 +70,10 @@ private ChangeProcessor getChangeProcessor() { return changeProcessor; } + protected Clock getClock() { + return clock; + } + public void setName(String name) { aCase.setName(PolyStringType.fromOrig(name)); } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PcpStartInstruction.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PcpStartInstruction.java index 7e1f8e3f5f6..64472af699c 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PcpStartInstruction.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PcpStartInstruction.java @@ -6,10 +6,9 @@ package com.evolveum.midpoint.wf.impl.processors.primary; -import java.util.Date; - import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.model.api.ModelExecuteOptions; import com.evolveum.midpoint.model.api.ObjectTreeDeltas; import com.evolveum.midpoint.model.api.context.ModelContext; @@ -41,22 +40,22 @@ public class PcpStartInstruction extends StartInstruction { private ObjectTreeDeltas deltasToApprove; - private PcpStartInstruction(@NotNull ChangeProcessor changeProcessor, @NotNull String archetypeOid) { - super(changeProcessor, archetypeOid); + private PcpStartInstruction(@NotNull ChangeProcessor changeProcessor, @NotNull String archetypeOid, @NotNull Clock clock) { + super(changeProcessor, archetypeOid, clock); aCase.setApprovalContext(new ApprovalContextType()); } public static PcpStartInstruction createItemApprovalInstruction(ChangeProcessor changeProcessor, - @NotNull ApprovalSchemaType approvalSchemaType, SchemaAttachedPolicyRulesType attachedPolicyRules) { + @NotNull ApprovalSchemaType approvalSchemaType, SchemaAttachedPolicyRulesType attachedPolicyRules, @NotNull Clock clock) { PcpStartInstruction instruction = new PcpStartInstruction(changeProcessor, - SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value()); + SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value(), clock); instruction.getApprovalContext().setApprovalSchema(approvalSchemaType); instruction.getApprovalContext().setPolicyRules(attachedPolicyRules); return instruction; } - static PcpStartInstruction createEmpty(ChangeProcessor changeProcessor, @NotNull String archetypeOid) { - return new PcpStartInstruction(changeProcessor, archetypeOid); + static PcpStartInstruction createEmpty(ChangeProcessor changeProcessor, @NotNull String archetypeOid, @NotNull Clock clock) { + return new PcpStartInstruction(changeProcessor, archetypeOid, clock); } public boolean isExecuteApprovedChangeImmediately() { @@ -80,7 +79,7 @@ public void prepareCommonAttributes( getApprovalContext().setChangeAspect(aspect.getClass().getName()); CaseCreationEventType event = new CaseCreationEventType(); - event.setTimestamp(XmlTypeConverter.createXMLGregorianCalendar(new Date())); + event.setTimestamp(getClock().currentTimeXMLGregorianCalendar()); if (requester != null) { event.setInitiatorRef(ObjectTypeUtil.createObjectRef(requester)); // attorney does not need to be set here (for now) diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java index b0bd875af16..6e7fcf3e947 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java @@ -18,6 +18,7 @@ import com.google.common.base.Preconditions; import jakarta.annotation.PostConstruct; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.wf.impl.processors.primary.cases.CaseClosing; import com.evolveum.midpoint.wf.impl.util.MiscHelper; @@ -75,6 +76,7 @@ public class PrimaryChangeProcessor implements ChangeProcessor { @Autowired protected MiscHelper miscHelper; @Autowired private ApprovalBeans beans; + @Autowired private Clock clock; @Autowired private CaseEngineImpl caseEngine; @@ -368,7 +370,8 @@ private CaseType addRoot(ModelInvocationContext ctx, OperationResult result) private PcpStartInstruction createInstruction0(ModelInvocationContext ctx, ObjectTreeDeltas changesWithoutApproval, CaseType rootCase) throws SchemaException { if (changesWithoutApproval != null && !changesWithoutApproval.isEmpty()) { - PcpStartInstruction instruction0 = PcpStartInstruction.createEmpty(this, SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value()); + PcpStartInstruction instruction0 = PcpStartInstruction.createEmpty( + this, SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value(), clock); instruction0.setName("Changes that do not require approval"); instruction0.setObjectRef(ctx); instruction0.setDeltasToApprove(changesWithoutApproval); @@ -398,6 +401,10 @@ private LensContext contextCopyWithNoDelta(ModelContext context) { } //endregion + public Clock getClock() { + return clock; + } + //region Processing process finish event /** * This method is called OUTSIDE the workflow engine computation - i.e. changes are already committed into repository. diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java index 5fea1a5ab6e..d506d4f805d 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java @@ -272,7 +272,7 @@ private List prepareJobCreateInstructions(ModelInvocationCo PcpStartInstruction instruction = PcpStartInstruction.createItemApprovalInstruction( getChangeProcessor(), - approvalRequest.schema, null); + approvalRequest.schema, null, getChangeProcessor().getClock()); // set some common task/process attributes instruction.prepareCommonAttributes(this, ctx.modelContext, requester); diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java index bf8b0807b0d..74244866c62 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java @@ -363,7 +363,7 @@ private PcpStartInstruction prepareAssignmentRelatedStartInstruction( PcpStartInstruction instruction = PcpStartInstruction .createItemApprovalInstruction(main.getChangeProcessor(), - builderResult.schema, builderResult.attachedRules); + builderResult.schema, builderResult.attachedRules, main.getChangeProcessor().getClock()); instruction.prepareCommonAttributes(main, ctx.modelContext, requester); instruction.setDeltasToApprove(deltaToApprove); diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/ObjectPolicyAspectPart.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/ObjectPolicyAspectPart.java index d9f4273fc46..5faf48ff26c 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/ObjectPolicyAspectPart.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/ObjectPolicyAspectPart.java @@ -208,7 +208,7 @@ private void prepareObjectRelatedTaskInstructions( PcpStartInstruction instruction = PcpStartInstruction .createItemApprovalInstruction(main.getChangeProcessor(), - builderResult.schema, builderResult.attachedRules); + builderResult.schema, builderResult.attachedRules, main.getChangeProcessor().getClock()); instruction.prepareCommonAttributes(main, ctx.modelContext, requester); instruction.setDeltasToApprove(deltaToApprove); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/resources/ResourceUpdater.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/resources/ResourceUpdater.java index 5ab7c518a45..a697d897804 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/resources/ResourceUpdater.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/resources/ResourceUpdater.java @@ -97,7 +97,8 @@ void updateNativeCapabilities( newCapabilities.setConfigured(existingConfigured.clone()); } newCapabilities.setNative(CloneUtil.clone(nativeCapabilities)); - newCapabilities.setCachingMetadata(MiscSchemaUtil.generateCachingMetadata()); + newCapabilities.setCachingMetadata(MiscSchemaUtil.generateCachingMetadata( + beans.clock.currentTimeXMLGregorianCalendar())); if (updateRepository) { modifications.add( @@ -113,7 +114,8 @@ void updateNativeCapabilities( void updateCapabilitiesCachingMetadata(@NotNull ConnectorSpec connectorSpec) throws SchemaException { - CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); + CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata( + beans.clock.currentTimeXMLGregorianCalendar()); if (updateRepository) { modifications.add( PrismContext.get().deltaFor(ResourceType.class) @@ -145,7 +147,8 @@ private XmlSchemaType createSchemaUpdateValue(NativeResourceSchema nativeResourc getSchemaRootElement(nativeResourceSchema)); return new XmlSchemaType() - .cachingMetadata(MiscSchemaUtil.generateCachingMetadata()) + .cachingMetadata(MiscSchemaUtil.generateCachingMetadata( + beans.clock.currentTimeXMLGregorianCalendar())) .definition(schemaDefinition) .generationConstraints(getCurrentSchemaGenerationConstraints()); } @@ -175,7 +178,8 @@ void updateSchemaCachingMetadata() throws SchemaException { modifications.add( PrismContext.get().deltaFor(ResourceType.class) .item(ResourceType.F_SCHEMA, CapabilitiesType.F_CACHING_METADATA) - .replace(MiscSchemaUtil.generateCachingMetadata()) + .replace(MiscSchemaUtil.generateCachingMetadata( + beans.clock.currentTimeXMLGregorianCalendar())) .asItemDelta()); } diff --git a/release-notes.adoc b/release-notes.adoc index 01bfe89bbed..88185aea874 100644 --- a/release-notes.adoc +++ b/release-notes.adoc @@ -95,6 +95,7 @@ Overall, midPoint 4.10 opens up the world of identity management and governance * Fixed modification of query of report fails when the query is defined using midPoint query language. See bug:MID-11093[] * Fixed access request shows unrelated conflicts. See bug:MID-10994[] * Fixed false replace updates in admin GUI dateTime comparison (seconds normalization issue). See bug:MID-11078[] +* Fixed timestamp handling to use logical Clock where appropriate.. See bug:MID-11008[] === Releases Of Other Components diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/ActivityStatistics.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/ActivityStatistics.java index 9ea9bcd40fe..2c8d354dffb 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/ActivityStatistics.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/ActivityStatistics.java @@ -114,7 +114,7 @@ public void stopCollectingSynchronizationStatistics(@NotNull Task task, @NotNull public void startCollectingActionsExecuted(@NotNull Task task) { task.startCollectingActionsExecuted( - new ActionsExecutedCollectorImpl(actionsExecuted)); + new ActionsExecutedCollectorImpl(actionsExecuted, activityState.getBeans().clock)); } public void stopCollectingActionsExecuted(@NotNull Task task) { diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/actions/ActionsExecutedCollectorImpl.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/actions/ActionsExecutedCollectorImpl.java index 411953611e9..26faf5527e1 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/actions/ActionsExecutedCollectorImpl.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/activity/run/state/actions/ActionsExecutedCollectorImpl.java @@ -12,6 +12,7 @@ import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismObjectDefinition; @@ -46,9 +47,11 @@ public class ActionsExecutedCollectorImpl implements ActionsExecutedCollector { * Place where the summarized information is sent at the end. */ @NotNull private final ActivityActionsExecuted activityActionsExecuted; + @NotNull private final Clock clock; - public ActionsExecutedCollectorImpl(@NotNull ActivityActionsExecuted activityActionsExecuted) { + public ActionsExecutedCollectorImpl(@NotNull ActivityActionsExecuted activityActionsExecuted, @NotNull Clock clock) { this.activityActionsExecuted = activityActionsExecuted; + this.clock = clock; } @Override @@ -93,7 +96,7 @@ public void recordActionExecuted(PrismObject object, C private void recordInternal(String objectName, String objectDisplayName, QName objectType, String objectOid, ChangeType changeType, String channel, Throwable exception) { - XMLGregorianCalendar now = XmlTypeConverter.createXMLGregorianCalendar(new Date()); + XMLGregorianCalendar now = clock.currentTimeXMLGregorianCalendar(); ActionExecuted action = new ActionExecuted(objectName, objectDisplayName, objectType, objectOid, changeType, channel, exception, now); if (action.objectOid == null) { diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/util/OperationExecutionWriter.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/util/OperationExecutionWriter.java index 51617809aea..1abd45e0fb1 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/util/OperationExecutionWriter.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/util/OperationExecutionWriter.java @@ -6,6 +6,7 @@ package com.evolveum.midpoint.repo.common.util; +import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.delta.ItemDelta; @@ -64,6 +65,7 @@ public class OperationExecutionWriter implements SystemConfigurationChangeListen @Autowired private SchemaService schemaService; @Autowired private SystemConfigurationChangeDispatcher systemConfigurationChangeDispatcher; @Autowired private PrismContext prismContext; + @Autowired private Clock clock; @Autowired @Qualifier("cacheRepositoryService") private RepositoryService repositoryService; public static final int DEFAULT_NUMBER_OF_RESULTS_TO_KEEP = 5; @@ -102,7 +104,8 @@ public void write(Request request, OperationResult par return; } - CleaningSpecification cleaningSpec = CleaningSpecification.createFrom(selectCleanupPolicy(currentRecordType)); + CleaningSpecification cleaningSpec = + CleaningSpecification.createFrom(selectCleanupPolicy(currentRecordType), clock.currentTimeMillis()); boolean addingRecord; if (cleaningSpec.isKeepNone()) { @@ -372,21 +375,21 @@ private CleaningSpecification(int recordsToKeep, int recordsToKeepPerTask, long this.deleteBefore = deleteBefore; } - private static CleaningSpecification createFrom(OperationExecutionCleanupPolicyType policy) { + private static CleaningSpecification createFrom(OperationExecutionCleanupPolicyType policy, long now) { if (policy != null) { return new CleaningSpecification( ObjectUtils.defaultIfNull(policy.getMaxRecords(), Integer.MAX_VALUE), ObjectUtils.defaultIfNull(policy.getMaxRecordsPerTask(), Integer.MAX_VALUE), - computeDeleteBefore(policy)); + computeDeleteBefore(policy, now)); } else { return new CleaningSpecification(DEFAULT_NUMBER_OF_RESULTS_TO_KEEP, DEFAUL_NUMBER_OF_RESULTS_TO_KEEP_PER_TASK, 0L); } } - private static long computeDeleteBefore(CleanupPolicyType cleanupPolicy) { + private static long computeDeleteBefore(CleanupPolicyType cleanupPolicy, long now) { if (cleanupPolicy.getMaxAge() != null) { XMLGregorianCalendar limit = XmlTypeConverter.addDuration( - XmlTypeConverter.createXMLGregorianCalendar(new Date()), cleanupPolicy.getMaxAge().negate()); + XmlTypeConverter.createXMLGregorianCalendar(new Date(now)), cleanupPolicy.getMaxAge().negate()); return XmlTypeConverter.toMillis(limit); } else { return 0;