/*
* Copyright (c) 2010-2017 Evolveum
*
* 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.
*/
package com.evolveum.midpoint.model.impl.lens;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.ProgressInformation;
import com.evolveum.midpoint.model.api.ProgressListener;
import com.evolveum.midpoint.model.api.context.*;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.DeltaSetTriple;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;
import com.evolveum.midpoint.schema.ObjectDeltaOperation;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.PolicyRuleTypeUtil;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import javax.xml.namespace.QName;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
/**
* @author semancik
*
*/
public class LensContext<F extends ObjectType> implements ModelContext<F> {
private static final long serialVersionUID = -778283437426659540L;
private static final String DOT_CLASS = LensContext.class.getName() + ".";
private ModelState state = ModelState.INITIAL;
/**
* Channel that is the source of primary change (GUI, live sync, import,
* ...)
*/
private String channel;
private LensFocusContext<F> focusContext;
private Collection<LensProjectionContext> projectionContexts = new ArrayList<>();
/**
* EXPERIMENTAL. A trace of resource objects that once existed but were
* unlinked or deleted, and the corresponding contexts were rotten and
* removed afterwards.
*
* Necessary to evaluate old state of hasLinkedAccount.
*
* TODO implement as non-transient. TODO consider storing whole projection
* contexts here.
*/
transient private Collection<ResourceShadowDiscriminator> historicResourceObjects;
private Class<F> focusClass;
private boolean lazyAuditRequest = false; // should be the request audited
// just before the execution is
// audited?
private boolean requestAudited = false; // was the request audited?
private boolean executionAudited = false; // was the execution audited?
private LensContextStatsType stats = new LensContextStatsType();
/**
* Metadata of the request. Metadata recorded when the operation has
* started. Currently only the requestTimestamp and requestorRef are
* meaningful. But later other metadata may be used.
*/
private MetadataType requestMetadata;
/*
* Executed deltas from rotten contexts.
*/
private List<LensObjectDeltaOperation<?>> rottenExecutedDeltas = new ArrayList<>();
transient private ObjectTemplateType focusTemplate;
transient private ProjectionPolicyType accountSynchronizationSettings;
transient private DeltaSetTriple<EvaluatedAssignmentImpl<?>> evaluatedAssignmentTriple;
/**
* Just a cached copy. Keep it in context so we do not need to reload it all
* the time.
*/
transient private PrismObject<SystemConfigurationType> systemConfiguration;
/**
* True if we want to reconcile all accounts in this context.
*/
private boolean doReconciliationForAllProjections = false;
/**
* If set to true then all operations are considered to be
* in execution phase - for the purpose of authorizations and auditing.
* This is used in case that the whole operation (context) is a
* secondary change, e.g. in case that persona is provisioned.
*/
private boolean executionPhaseOnly = false;
/**
* Current wave of computation and execution.
*/
int projectionWave = 0;
/**
* Current wave of execution.
*/
int executionWave = 0;
private String triggeredResourceOid;
/**
* At this level, isFresh == false means that deeper recomputation has to be
* carried out.
*/
transient private boolean isFresh = false;
transient private boolean isRequestAuthorized = false;
/**
* Cache of resource instances. It is used to reduce the number of read
* (getObject) calls for ResourceType objects.
*/
transient private Map<String, ResourceType> resourceCache;
transient private PrismContext prismContext;
transient private ProvisioningService provisioningService;
private ModelExecuteOptions options;
/**
* Used mostly in unit tests.
*/
transient private LensDebugListener debugListener;
/**
* User feedback.
*/
transient private Collection<ProgressListener> progressListeners;
private Map<String, Long> sequences = new HashMap<>();
/**
* Moved from ProjectionValuesProcessor TODO consider if necessary to
* serialize to XML
*/
private List<LensProjectionContext> conflictingProjectionContexts = new ArrayList<>();
public LensContext(Class<F> focusClass, PrismContext prismContext,
ProvisioningService provisioningService) {
Validate.notNull(prismContext, "No prismContext");
this.prismContext = prismContext;
this.provisioningService = provisioningService;
this.focusClass = focusClass;
}
protected LensContext(PrismContext prismContext) {
this.prismContext = prismContext;
}
public PrismContext getPrismContext() {
return prismContext;
}
protected PrismContext getNotNullPrismContext() {
if (prismContext == null) {
throw new IllegalStateException(
"Null prism context in " + this + "; the context was not adopted (most likely)");
}
return prismContext;
}
public ProvisioningService getProvisioningService() {
return provisioningService;
}
public void setTriggeredResource(ResourceType triggeredResource) {
if (triggeredResource != null) {
this.triggeredResourceOid = triggeredResource.getOid();
}
}
public String getTriggeredResourceOid() {
return triggeredResourceOid;
}
@Override
public ModelState getState() {
return state;
}
public void setState(ModelState state) {
this.state = state;
}
@Override
public LensFocusContext<F> getFocusContext() {
return focusContext;
}
public void setFocusContext(LensFocusContext<F> focusContext) {
this.focusContext = focusContext;
}
public LensFocusContext<F> createFocusContext() {
return createFocusContext(null);
}
public LensFocusContext<F> createFocusContext(Class<F> explicitFocusClass) {
if (explicitFocusClass != null) {
this.focusClass = explicitFocusClass;
}
focusContext = new LensFocusContext<>(focusClass, this);
return focusContext;
}
public LensFocusContext<F> getOrCreateFocusContext() {
return getOrCreateFocusContext(null);
}
public LensFocusContext<F> getOrCreateFocusContext(Class<F> explicitFocusClass) {
if (focusContext == null) {
createFocusContext(explicitFocusClass);
}
return focusContext;
}
@Override
public Collection<LensProjectionContext> getProjectionContexts() {
return projectionContexts;
}
public Iterator<LensProjectionContext> getProjectionContextsIterator() {
return projectionContexts.iterator();
}
public void addProjectionContext(LensProjectionContext projectionContext) {
projectionContexts.add(projectionContext);
}
public LensProjectionContext findProjectionContextByOid(String oid) {
for (LensProjectionContext projCtx : getProjectionContexts()) {
if (oid.equals(projCtx.getOid())) {
return projCtx;
}
}
return null;
}
public LensProjectionContext findProjectionContext(ResourceShadowDiscriminator rat) {
Validate.notNull(rat);
for (LensProjectionContext projCtx : getProjectionContexts()) {
if (projCtx.compareResourceShadowDiscriminator(rat, true)) {
return projCtx;
}
}
return null;
}
public LensProjectionContext findOrCreateProjectionContext(ResourceShadowDiscriminator rat) {
LensProjectionContext projectionContext = findProjectionContext(rat);
if (projectionContext == null) {
projectionContext = createProjectionContext(rat);
}
return projectionContext;
}
public ObjectTemplateType getFocusTemplate() {
return focusTemplate;
}
public void setFocusTemplate(ObjectTemplateType focusTemplate) {
this.focusTemplate = focusTemplate;
}
public LensProjectionContext findProjectionContext(ResourceShadowDiscriminator rat, String oid) {
LensProjectionContext projectionContext = findProjectionContext(rat);
if (projectionContext == null || projectionContext.getOid() == null
|| !oid.equals(projectionContext.getOid())) {
return null;
}
return projectionContext;
}
public PrismObject<SystemConfigurationType> getSystemConfiguration() {
return systemConfiguration;
}
public void setSystemConfiguration(PrismObject<SystemConfigurationType> systemConfiguration) {
this.systemConfiguration = systemConfiguration;
}
public ProjectionPolicyType getAccountSynchronizationSettings() {
return accountSynchronizationSettings;
}
public void setAccountSynchronizationSettings(ProjectionPolicyType accountSynchronizationSettings) {
this.accountSynchronizationSettings = accountSynchronizationSettings;
}
public int getProjectionWave() {
return projectionWave;
}
public void setProjectionWave(int wave) {
this.projectionWave = wave;
}
public void incrementProjectionWave() {
projectionWave++;
}
public void resetProjectionWave() {
projectionWave = executionWave;
}
public int getExecutionWave() {
return executionWave;
}
public void setExecutionWave(int executionWave) {
this.executionWave = executionWave;
}
public void incrementExecutionWave() {
executionWave++;
}
public int getMaxWave() {
int maxWave = 0;
for (LensProjectionContext projContext : projectionContexts) {
if (projContext.getWave() > maxWave) {
maxWave = projContext.getWave();
}
}
return maxWave;
}
public boolean isFresh() {
return isFresh;
}
public void setFresh(boolean isFresh) {
this.isFresh = isFresh;
}
public boolean isRequestAuthorized() {
return isRequestAuthorized;
}
public void setRequestAuthorized(boolean isRequestAuthorized) {
this.isRequestAuthorized = isRequestAuthorized;
}
/**
* Makes the context and all sub-context non-fresh.
*/
public void rot() {
setFresh(false);
if (focusContext != null) {
focusContext.setFresh(false);
}
for (LensProjectionContext projectionContext : projectionContexts) {
projectionContext.setFresh(false);
projectionContext.setFullShadow(false);
}
}
// /**
// * Make the context as clean as new. Except for the executed deltas and
// other "traces" of
// * what was already done and cannot be undone. Also the configuration
// items that were loaded may remain.
// * This is used to restart the context computation but keep the trace of
// what was already done.
// */
// public void reset() {
// state = ModelState.INITIAL;
// evaluatedAssignmentTriple = null;
// projectionWave = 0;
// executionWave = 0;
// isFresh = false;
// if (focusContext != null) {
// focusContext.reset();
// }
// if (projectionContexts != null) {
// for (LensProjectionContext projectionContext: projectionContexts) {
// projectionContext.reset();
// }
// }
// }
public String getChannel() {
return channel;
}
public void setChannel(String channelUri) {
this.channel = channelUri;
}
public void setChannel(QName channelQName) {
this.channel = QNameUtil.qNameToUri(channelQName);
}
public boolean isDoReconciliationForAllProjections() {
return doReconciliationForAllProjections;
}
public void setDoReconciliationForAllProjections(boolean doReconciliationForAllProjections) {
this.doReconciliationForAllProjections = doReconciliationForAllProjections;
}
public boolean isReconcileFocus() {
return doReconciliationForAllProjections || ModelExecuteOptions.isReconcileFocus(options);
}
public boolean isExecutionPhaseOnly() {
return executionPhaseOnly;
}
public void setExecutionPhaseOnly(boolean executionPhaseOnly) {
this.executionPhaseOnly = executionPhaseOnly;
}
public DeltaSetTriple<EvaluatedAssignmentImpl<?>> getEvaluatedAssignmentTriple() {
return evaluatedAssignmentTriple;
}
public void setEvaluatedAssignmentTriple(
DeltaSetTriple<EvaluatedAssignmentImpl<?>> evaluatedAssignmentTriple) {
this.evaluatedAssignmentTriple = evaluatedAssignmentTriple;
}
public ModelExecuteOptions getOptions() {
return options;
}
public void setOptions(ModelExecuteOptions options) {
this.options = options;
}
public PartialProcessingOptionsType getPartialProcessingOptions() {
if (options == null || options.getPartialProcessing() == null) {
return new PartialProcessingOptionsType();
} else {
return options.getPartialProcessing();
}
}
public MetadataType getRequestMetadata() {
return requestMetadata;
}
public void setRequestMetadata(MetadataType requestMetadata) {
this.requestMetadata = requestMetadata;
}
public LensDebugListener getDebugListener() {
return debugListener;
}
public void setDebugListener(LensDebugListener debugListener) {
this.debugListener = debugListener;
}
/**
* If set to true then the request will be audited right before execution.
* If no execution takes place then no request will be audited.
*/
public boolean isLazyAuditRequest() {
return lazyAuditRequest;
}
public void setLazyAuditRequest(boolean lazyAuditRequest) {
this.lazyAuditRequest = lazyAuditRequest;
}
public boolean isRequestAudited() {
return requestAudited;
}
public void setRequestAudited(boolean requestAudited) {
this.requestAudited = requestAudited;
}
public boolean isExecutionAudited() {
return executionAudited;
}
public void setExecutionAudited(boolean executionAudited) {
this.executionAudited = executionAudited;
}
public LensContextStatsType getStats() {
return stats;
}
public void setStats(LensContextStatsType stats) {
this.stats = stats;
}
public OperationBusinessContextType getRequestBusinessContext() {
if (options == null) {
return null;
}
return options.getRequestBusinessContext();
}
/**
* Returns all changes, user and all accounts. Both primary and secondary
* changes are returned, but these are not merged. TODO: maybe it would be
* better to merge them.
*/
public Collection<ObjectDelta<? extends ObjectType>> getAllChanges() throws SchemaException {
Collection<ObjectDelta<? extends ObjectType>> allChanges = new ArrayList<>();
if (focusContext != null) {
addChangeIfNotNull(allChanges, focusContext.getPrimaryDelta());
addChangeIfNotNull(allChanges, focusContext.getSecondaryDelta());
}
for (LensProjectionContext projCtx : getProjectionContexts()) {
addChangeIfNotNull(allChanges, projCtx.getPrimaryDelta());
addChangeIfNotNull(allChanges, projCtx.getSecondaryDelta());
}
return allChanges;
}
public boolean hasAnyPrimaryChange() throws SchemaException {
if (focusContext != null) {
if (!ObjectDelta.isNullOrEmpty(focusContext.getPrimaryDelta())) {
return true;
}
}
for (LensProjectionContext projCtx : getProjectionContexts()) {
if (!ObjectDelta.isNullOrEmpty(projCtx.getPrimaryDelta())) {
return true;
}
}
return false;
}
public Collection<ObjectDelta<? extends ObjectType>> getPrimaryChanges() throws SchemaException {
Collection<ObjectDelta<? extends ObjectType>> allChanges = new ArrayList<>();
if (focusContext != null) {
addChangeIfNotNull(allChanges, focusContext.getPrimaryDelta());
}
for (LensProjectionContext projCtx : getProjectionContexts()) {
addChangeIfNotNull(allChanges, projCtx.getPrimaryDelta());
}
return allChanges;
}
private <T extends ObjectType> void addChangeIfNotNull(
Collection<ObjectDelta<? extends ObjectType>> changes, ObjectDelta<T> change) {
if (change != null) {
changes.add(change);
}
}
public void replacePrimaryFocusDelta(ObjectDelta<F> newDelta) {
focusContext.setPrimaryDelta(newDelta);
// todo any other changes have to be done?
}
public void replacePrimaryFocusDeltas(List<ObjectDelta<F>> deltas) throws SchemaException {
replacePrimaryFocusDelta(null);
if (deltas != null) {
for (ObjectDelta<F> delta : deltas) {
focusContext.addPrimaryDelta(delta);
}
}
// todo any other changes have to be done?
}
/**
* Returns all executed deltas, user and all accounts.
*/
public Collection<ObjectDeltaOperation<? extends ObjectType>> getExecutedDeltas() throws SchemaException {
return getExecutedDeltas(null);
}
/**
* Returns all executed deltas, user and all accounts.
*/
public Collection<ObjectDeltaOperation<? extends ObjectType>> getUnauditedExecutedDeltas()
throws SchemaException {
return getExecutedDeltas(false);
}
/**
* Returns all executed deltas, user and all accounts.
*/
Collection<ObjectDeltaOperation<? extends ObjectType>> getExecutedDeltas(Boolean audited)
throws SchemaException {
Collection<ObjectDeltaOperation<? extends ObjectType>> executedDeltas = new ArrayList<>();
if (focusContext != null) {
executedDeltas.addAll(focusContext.getExecutedDeltas(audited));
}
for (LensProjectionContext projCtx : getProjectionContexts()) {
executedDeltas.addAll(projCtx.getExecutedDeltas(audited));
}
if (audited == null) {
executedDeltas.addAll((Collection<? extends ObjectDeltaOperation<? extends ObjectType>>) getRottenExecutedDeltas());
}
return executedDeltas;
}
public void markExecutedDeltasAudited() {
if (focusContext != null) {
focusContext.markExecutedDeltasAudited();
}
for (LensProjectionContext projCtx : getProjectionContexts()) {
projCtx.markExecutedDeltasAudited();
}
}
public List<LensObjectDeltaOperation<?>> getRottenExecutedDeltas() {
return rottenExecutedDeltas;
}
public void recompute() throws SchemaException {
recomputeFocus();
recomputeProjections();
}
// mainly computes new state based on old state and delta(s)
public void recomputeFocus() throws SchemaException {
if (focusContext != null) {
focusContext.recompute();
}
}
public void recomputeProjections() throws SchemaException {
for (LensProjectionContext projCtx : getProjectionContexts()) {
projCtx.recompute();
}
}
public void refreshAuxiliaryObjectClassDefinitions() throws SchemaException {
for (LensProjectionContext projCtx : getProjectionContexts()) {
projCtx.refreshAuxiliaryObjectClassDefinitions();
}
}
public void checkAbortRequested() {
if (isAbortRequested()) {
throw new RuntimeException("Aborted on user request"); // TODO more
// meaningful
// exception
// + message
}
}
public void checkConsistence() {
checkAbortRequested();
if (focusContext != null) {
focusContext.checkConsistence();
}
for (LensProjectionContext projectionContext : projectionContexts) {
projectionContext.checkConsistence(this.toString(), isFresh,
ModelExecuteOptions.isForce(options));
}
}
public void checkEncrypted() {
if (focusContext != null && !focusContext.isDelete()) {
focusContext.checkEncrypted();
}
for (LensProjectionContext projectionContext : projectionContexts) {
if (!projectionContext.isDelete()) {
projectionContext.checkEncrypted();
}
}
}
public LensProjectionContext createProjectionContext() {
return createProjectionContext(null);
}
public LensProjectionContext createProjectionContext(ResourceShadowDiscriminator rat) {
LensProjectionContext projCtx = new LensProjectionContext(this, rat);
addProjectionContext(projCtx);
return projCtx;
}
private Map<String, ResourceType> getResourceCache() {
if (resourceCache == null) {
resourceCache = new HashMap<>();
}
return resourceCache;
}
/**
* Returns a resource for specified account type. This is supposed to be
* efficient, taking the resource from the cache. It assumes the resource is
* in the cache.
*
* @see LensContext#rememberResource(ResourceType)
*/
public ResourceType getResource(ResourceShadowDiscriminator rat) {
return getResource(rat.getResourceOid());
}
/**
* Returns a resource for specified account type. This is supposed to be
* efficient, taking the resource from the cache. It assumes the resource is
* in the cache.
*
* @see LensContext#rememberResource(ResourceType)
*/
public ResourceType getResource(String resourceOid) {
return getResourceCache().get(resourceOid);
}
/**
* Puts resources in the cache for later use. The resources should be
* fetched from provisioning and have pre-parsed schemas. So the next time
* just reuse them without the other overhead.
*/
public void rememberResources(Collection<ResourceType> resources) {
for (ResourceType resourceType : resources) {
rememberResource(resourceType);
}
}
/**
* Puts resource in the cache for later use. The resource should be fetched
* from provisioning and have pre-parsed schemas. So the next time just
* reuse it without the other overhead.
*/
public void rememberResource(ResourceType resourceType) {
getResourceCache().put(resourceType.getOid(), resourceType);
}
/**
* Cleans up the contexts by removing some of the working state. The current
* wave number is retained. Otherwise it ends up in endless loop.
*/
public void cleanup() throws SchemaException {
if (focusContext != null) {
focusContext.cleanup();
}
for (LensProjectionContext projectionContext : projectionContexts) {
projectionContext.cleanup();
}
recompute();
}
public void adopt(PrismContext prismContext) throws SchemaException {
this.prismContext = prismContext;
if (focusContext != null) {
focusContext.adopt(prismContext);
}
for (LensProjectionContext projectionContext : projectionContexts) {
projectionContext.adopt(prismContext);
}
}
public void normalize() {
if (focusContext != null) {
focusContext.normalize();
}
if (projectionContexts != null) {
for (LensProjectionContext projectionContext : projectionContexts) {
projectionContext.normalize();
}
}
}
public LensContext<F> clone() {
LensContext<F> clone = new LensContext<>(focusClass, prismContext, provisioningService);
copyValues(clone);
return clone;
}
protected void copyValues(LensContext<F> clone) {
clone.state = this.state;
clone.channel = this.channel;
clone.doReconciliationForAllProjections = this.doReconciliationForAllProjections;
clone.executionPhaseOnly = this.executionPhaseOnly;
clone.focusClass = this.focusClass;
clone.isFresh = this.isFresh;
clone.prismContext = this.prismContext;
clone.resourceCache = cloneResourceCache();
// User template is de-facto immutable, OK to just pass reference here.
clone.focusTemplate = this.focusTemplate;
clone.projectionWave = this.projectionWave;
if (options != null) {
clone.options = this.options.clone();
}
if (this.focusContext != null) {
clone.focusContext = this.focusContext.clone(this);
}
for (LensProjectionContext thisProjectionContext : this.projectionContexts) {
clone.projectionContexts.add(thisProjectionContext.clone(this));
}
}
private Map<String, ResourceType> cloneResourceCache() {
if (resourceCache == null) {
return null;
}
Map<String, ResourceType> clonedMap = new HashMap<>();
for (Entry<String, ResourceType> entry : resourceCache.entrySet()) {
clonedMap.put(entry.getKey(), entry.getValue());
}
return clonedMap;
}
public void distributeResource() {
for (LensProjectionContext projCtx : getProjectionContexts()) {
projCtx.distributeResource();
}
}
@Override
public Class<F> getFocusClass() {
return focusClass;
}
@Override
public String debugDump() {
return debugDump(0);
}
public String dump(boolean showTriples) {
return debugDump(0, showTriples);
}
@Override
public String debugDump(int indent) {
return debugDump(indent, true);
}
public String debugDump(int indent, boolean showTriples) {
StringBuilder sb = new StringBuilder();
DebugUtil.indentDebugDump(sb, indent);
sb.append("LensContext: state=").append(state);
sb.append(", Wave(e=").append(executionWave);
sb.append(",p=").append(projectionWave);
sb.append(",max=").append(getMaxWave());
sb.append("), ");
if (focusContext != null) {
sb.append("focus, ");
}
sb.append(projectionContexts.size());
sb.append(" projections, ");
try {
Collection<ObjectDelta<? extends ObjectType>> allChanges = getAllChanges();
sb.append(allChanges.size());
} catch (SchemaException e) {
sb.append("[ERROR]");
}
sb.append(" changes, ");
sb.append("fresh=").append(isFresh);
if (systemConfiguration == null) {
sb.append(" null-system-configuration");
}
if (executionPhaseOnly) {
sb.append(" execution-phase-only");
}
sb.append("\n");
DebugUtil.debugDumpLabel(sb, "Channel", indent + 1);
sb.append(" ").append(channel).append("\n");
DebugUtil.debugDumpLabel(sb, "Options", indent + 1);
sb.append(" ").append(options).append("\n");
DebugUtil.debugDumpLabel(sb, "Settings", indent + 1);
sb.append(" ");
if (accountSynchronizationSettings != null) {
sb.append("assignments=");
sb.append(accountSynchronizationSettings.getAssignmentPolicyEnforcement());
} else {
sb.append("null");
}
sb.append("\n");
DebugUtil.debugDumpWithLabel(sb, "FOCUS", focusContext, indent + 1);
sb.append("\n");
if (DebugUtil.isDetailedDebugDump()) {
DebugUtil.debugDumpWithLabel(sb, "EvaluatedAssignments", evaluatedAssignmentTriple, indent + 3);
} else {
DebugUtil.indentDebugDump(sb, indent + 3);
sb.append("Evaluated assignments:");
if (evaluatedAssignmentTriple != null) {
dumpEvaluatedAssignments(sb, "Zero", (Collection) evaluatedAssignmentTriple.getZeroSet(),
indent + 4);
dumpEvaluatedAssignments(sb, "Plus", (Collection) evaluatedAssignmentTriple.getPlusSet(),
indent + 4);
dumpEvaluatedAssignments(sb, "Minus", (Collection) evaluatedAssignmentTriple.getMinusSet(),
indent + 4);
} else {
sb.append(" (null)");
}
}
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("PROJECTIONS:");
if (projectionContexts.isEmpty()) {
sb.append(" none");
} else {
sb.append(" (").append(projectionContexts.size()).append("):");
for (LensProjectionContext projCtx : projectionContexts) {
sb.append("\n");
sb.append(projCtx.debugDump(indent + 2, showTriples));
}
}
if (historicResourceObjects != null && !historicResourceObjects.isEmpty()) {
sb.append("\n");
DebugUtil.debugDumpWithLabel(sb, "Deleted/unlinked resource objects",
historicResourceObjects.toString(), indent + 1); // temporary
// impl
}
return sb.toString();
}
@Override
public String dumpPolicyRules(int indent) {
if (evaluatedAssignmentTriple == null) {
return "";
}
StringBuilder sb = new StringBuilder();
evaluatedAssignmentTriple.debugDumpSets(sb, assignment -> {
DebugUtil.indentDebugDump(sb, indent);
sb.append(assignment.toHumanReadableString());
@SuppressWarnings("unchecked")
Collection<EvaluatedPolicyRule> thisTargetPolicyRules = assignment.getThisTargetPolicyRules();
dumpPolicyRulesCollection("thisTargetPolicyRules", indent + 1, sb, thisTargetPolicyRules);
@SuppressWarnings({ "unchecked", "raw" })
Collection<EvaluatedPolicyRule> otherTargetsPolicyRules = assignment.getOtherTargetsPolicyRules();
dumpPolicyRulesCollection("otherTargetsPolicyRules", indent + 1, sb, otherTargetsPolicyRules);
}, 1);
return sb.toString();
}
private void dumpPolicyRulesCollection(String label, int indent, StringBuilder sb, Collection<EvaluatedPolicyRule> rules) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent);
sb.append(label).append(" (").append(rules.size()).append("):");
for (EvaluatedPolicyRule rule : rules) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
if (rule.isGlobal()) {
sb.append("global ");
}
sb.append("rule: ").append(rule.getName());
for (EvaluatedPolicyRuleTrigger trigger : rule.getTriggers()) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 2);
sb.append("trigger: ").append(trigger);
if (trigger instanceof EvaluatedExclusionTrigger
&& ((EvaluatedExclusionTrigger) trigger).getConflictingAssignment() != null) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 3);
sb.append("conflict: ")
.append(((EvaluatedAssignmentImpl) ((EvaluatedExclusionTrigger) trigger)
.getConflictingAssignment()).toHumanReadableString());
}
}
for (PolicyExceptionType exc : rule.getPolicyExceptions()) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 2);
sb.append("exception: ").append(exc);
}
}
}
// <F> of LensContext is of ObjectType; so we need to override it (but using another name to avoid IDE warnings)
private <FT extends FocusType> void dumpEvaluatedAssignments(StringBuilder sb, String label,
Collection<EvaluatedAssignmentImpl<FT>> set, int indent) {
sb.append("\n");
DebugUtil.debugDumpLabel(sb, label, indent);
for (EvaluatedAssignmentImpl<FT> assignment : set) {
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("-> ").append(assignment.getTarget());
dumpRules(sb, "focus rules", indent + 3, assignment.getFocusPolicyRules());
dumpRules(sb, "this target rules", indent + 3, assignment.getThisTargetPolicyRules());
dumpRules(sb, "other targets rules", indent + 3, assignment.getOtherTargetsPolicyRules());
}
}
private void dumpRules(StringBuilder sb, String label, int indent, Collection<EvaluatedPolicyRule> policyRules) {
if (policyRules.isEmpty()) {
return;
}
sb.append("\n");
DebugUtil.debugDumpLabel(sb, "- " + label + " (" + policyRules.size() + ", triggered " + getTriggeredRulesCount(policyRules) + ")", indent);
boolean first = true;
for (EvaluatedPolicyRule rule : policyRules) {
if (first) {
first = false;
sb.append(" ");
} else {
sb.append("; ");
}
if (rule.isGlobal()) {
sb.append("G:");
}
if (rule.getName() != null) {
sb.append(rule.getName());
}
sb.append("(").append(PolicyRuleTypeUtil.toShortString(rule.getPolicyConstraints())).append(")");
sb.append("->");
sb.append("(").append(PolicyRuleTypeUtil.toShortString(rule.getActions())).append(")");
if (!rule.getTriggers().isEmpty()) {
sb.append("=>T:(");
sb.append(rule.getTriggers().stream().map(EvaluatedPolicyRuleTrigger::toDiagShortcut)
.collect(Collectors.joining(", ")));
sb.append(")");
}
}
}
static int getTriggeredRulesCount(Collection<EvaluatedPolicyRule> policyRules) {
return (int) policyRules.stream().filter(r -> !r.getTriggers().isEmpty()).count();
}
public LensContextType toLensContextType() throws SchemaException {
PrismContainer<LensContextType> pc = toPrismContainer();
return pc.getValue().asContainerable();
}
public PrismContainer<LensContextType> toPrismContainer() throws SchemaException {
PrismContainer<LensContextType> lensContextTypeContainer = PrismContainer
.newInstance(getPrismContext(), LensContextType.COMPLEX_TYPE);
LensContextType lensContextType = lensContextTypeContainer.createNewValue().asContainerable();
lensContextType.setState(state != null ? state.toModelStateType() : null);
lensContextType.setChannel(channel);
if (focusContext != null) {
PrismContainer<LensFocusContextType> lensFocusContextTypeContainer = lensContextTypeContainer
.findOrCreateContainer(LensContextType.F_FOCUS_CONTEXT);
focusContext.addToPrismContainer(lensFocusContextTypeContainer);
}
PrismContainer<LensProjectionContextType> lensProjectionContextTypeContainer = lensContextTypeContainer
.findOrCreateContainer(LensContextType.F_PROJECTION_CONTEXT);
for (LensProjectionContext lensProjectionContext : projectionContexts) {
lensProjectionContext.addToPrismContainer(lensProjectionContextTypeContainer);
}
lensContextType.setFocusClass(focusClass != null ? focusClass.getName() : null);
lensContextType.setDoReconciliationForAllProjections(doReconciliationForAllProjections);
lensContextType.setExecutionPhaseOnly(executionPhaseOnly);
lensContextType.setProjectionWave(projectionWave);
lensContextType.setExecutionWave(executionWave);
lensContextType.setOptions(options != null ? options.toModelExecutionOptionsType() : null);
lensContextType.setLazyAuditRequest(lazyAuditRequest);
lensContextType.setRequestAudited(requestAudited);
lensContextType.setExecutionAudited(executionAudited);
lensContextType.setStats(stats);
lensContextType.setRequestMetadata(requestMetadata);
for (LensObjectDeltaOperation executedDelta : rottenExecutedDeltas) {
lensContextType.getRottenExecutedDeltas().add(executedDelta.toLensObjectDeltaOperationType());
}
return lensContextTypeContainer;
}
@SuppressWarnings({"unchecked", "raw"})
public static LensContext fromLensContextType(LensContextType lensContextType, PrismContext prismContext,
ProvisioningService provisioningService, OperationResult parentResult)
throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException {
OperationResult result = parentResult.createSubresult(DOT_CLASS + "fromLensContextType");
String focusClassString = lensContextType.getFocusClass();
if (StringUtils.isEmpty(focusClassString)) {
throw new SystemException("Focus class is undefined in LensContextType");
}
LensContext lensContext;
try {
lensContext = new LensContext(Class.forName(focusClassString), prismContext, provisioningService);
} catch (ClassNotFoundException e) {
throw new SystemException(
"Couldn't instantiate LensContext because focus or projection class couldn't be found",
e);
}
lensContext.setState(ModelState.fromModelStateType(lensContextType.getState()));
lensContext.setChannel(lensContextType.getChannel());
lensContext.setFocusContext(LensFocusContext
.fromLensFocusContextType(lensContextType.getFocusContext(), lensContext, result));
for (LensProjectionContextType lensProjectionContextType : lensContextType.getProjectionContext()) {
lensContext.addProjectionContext(LensProjectionContext
.fromLensProjectionContextType(lensProjectionContextType, lensContext, result));
}
lensContext.setDoReconciliationForAllProjections(
lensContextType.isDoReconciliationForAllProjections() != null
? lensContextType.isDoReconciliationForAllProjections() : false);
lensContext.setExecutionPhaseOnly(
lensContextType.isExecutionPhaseOnly() != null
? lensContextType.isExecutionPhaseOnly() : false);
lensContext.setProjectionWave(
lensContextType.getProjectionWave() != null ? lensContextType.getProjectionWave() : 0);
lensContext.setExecutionWave(
lensContextType.getExecutionWave() != null ? lensContextType.getExecutionWave() : 0);
lensContext
.setOptions(ModelExecuteOptions.fromModelExecutionOptionsType(lensContextType.getOptions()));
if (lensContextType.isLazyAuditRequest() != null) {
lensContext.setLazyAuditRequest(lensContextType.isLazyAuditRequest());
}
if (lensContextType.isRequestAudited() != null) {
lensContext.setRequestAudited(lensContextType.isRequestAudited());
}
if (lensContextType.isExecutionAudited() != null) {
lensContext.setExecutionAudited(lensContextType.isExecutionAudited());
}
lensContext.setStats(lensContextType.getStats());
lensContext.setRequestMetadata(lensContextType.getRequestMetadata());
for (LensObjectDeltaOperationType eDeltaOperationType : lensContextType.getRottenExecutedDeltas()) {
LensObjectDeltaOperation objectDeltaOperation = LensObjectDeltaOperation
.fromLensObjectDeltaOperationType(eDeltaOperationType, lensContext.getPrismContext());
if (objectDeltaOperation.getObjectDelta() != null) {
lensContext.fixProvisioningTypeInDelta(objectDeltaOperation.getObjectDelta(), result);
}
lensContext.rottenExecutedDeltas.add(objectDeltaOperation);
}
if (result.isUnknown()) {
result.computeStatus();
}
return lensContext;
}
protected void fixProvisioningTypeInDelta(ObjectDelta delta, OperationResult result)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException {
if (delta != null && delta.getObjectTypeClass() != null
&& (ShadowType.class.isAssignableFrom(delta.getObjectTypeClass())
|| ResourceType.class.isAssignableFrom(delta.getObjectTypeClass()))) {
getProvisioningService().applyDefinition(delta, result);
}
}
@Override
public String toString() {
return "LensContext(s=" + state + ", W(e=" + executionWave + ",p=" + projectionWave + "): "
+ focusContext + ", " + projectionContexts + ")";
}
public void setProgressListeners(Collection<ProgressListener> progressListeners) {
this.progressListeners = progressListeners;
}
public Collection<ProgressListener> getProgressListeners() {
return progressListeners;
}
@Override
public void reportProgress(ProgressInformation progress) {
if (progressListeners == null) {
return;
}
for (ProgressListener listener : progressListeners) {
listener.onProgressAchieved(this, progress);
}
}
public boolean isAbortRequested() {
if (progressListeners == null) {
return false;
}
for (ProgressListener progressListener : progressListeners) {
if (progressListener.isAbortRequested()) {
return true;
}
}
return false;
}
public Collection<ResourceShadowDiscriminator> getHistoricResourceObjects() {
if (historicResourceObjects == null) {
historicResourceObjects = new ArrayList<>();
}
return historicResourceObjects;
}
public Map<String, Long> getSequences() {
return sequences;
}
public Long getSequenceCounter(String sequenceOid) {
return sequences.get(sequenceOid);
}
public void setSequenceCounter(String sequenceOid, long counter) {
sequences.put(sequenceOid, counter);
}
public List<LensProjectionContext> getConflictingProjectionContexts() {
return conflictingProjectionContexts;
}
public void addConflictingProjectionContext(LensProjectionContext conflictingContext) {
conflictingProjectionContexts.add(conflictingContext);
}
public void clearConflictingProjectionContexts() {
conflictingProjectionContexts.clear();
}
public boolean hasExplosiveProjection() throws SchemaException {
for (LensProjectionContext projectionContext : projectionContexts) {
if (projectionContext.getVolatility() == ResourceObjectVolatilityType.EXPLOSIVE) {
return true;
}
}
return false;
}
}