/*
* 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.wf.impl.processors.primary.aspect;
import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.model.common.SystemObjectCache;
import com.evolveum.midpoint.model.common.expression.ExpressionUtil;
import com.evolveum.midpoint.model.common.expression.ExpressionVariables;
import com.evolveum.midpoint.model.common.mapping.MappingFactory;
import com.evolveum.midpoint.model.impl.expr.ExpressionEnvironment;
import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder;
import com.evolveum.midpoint.model.impl.lens.LensContext;
import com.evolveum.midpoint.model.impl.util.Utils;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismReferenceValue;
import com.evolveum.midpoint.prism.marshaller.QueryConvertor;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterExit;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.ObjectTreeDeltas;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.impl.messages.ProcessEvent;
import com.evolveum.midpoint.wf.impl.processes.ProcessInterfaceFinder;
import com.evolveum.midpoint.wf.impl.processes.itemApproval.ApprovalSchemaHelper;
import com.evolveum.midpoint.wf.impl.processes.itemApproval.ItemApprovalProcessInterface;
import com.evolveum.midpoint.wf.impl.processes.itemApproval.ReferenceResolver;
import com.evolveum.midpoint.wf.impl.processes.itemApproval.RelationResolver;
import com.evolveum.midpoint.wf.impl.processors.BaseConfigurationHelper;
import com.evolveum.midpoint.wf.impl.processors.BaseModelInvocationProcessingHelper;
import com.evolveum.midpoint.wf.impl.processors.primary.PcpWfTask;
import com.evolveum.midpoint.wf.impl.processors.primary.PrimaryChangeProcessor;
import com.evolveum.midpoint.wf.impl.tasks.WfTaskUtil;
import com.evolveum.midpoint.wf.impl.util.MiscDataUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.PostConstruct;
import javax.xml.namespace.QName;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author mederly
*/
public abstract class BasePrimaryChangeAspect implements PrimaryChangeAspect, BeanNameAware {
private static final Trace LOGGER = TraceManager.getTrace(BasePrimaryChangeAspect.class);
private String beanName;
@Autowired
@Qualifier("cacheRepositoryService")
protected RepositoryService repositoryService;
@Autowired
protected WfTaskUtil wfTaskUtil;
@Autowired
protected PrimaryChangeProcessor changeProcessor;
@Autowired
protected PrimaryChangeAspectHelper primaryChangeAspectHelper;
@Autowired
protected ProcessInterfaceFinder processInterfaceFinder;
@Autowired
protected BaseConfigurationHelper baseConfigurationHelper;
@Autowired
protected PrismContext prismContext;
@Autowired
protected ItemApprovalProcessInterface itemApprovalProcessInterface;
@Autowired
protected MiscDataUtil miscDataUtil;
@Autowired
protected BaseModelInvocationProcessingHelper baseModelInvocationProcessingHelper;
@Autowired
private SystemObjectCache systemObjectCache;
@Autowired
private MappingFactory mappingFactory;
@Autowired
protected ApprovalSchemaHelper approvalSchemaHelper;
@PostConstruct
public void init() {
changeProcessor.registerChangeAspect(this, isFirst());
}
protected boolean isFirst() {
return false;
}
@Override
public String getBeanName() {
return beanName;
}
public void setBeanName(String name) {
this.beanName = name;
}
@Override
public ObjectTreeDeltas prepareDeltaOut(ProcessEvent event, PcpWfTask pcpJob, OperationResult result) throws SchemaException {
return primaryChangeAspectHelper.prepareDeltaOut(event, pcpJob, result);
}
@Override
public List<ObjectReferenceType> prepareApprovedBy(ProcessEvent event, PcpWfTask job, OperationResult result) {
return processInterfaceFinder.getProcessInterface(event.getVariables()).prepareApprovedBy(event, job, result);
}
public PrimaryChangeProcessor getChangeProcessor() {
return changeProcessor;
}
@Override
public boolean isEnabledByDefault() {
return false; // overriden in selected aspects
}
@Override
public boolean isEnabled(PrimaryChangeProcessorConfigurationType processorConfigurationType) {
return primaryChangeAspectHelper.isEnabled(processorConfigurationType, this);
}
public RelationResolver createRelationResolver(ObjectType object, OperationResult result) {
return createRelationResolver(object != null ? object.asPrismObject() : null, result);
}
private <O extends ObjectType, F extends ObjectType> List<ObjectReferenceType> resolveReferenceFromFilter(Class<O> clazz, SearchFilterType filter, String sourceDescription,
LensContext<F> lensContext, Task task, OperationResult result)
throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException {
ExpressionEnvironment<F> env = new ExpressionEnvironment<>();
env.setLensContext(lensContext);
env.setCurrentResult(result);
env.setCurrentTask(task);
ModelExpressionThreadLocalHolder.pushExpressionEnvironment(env);
try {
PrismObject<SystemConfigurationType> systemConfiguration = systemObjectCache.getSystemConfiguration(result);
ExpressionVariables variables = Utils.getDefaultExpressionVariables(getFocusObjectable(lensContext), null, null, systemConfiguration.asObjectable());
ObjectFilter origFilter = QueryConvertor.parseFilter(filter, clazz, prismContext);
ObjectFilter evaluatedFilter = ExpressionUtil
.evaluateFilterExpressions(origFilter, variables, mappingFactory.getExpressionFactory(), prismContext, " evaluating approverRef filter expression ", task, result);
if (evaluatedFilter == null) {
throw new SchemaException("Filter could not be evaluated in approverRef in "+sourceDescription+"; original filter = "+origFilter);
}
SearchResultList<PrismObject<O>> targets = repositoryService.searchObjects(clazz, ObjectQuery.createObjectQuery(evaluatedFilter), null, result);
return targets.stream()
.map(ObjectTypeUtil::createObjectRef)
.collect(Collectors.toList());
} finally {
ModelExpressionThreadLocalHolder.popExpressionEnvironment();
}
}
private FocusType getFocusObjectable(LensContext<?> lensContext) {
if (lensContext.getFocusContext() == null) {
return null; // shouldn't occur, probably
}
PrismObject<?> focus = lensContext.getFocusContext().getObjectAny();
return focus != null ? (FocusType) focus.asObjectable() : null;
}
public ReferenceResolver createReferenceResolver(ModelContext modelContext, Task taskFromModel, OperationResult result) {
return (ref, sourceDescription) -> {
if (ref == null) {
return Collections.emptyList();
} else if (ref.getOid() != null) {
return Collections.singletonList(ref.clone());
} else {
Class<? extends ObjectType> clazz;
if (ref.getType() != null) {
clazz = prismContext.getSchemaRegistry().determineCompileTimeClass(ref.getType());
if (clazz == null) {
throw new SchemaException("Cannot determine type from " + ref.getType() + " in approver reference in " + sourceDescription);
}
} else {
throw new SchemaException("Missing type in target reference in " + sourceDescription);
}
return resolveReferenceFromFilter(clazz, ref.getFilter(), sourceDescription, (LensContext) modelContext, taskFromModel, result);
}
};
}
public RelationResolver createRelationResolver(PrismObject<?> object, OperationResult result) {
return relations -> {
if (object == null || object.getOid() == null || relations.isEmpty()) {
return Collections.emptyList();
}
S_AtomicFilterExit q = QueryBuilder.queryFor(FocusType.class, prismContext).none();
for (QName approverRelation : relations) {
PrismReferenceValue approverReference = new PrismReferenceValue(object.getOid());
approverReference.setRelation(QNameUtil.qualifyIfNeeded(approverRelation, SchemaConstants.NS_ORG));
q = q.or().item(FocusType.F_ROLE_MEMBERSHIP_REF).ref(approverReference);
}
ObjectQuery query = q.build();
LOGGER.trace("Looking for approvers for {} using query:\n{}", object, DebugUtil.debugDumpLazily(query));
List<PrismObject<FocusType>> objects = null;
try {
objects = repositoryService.searchObjects(FocusType.class, query, null, result);
} catch (SchemaException e) {
throw new SystemException("Couldn't retrieve approvers for " + object + ": " + e.getMessage(), e);
}
Set<PrismObject<FocusType>> distinctObjects = new HashSet<>(objects);
LOGGER.trace("Found {} approver(s): {}", distinctObjects.size(), DebugUtil.toStringLazily(distinctObjects));
return distinctObjects.stream()
.map(ObjectTypeUtil::createObjectRef)
.collect(Collectors.toList());
};
}
protected List<ObjectReferenceType> findApproversByReference(PrismObject<?> target, ApprovalPolicyActionType action,
OperationResult result) throws SchemaException {
return createRelationResolver(target, result)
.getApprovers(action.getApproverRelation());
}
}