use of com.evolveum.midpoint.model.api.hooks.HookOperationMode in project midpoint by Evolveum.
the class WfHook method invoke.
@Override
public <O extends ObjectType> HookOperationMode invoke(@NotNull ModelContext<O> context, @NotNull Task taskFromModel, @NotNull OperationResult parentResult) {
Validate.notNull(context);
Validate.notNull(taskFromModel);
Validate.notNull(parentResult);
// Generally this cannot be minor as we need the "task switched to background" flag.
// But if the hook does nothing (returns FOREGROUND flag), we mark the result
// as minor afterwards.
OperationResult result = parentResult.createSubresult(OPERATION_INVOKE);
result.addParam("taskFromModel", taskFromModel.toString());
result.addContext("model state", context.getState());
try {
WfConfigurationType wfConfigurationType = baseConfigurationHelper.getWorkflowConfiguration(context, result);
// TODO consider this if it's secure enough
if (wfConfigurationType != null && Boolean.FALSE.equals(wfConfigurationType.isModelHookEnabled())) {
LOGGER.info("Workflow model hook is disabled. Proceeding with operation execution as if everything is approved.");
result.recordSuccess();
return HookOperationMode.FOREGROUND;
}
if (context.getPartialProcessingOptions().getApprovals() == PartialProcessingTypeType.SKIP) {
LOGGER.debug("Skipping workflow processing because of the partial processing option set to SKIP");
result.recordSuccess();
return HookOperationMode.FOREGROUND;
}
// e.g. for tests, initialization is scattered through many places, so that would be too much work.
if (SchemaConstants.CHANNEL_GUI_INIT_URI.equals(context.getChannel())) {
LOGGER.debug("Skipping workflow processing because the channel is '" + SchemaConstants.CHANNEL_GUI_INIT_URI + "'.");
result.recordSuccess();
return HookOperationMode.FOREGROUND;
}
logOperationInformation(context);
HookOperationMode retval = processModelInvocation(context, wfConfigurationType, taskFromModel, result);
result.computeStatus();
if (retval == HookOperationMode.FOREGROUND) {
result.setMinor(true);
}
return retval;
} catch (RuntimeException e) {
result.recordFatalError("Couldn't process model invocation in workflow module: " + e.getMessage(), e);
throw e;
}
}
use of com.evolveum.midpoint.model.api.hooks.HookOperationMode in project midpoint by Evolveum.
the class Clockwork method runWithConflictDetection.
/**
* Runs the clockwork with the aim of detecting modify-modify conflicts on the focus object.
* It reports such states via the conflictResolutionContext parameter.
*/
<F extends ObjectType> HookOperationMode runWithConflictDetection(LensContext<F> context, ClockworkConflictResolver.Context conflictResolutionContext, Task task, OperationResult result) throws SchemaException, PolicyViolationException, ExpressionEvaluationException, ObjectNotFoundException, ObjectAlreadyExistsException, CommunicationException, ConfigurationException, SecurityViolationException {
context.setStartedIfNotYet();
context.updateSystemConfiguration(result);
LOGGER.trace("Running clockwork for context {}", context);
context.checkConsistenceIfNeeded();
context.resetClickCounter();
try {
context.reportProgress(new ProgressInformation(CLOCKWORK, ENTERING));
clockworkConflictResolver.createConflictWatcherOnStart(context);
enterCaches();
executeInitialChecks(context);
try {
while (context.getState() != ModelState.FINAL) {
context.increaseClickCounter();
HookOperationMode mode = click(context, task, result);
if (mode == HookOperationMode.BACKGROUND) {
result.setInProgress();
return mode;
} else if (mode == HookOperationMode.ERROR) {
return mode;
}
}
// One last click in FINAL state
HookOperationMode mode = click(context, task, result);
if (mode == HookOperationMode.FOREGROUND) {
// We must check inside here - before watchers are unregistered
clockworkConflictResolver.detectFocusConflicts(context, conflictResolutionContext, result);
}
return mode;
} catch (ConflictDetectedException e) {
LOGGER.debug("Clockwork conflict detected", e);
conflictResolutionContext.recordConflictException();
return HookOperationMode.FOREGROUND;
}
} finally {
operationExecutionRecorder.recordOperationExecutions(context, task, result);
clockworkConflictResolver.unregisterConflictWatcher(context);
exitCaches();
context.reportProgress(new ProgressInformation(CLOCKWORK, EXITING));
}
}
use of com.evolveum.midpoint.model.api.hooks.HookOperationMode in project midpoint by Evolveum.
the class Clockwork method run.
public <F extends ObjectType> HookOperationMode run(LensContext<F> context, Task task, OperationResult parentResult) throws SchemaException, PolicyViolationException, ExpressionEvaluationException, ObjectNotFoundException, ObjectAlreadyExistsException, CommunicationException, ConfigurationException, SecurityViolationException {
OperationResultBuilder builder = parentResult.subresult(OP_RUN);
boolean tracingRequested = startTracingIfRequested(context, task, builder, parentResult);
OperationResult result = builder.build();
// There are some parts of processing (e.g. notifications deep in provisioning module) that have no access
// to context.channel value, only to task.channel. So we have to get the two into sync. To return to the original
// state, we restore task.channel afterwards.
String originalTaskChannel = task.getChannel();
task.setChannel(context.getChannel());
ClockworkRunTraceType trace = null;
try {
trace = recordTraceAtStart(context, result);
ClockworkConflictResolver.Context conflictResolutionContext = new ClockworkConflictResolver.Context();
HookOperationMode mode = runWithConflictDetection(context, conflictResolutionContext, task, result);
return clockworkConflictResolver.resolveFocusConflictIfPresent(context, conflictResolutionContext, mode, task, result);
} catch (CommonException t) {
result.recordFatalError(t.getMessage(), t);
throw t;
} finally {
task.setChannel(originalTaskChannel);
result.computeStatusIfUnknown();
recordTraceAtEnd(context, trace, result);
if (tracingRequested) {
tracer.storeTrace(task, result, parentResult);
}
}
}
use of com.evolveum.midpoint.model.api.hooks.HookOperationMode in project midpoint by Evolveum.
the class AbstractWfTestPolicy method executeTest.
protected <F extends FocusType> OperationResult executeTest(TestDetails testDetails, int expectedSubTaskCount) throws Exception {
// GIVEN
prepareNotifications();
dummyAuditService.clear();
Task opTask = getTestTask();
boolean USE_FULL_TRACING = false;
// noinspection ConstantConditions
if (USE_FULL_TRACING) {
setModelAndWorkflowLoggingTracing(opTask);
} else {
testDetails.setTracing(opTask);
}
opTask.setOwner(userAdministrator);
OperationResult result = opTask.getResult();
LensContext<F> modelContext = testDetails.createModelContext(result);
displayDumpable("Model context at test start", modelContext);
// this has problems with deleting assignments by ID
// assertFocusModificationSanity(modelContext);
// WHEN
HookOperationMode mode;
clockworkMedic.enterModelMethod(true);
try {
mode = clockwork.run(modelContext, opTask, result);
} finally {
clockworkMedic.exitModelMethod(true);
}
// THEN
displayDumpable("Model context after first clockwork.run", modelContext);
assertEquals("Unexpected state of the context", ModelState.PRIMARY, modelContext.getState());
assertEquals("Wrong mode after clockwork.run in " + modelContext.getState(), HookOperationMode.BACKGROUND, mode);
opTask.refresh(result);
display("Model task after first clockwork.run", opTask);
CaseType rootCase = testHelper.getRootCase(result);
List<CaseType> subcases = miscHelper.getSubcases(rootCase, result);
CaseType case0 = WfTestHelper.findAndRemoveCase0(subcases);
assertEquals("Incorrect number of subtasks", expectedSubTaskCount, subcases.size());
final Collection<SelectorOptions<GetOperationOptions>> options1 = schemaService.getOperationOptionsBuilder().item(T_PARENT, F_OBJECT_REF).resolve().item(T_PARENT, F_TARGET_REF).resolve().item(F_ASSIGNEE_REF).resolve().item(F_ORIGINAL_ASSIGNEE_REF).resolve().item(T_PARENT, F_REQUESTOR_REF).resolve().build();
List<CaseWorkItemType> workItems = new // to assure modifiable result list
ArrayList<>(modelService.searchContainers(CaseWorkItemType.class, getOpenItemsQuery(), options1, opTask, result));
displayDumpable("changes by state after first clockwork run", approvalsManager.getChangesByState(rootCase, modelInteractionService, prismContext, opTask, result));
testDetails.afterFirstClockworkRun(rootCase, case0, subcases, workItems, opTask, result);
if (testDetails.executeImmediately()) {
if (case0 != null) {
testHelper.waitForCaseClose(case0, 20000);
}
displayDumpable("changes by state after case0 finishes", approvalsManager.getChangesByState(rootCase, modelInteractionService, prismContext, opTask, result));
testDetails.afterCase0Finishes(rootCase, opTask, result);
}
for (int i = 0; i < subcases.size(); i++) {
CaseType subcase = subcases.get(i);
PrismProperty<ObjectTreeDeltasType> deltas = subcase.asPrismObject().findProperty(ItemPath.create(F_APPROVAL_CONTEXT, F_DELTAS_TO_APPROVE));
assertNotNull("There are no modifications in subcase #" + i + ": " + subcase, deltas);
assertEquals("Incorrect number of modifications in subcase #" + i + ": " + subcase, 1, deltas.getRealValues().size());
// todo check correctness of the modification?
// now check the workflow state
String caseOid = subcase.getOid();
List<CaseWorkItemType> caseWorkItems = getWorkItemsForCase(caseOid, null, result);
assertFalse("work item not found", caseWorkItems.isEmpty());
for (CaseWorkItemType caseWorkItem : caseWorkItems) {
Boolean approve = testDetails.decideOnApproval(caseWorkItem);
if (approve != null) {
caseManager.completeWorkItem(WorkItemId.create(caseOid, caseWorkItem.getId()), new AbstractWorkItemOutputType(prismContext).outcome(ApprovalUtils.toUri(approve)), null, opTask, result);
login(userAdministrator);
break;
}
}
}
// alternative way of approvals executions
if (CollectionUtils.isNotEmpty(testDetails.getApprovalSequence())) {
List<ApprovalInstruction> instructions = new ArrayList<>(testDetails.getApprovalSequence());
while (!instructions.isEmpty()) {
List<CaseWorkItemType> currentWorkItems = modelService.searchContainers(CaseWorkItemType.class, getOpenItemsQuery(), options1, opTask, result);
boolean matched = false;
Collection<ApprovalInstruction> instructionsToConsider = testDetails.strictlySequentialApprovals() ? singleton(instructions.get(0)) : instructions;
main: for (ApprovalInstruction approvalInstruction : instructionsToConsider) {
for (CaseWorkItemType workItem : currentWorkItems) {
if (approvalInstruction.matches(workItem)) {
if (approvalInstruction.beforeApproval != null) {
approvalInstruction.beforeApproval.run();
}
login(getUserFromRepo(approvalInstruction.approverOid));
System.out.println("Completing work item " + WorkItemId.of(workItem) + " using " + approvalInstruction);
caseManager.completeWorkItem(WorkItemId.of(workItem), new AbstractWorkItemOutputType(prismContext).outcome(ApprovalUtils.toUri(approvalInstruction.approval)).comment(approvalInstruction.comment), null, opTask, result);
if (approvalInstruction.afterApproval != null) {
approvalInstruction.afterApproval.run();
}
login(userAdministrator);
matched = true;
instructions.remove(approvalInstruction);
break main;
}
}
}
if (!matched) {
fail("None of approval instructions " + instructionsToConsider + " matched any of current work items: " + currentWorkItems);
}
}
}
CaseType rootCaseAfter = testHelper.waitForCaseClose(rootCase, 60000);
subcases = miscHelper.getSubcases(rootCaseAfter, result);
WfTestHelper.findAndRemoveCase0(subcases);
displayDumpable("changes by state after root case finishes", approvalsManager.getChangesByState(rootCaseAfter, modelInteractionService, prismContext, opTask, result));
testDetails.afterRootCaseFinishes(rootCaseAfter, subcases, opTask, result);
notificationManager.setDisabled(true);
// Check audit
displayDumpable("Audit", dummyAuditService);
displayDumpable("Output context", modelContext);
return result;
}
use of com.evolveum.midpoint.model.api.hooks.HookOperationMode in project midpoint by Evolveum.
the class TestClockwork method assignAccountToJackAsync.
private void assignAccountToJackAsync(boolean serialize) throws Exception {
given();
Task task = getTestTask();
OperationResult result = task.getResult();
assertNoDummyAccount(ACCOUNT_JACK_DUMMY_USERNAME);
LensContext<UserType> context = createJackAssignAccountContext(result);
assertFocusModificationSanity(context);
mockClockworkHook.reset();
mockClockworkHook.setRecord(true);
mockClockworkHook.setAsynchronous(true);
rememberCounter(InternalCounters.SHADOW_FETCH_OPERATION_COUNT);
// WHEN
when();
context.setStartedIfNotYet();
while (context.getState() != ModelState.FINAL) {
display("CLICK START: " + context.getState());
HookOperationMode mode = clockwork.click(context, task, result);
display("CLICK END: " + context.getState());
assertNotSame("Unexpected INITIAL state of the context", context.getState(), ModelState.INITIAL);
assertEquals("Wrong mode after click in " + context.getState(), HookOperationMode.BACKGROUND, mode);
assertCounterIncrement(InternalCounters.SHADOW_FETCH_OPERATION_COUNT, 0);
if (serialize) {
displayDumpable("Context before serialization", context);
LensContextType lensContextType = context.toLensContextType();
String xml = prismContext.xmlSerializer().serializeRealValue(lensContextType, SchemaConstants.C_MODEL_CONTEXT);
displayValue("Serialized form", xml);
LensContextType unmarshalledContainer = prismContext.parserFor(xml).xml().parseRealValue(LensContextType.class);
context = LensContext.fromLensContextBean(unmarshalledContainer, task, result);
displayValue("Context after deserialization", context.debugDump());
context.checkConsistence();
}
}
then();
mockClockworkHook.setRecord(false);
assertCounterIncrement(InternalCounters.SHADOW_FETCH_OPERATION_COUNT, 0);
assertJackAssignAccountContext(context);
assertJackAccountShadow(context);
}
Aggregations