private <F extends ObjectType> void recordEffectiveStatusDelta( LensFocusContext<F> focusContext, ActivationStatusType effectiveStatusNew, XMLGregorianCalendar now) throws SchemaException { PrismContainerDefinition<ActivationType> activationDefinition = getActivationDefinition(); PrismPropertyDefinition<ActivationStatusType> effectiveStatusDef = activationDefinition.findPropertyDefinition(ActivationType.F_EFFECTIVE_STATUS); PropertyDelta<ActivationStatusType> effectiveStatusDelta = effectiveStatusDef.createEmptyDelta( new ItemPath(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS)); effectiveStatusDelta.setValueToReplace( new PrismPropertyValue<ActivationStatusType>( effectiveStatusNew, OriginType.USER_POLICY, null)); focusContext.swallowToProjectionWaveSecondaryDelta(effectiveStatusDelta); PropertyDelta<XMLGregorianCalendar> timestampDelta = LensUtil.createActivationTimestampDelta( effectiveStatusNew, now, activationDefinition, OriginType.USER_POLICY); focusContext.swallowToProjectionWaveSecondaryDelta(timestampDelta); }
private <F extends FocusType> void processFocusFocus( LensContext<F> context, String activityDescription, XMLGregorianCalendar now, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException, PolicyViolationException, ObjectAlreadyExistsException, CommunicationException, ConfigurationException, SecurityViolationException { LensFocusContext<F> focusContext = context.getFocusContext(); ObjectTemplateType objectTemplate = context.getFocusTemplate(); boolean resetOnRename = true; // This is fixed now. TODO: make it configurable int maxIterations = 0; IterationSpecificationType iterationSpecificationType = null; if (objectTemplate != null) { iterationSpecificationType = objectTemplate.getIteration(); maxIterations = LensUtil.determineMaxIterations(iterationSpecificationType); } int iteration = focusContext.getIteration(); String iterationToken = focusContext.getIterationToken(); boolean wasResetIterationCounter = false; PrismObject<F> focusCurrent = focusContext.getObjectCurrent(); if (focusCurrent != null && iterationToken == null) { Integer focusIteration = focusCurrent.asObjectable().getIteration(); if (focusIteration != null) { iteration = focusIteration; } iterationToken = focusCurrent.asObjectable().getIterationToken(); } while (true) { ObjectTypeTemplateType objectPolicyConfigurationType = focusContext.getObjectPolicyConfigurationType(); if (objectPolicyConfigurationType != null && BooleanUtils.isTrue(objectPolicyConfigurationType.isOidNameBoundMode())) { // Generate the name now - unless it is already present PrismObject<F> focusNew = focusContext.getObjectNew(); if (focusNew != null) { PolyStringType focusNewName = focusNew.asObjectable().getName(); if (focusNewName == null) { String newName = focusNew.getOid(); if (newName == null) { newName = OidUtil.generateOid(); } LOGGER.trace("Generating new name (bound to OID): {}", newName); PrismObjectDefinition<F> focusDefinition = focusContext.getObjectDefinition(); PrismPropertyDefinition<PolyString> focusNameDef = focusDefinition.findPropertyDefinition(FocusType.F_NAME); PropertyDelta<PolyString> nameDelta = focusNameDef.createEmptyDelta(new ItemPath(FocusType.F_NAME)); nameDelta.setValueToReplace( new PrismPropertyValue<PolyString>( new PolyString(newName), OriginType.USER_POLICY, null)); focusContext.swallowToSecondaryDelta(nameDelta); focusContext.recompute(); } } } ExpressionVariables variables = Utils.getDefaultExpressionVariables( focusContext.getObjectNew(), null, null, null, context.getSystemConfiguration()); if (iterationToken == null) { iterationToken = LensUtil.formatIterationToken( context, focusContext, iterationSpecificationType, iteration, expressionFactory, variables, task, result); } // We have to remember the token and iteration in the context. // The context can be recomputed several times. But we always want // to use the same iterationToken if possible. If there is a random // part in the iterationToken expression that we need to avoid recomputing // the token otherwise the value can change all the time (even for the same inputs). // Storing the token in the secondary delta is not enough because secondary deltas can be // dropped // if the context is re-projected. focusContext.setIteration(iteration); focusContext.setIterationToken(iterationToken); LOGGER.trace( "Focus {} processing, iteration {}, token '{}'", new Object[] {focusContext.getHumanReadableName(), iteration, iterationToken}); String conflictMessage; if (!LensUtil.evaluateIterationCondition( context, focusContext, iterationSpecificationType, iteration, iterationToken, true, expressionFactory, variables, task, result)) { conflictMessage = "pre-iteration condition was false"; LOGGER.debug( "Skipping iteration {}, token '{}' for {} because the pre-iteration condition was false", new Object[] {iteration, iterationToken, focusContext.getHumanReadableName()}); } else { // INBOUND if (consistencyChecks) context.checkConsistence(); // Loop through the account changes, apply inbound expressions inboundProcessor.processInbound(context, now, task, result); if (consistencyChecks) context.checkConsistence(); context.recomputeFocus(); LensUtil.traceContext(LOGGER, activityDescription, "inbound", false, context, false); if (consistencyChecks) context.checkConsistence(); // ACTIVATION processActivation(context, now, result); // OBJECT TEMPLATE (before assignments) objectTemplateProcessor.processTemplate( context, ObjectTemplateMappingEvaluationPhaseType.BEFORE_ASSIGNMENTS, now, task, result); // ASSIGNMENTS assignmentProcessor.processAssignmentsProjections(context, now, task, result); assignmentProcessor.processOrgAssignments(context, result); context.recompute(); assignmentProcessor.checkForAssignmentConflicts(context, result); // OBJECT TEMPLATE (after assignments) objectTemplateProcessor.processTemplate( context, ObjectTemplateMappingEvaluationPhaseType.AFTER_ASSIGNMENTS, now, task, result); context.recompute(); // PASSWORD POLICY passwordPolicyProcessor.processPasswordPolicy(focusContext, context, result); // Processing done, check for success if (resetOnRename && !wasResetIterationCounter && willResetIterationCounter(focusContext)) { // Make sure this happens only the very first time during the first recompute. // Otherwise it will always change the token (especially if the token expression has a // random part) // hence the focusContext.getIterationToken() == null wasResetIterationCounter = true; if (iteration != 0) { iteration = 0; iterationToken = null; LOGGER.trace("Resetting iteration counter and token because rename was detected"); cleanupContext(focusContext); continue; } } PrismObject<F> previewObjectNew = focusContext.getObjectNew(); if (previewObjectNew == null) { // this must be delete } else { // Explicitly check for name. The checker would check for this also. But checking it here // will produce better error message PolyStringType objectName = previewObjectNew.asObjectable().getName(); if (objectName == null || objectName.getOrig().isEmpty()) { throw new SchemaException( "No name in new object " + objectName + " as produced by template " + objectTemplate + " in iteration " + iteration + ", we cannot process an object without a name"); } } // Check if iteration constraints are OK FocusConstraintsChecker<F> checker = new FocusConstraintsChecker<>(); checker.setPrismContext(prismContext); checker.setContext(context); checker.setRepositoryService(cacheRepositoryService); checker.check(previewObjectNew, result); if (checker.isSatisfiesConstraints()) { LOGGER.trace( "Current focus satisfies uniqueness constraints. Iteration {}, token '{}'", iteration, iterationToken); if (LensUtil.evaluateIterationCondition( context, focusContext, iterationSpecificationType, iteration, iterationToken, false, expressionFactory, variables, task, result)) { // stop the iterations break; } else { conflictMessage = "post-iteration condition was false"; LOGGER.debug( "Skipping iteration {}, token '{}' for {} because the post-iteration condition was false", new Object[] {iteration, iterationToken, focusContext.getHumanReadableName()}); } } else { LOGGER.trace( "Current focus does not satisfy constraints. Conflicting object: {}; iteration={}, maxIterations={}", new Object[] {checker.getConflictingObject(), iteration, maxIterations}); conflictMessage = checker.getMessages(); } if (!wasResetIterationCounter) { wasResetIterationCounter = true; if (iteration != 0) { iterationToken = null; iteration = 0; LOGGER.trace("Resetting iteration counter and token after conflict"); cleanupContext(focusContext); continue; } } } // Next iteration iteration++; iterationToken = null; if (iteration > maxIterations) { StringBuilder sb = new StringBuilder(); if (iteration == 1) { sb.append("Error processing "); } else { sb.append("Too many iterations (" + iteration + ") for "); } sb.append(focusContext.getHumanReadableName()); if (iteration == 1) { sb.append(": constraint violation: "); } else { sb.append(": cannot determine values that satisfy constraints: "); } if (conflictMessage != null) { sb.append(conflictMessage); } throw new ObjectAlreadyExistsException(sb.toString()); } cleanupContext(focusContext); } addIterationTokenDeltas(focusContext, iteration, iterationToken); if (consistencyChecks) context.checkConsistence(); }