Skip to content

Commit

Permalink
Add midpoint.executeChangesAsynchronously method
Browse files Browse the repository at this point in the history
This is to allow easy execution of arbitrary deltas (via single
model.executeChanges) call within a background task.
  • Loading branch information
mederly committed Apr 25, 2018
1 parent 023fe19 commit cb18a35
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 4 deletions.
Expand Up @@ -362,6 +362,7 @@ public abstract class SchemaConstants {
public static final QName MODEL_EXTENSION_SEARCH_OPTIONS = new QName(NS_MODEL_EXTENSION, "searchOptions");
public static final QName MODEL_EXTENSION_ITERATION_METHOD = new QName(NS_MODEL_EXTENSION, "iterationMethod");
public static final QName MODEL_EXTENSION_OBJECT_DELTA = new QName(NS_MODEL_EXTENSION, "objectDelta");
public static final QName MODEL_EXTENSION_OBJECT_DELTAS = new QName(NS_MODEL_EXTENSION, "objectDeltas");
public static final QName MODEL_EXTENSION_WORKER_THREADS = new QName(NS_MODEL_EXTENSION, "workerThreads");
public static final QName MODEL_EXTENSION_OPTION_RAW = new QName(NS_MODEL_EXTENSION, "optionRaw");
public static final QName MODEL_EXTENSION_EXECUTE_OPTIONS = new QName(NS_MODEL_EXTENSION, "executeOptions");
Expand Down
Expand Up @@ -192,6 +192,17 @@
</xsd:annotation>
</xsd:element>

<xsd:element name="objectDeltas" type="t:ObjectDeltaType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
<xsd:appinfo>
<a:maxOccurs>unbounded</a:maxOccurs>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>

<xsd:element name="executeOptions" type="c:ModelExecuteOptionsType">
<xsd:annotation>
<xsd:documentation>
Expand Down
Expand Up @@ -48,4 +48,5 @@ public class ModelPublicConstants {
public static final String PARTITIONED_RECONCILIATION_TASK_HANDLER_URI_3 = PARTITIONED_RECONCILIATION_TASK_HANDLER_URI + "#3";
public static final String SCRIPT_EXECUTION_TASK_HANDLER_URI = SchemaConstants.NS_MODEL + "/scripting/handler-3";
public static final String ITERATIVE_SCRIPT_EXECUTION_TASK_HANDLER_URI = SchemaConstants.NS_MODEL + "/iterative-scripting/handler-3";
public static final String EXECUTE_DELTAS_TASK_HANDLER_URI = SchemaConstants.NS_MODEL + "/execute-deltas/handler-3";
}
Expand Up @@ -1098,6 +1098,12 @@ TaskType submitTaskFromTemplate(String templateTaskOid, Map<QName, Object> exten
throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException,
ConfigurationException, ExpressionEvaluationException, ObjectAlreadyExistsException, PolicyViolationException;

TaskType executeChangesAsynchronously(Collection<ObjectDelta<?>> deltas, ModelExecuteOptions options, String templateTaskOid) throws SecurityViolationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException, ObjectAlreadyExistsException, PolicyViolationException;

TaskType executeChangesAsynchronously(Collection<ObjectDelta<?>> deltas, ModelExecuteOptions options,
String templateTaskOid, Task opTask,
OperationResult result) throws SecurityViolationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException, ObjectAlreadyExistsException, PolicyViolationException;

String translate(LocalizableMessage message);

String translate(LocalizableMessageType message);
Expand Down
@@ -0,0 +1,110 @@
/*
* 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.controller;

import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.ModelPublicConstants;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.schema.DeltaConvertor;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.*;
import com.evolveum.midpoint.util.exception.CommonException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collection;

/**
* Temporary/experimental implementation.
*
* @author mederly
*/

@Component
public class ExecuteDeltasTaskHandler implements TaskHandler {

private static final Trace LOGGER = TraceManager.getTrace(ExecuteDeltasTaskHandler.class);

private static final String DOT_CLASS = ExecuteDeltasTaskHandler.class.getName() + ".";

@Autowired private TaskManager taskManager;
@Autowired private PrismContext prismContext;
@Autowired private ModelService modelService;

@Override
public TaskRunResult run(Task task) {

OperationResult result = task.getResult().createSubresult(DOT_CLASS + "run");
TaskRunResult runResult = new TaskRunResult();

Collection<ObjectDeltaType> deltas;
PrismProperty<ObjectDeltaType> deltasProperty = task.getExtensionProperty(SchemaConstants.MODEL_EXTENSION_OBJECT_DELTAS);
if (deltasProperty == null || deltasProperty.isEmpty()) {
PrismProperty<ObjectDeltaType> deltaProperty = task.getExtensionProperty(SchemaConstants.MODEL_EXTENSION_OBJECT_DELTA);
if (deltaProperty == null || deltaProperty.isEmpty()) {
throw new IllegalArgumentException("No deltas to execute");
} else {
deltas = deltaProperty.getRealValues();
}
} else {
deltas = deltasProperty.getRealValues();
}
PrismProperty<ModelExecuteOptionsType> optionsProperty = task.getExtensionProperty(SchemaConstants.MODEL_EXTENSION_EXECUTE_OPTIONS);
ModelExecuteOptions options = optionsProperty != null ?
ModelExecuteOptions.fromModelExecutionOptionsType(optionsProperty.getRealValue()) : null;

try {
Collection<ObjectDelta<?>> objectDeltas = new ArrayList<>();
for (ObjectDeltaType deltaBean : deltas) {
objectDeltas.add(DeltaConvertor.createObjectDelta(deltaBean, prismContext));
}
//noinspection unchecked
modelService.executeChanges((Collection) objectDeltas, options, task, result);
result.computeStatusIfUnknown();
runResult.setRunResultStatus(TaskRunResult.TaskRunResultStatus.FINISHED);
} catch (CommonException | RuntimeException e) {
String message = "An exception occurred when executing changes, in task " + task;
LoggingUtils.logUnexpectedException(LOGGER, message, e);
result.recordFatalError(message, e);
runResult.setRunResultStatus(TaskRunResult.TaskRunResultStatus.PERMANENT_ERROR);
}
task.getResult().recomputeStatus();
runResult.setOperationResult(task.getResult());
return runResult;
}

@Override
public String getCategoryName(Task task) {
return TaskCategory.UTIL;
}

@PostConstruct
private void initialize() {
taskManager.registerHandler(ModelPublicConstants.EXECUTE_DELTAS_TASK_HANDLER_URI, this);
}
}
Expand Up @@ -20,10 +20,7 @@
import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchema;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.ModelInteractionService;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.model.api.WorkflowService;
import com.evolveum.midpoint.model.api.*;
import com.evolveum.midpoint.model.api.context.AssignmentPath;
import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.model.api.context.ModelElementContext;
Expand Down Expand Up @@ -1570,6 +1567,60 @@ public ExtensionType collectExtensions(AssignmentPathType path, int startAt)
ConfigurationException, ExpressionEvaluationException {
return AssignmentPath.collectExtensions(path, startAt, modelService, getCurrentTask(), getCurrentResult());
}
@Override
public TaskType executeChangesAsynchronously(Collection<ObjectDelta<?>> deltas, ModelExecuteOptions options,
String templateTaskOid) throws SecurityViolationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException, ObjectAlreadyExistsException, PolicyViolationException {
return executeChangesAsynchronously(deltas, options, templateTaskOid, getCurrentTask(), getCurrentResult());
}

@Override
public TaskType executeChangesAsynchronously(Collection<ObjectDelta<?>> deltas, ModelExecuteOptions options,
String templateTaskOid, Task opTask, OperationResult result) throws SecurityViolationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException, ObjectAlreadyExistsException, PolicyViolationException {
MidPointPrincipal principal = securityContextManager.getPrincipal();
if (principal == null) {
throw new SecurityViolationException("No current user");
}
TaskType newTask;
if (templateTaskOid != null) {
newTask = modelService.getObject(TaskType.class, templateTaskOid,
getDefaultGetOptionCollection(), opTask, result).asObjectable();
} else {
newTask = new TaskType(prismContext);
newTask.setName(PolyStringType.fromOrig("Execute changes"));
newTask.setRecurrence(TaskRecurrenceType.SINGLE);
}
newTask.setName(PolyStringType.fromOrig(newTask.getName().getOrig() + " " + (int) (Math.random()*10000)));
newTask.setOid(null);
newTask.setTaskIdentifier(null);
newTask.setOwnerRef(createObjectRef(principal.getUser()));
newTask.setExecutionStatus(RUNNABLE);
newTask.setHandlerUri(ModelPublicConstants.EXECUTE_DELTAS_TASK_HANDLER_URI);
if (deltas.isEmpty()) {
throw new IllegalArgumentException("No deltas to execute");
}
List<ObjectDeltaType> deltasBeans = new ArrayList<>();
for (ObjectDelta<?> delta : deltas) {
//noinspection unchecked
deltasBeans.add(DeltaConvertor.toObjectDeltaType((ObjectDelta<? extends com.evolveum.prism.xml.ns._public.types_3.ObjectType>) delta));
}
//noinspection unchecked
PrismPropertyDefinition<ObjectDeltaType> deltasDefinition = prismContext.getSchemaRegistry()
.findPropertyDefinitionByElementName(SchemaConstants.MODEL_EXTENSION_OBJECT_DELTAS);
PrismProperty<ObjectDeltaType> deltasProperty = deltasDefinition.instantiate();
deltasProperty.setRealValues(deltasBeans.toArray(new ObjectDeltaType[0]));
newTask.asPrismObject().addExtensionItem(deltasProperty);
if (options != null) {
//noinspection unchecked
PrismPropertyDefinition<ModelExecuteOptionsType> optionsDefinition = prismContext.getSchemaRegistry()
.findPropertyDefinitionByElementName(SchemaConstants.MODEL_EXTENSION_EXECUTE_OPTIONS);
PrismProperty<ModelExecuteOptionsType> optionsProperty = optionsDefinition.instantiate();
optionsProperty.setRealValue(options.toModelExecutionOptionsType());
newTask.asPrismObject().addExtensionItem(optionsProperty);
}
ObjectDelta<TaskType> taskAddDelta = ObjectDelta.createAddDelta(newTask.asPrismObject());
modelService.executeChanges(singleton(taskAddDelta), null, opTask, result);
return newTask;
}

@Override
public TaskType submitTaskFromTemplate(String templateTaskOid, List<Item<?, ?>> extensionItems)
Expand Down
@@ -0,0 +1,55 @@
<!--
~ Copyright (c) 2010-2018 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.
-->

<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3"
xmlns:scext="http://midpoint.evolveum.com/xml/ns/public/model/scripting/extension-3"
xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<name>Execute arbitrary script</name>
<executionStatus>runnable</executionStatus>
<extension>
<scext:executeScript>
<s:action>
<s:type>execute-script</s:type>
<s:parameter>
<s:name>script</s:name>
<c:value xsi:type="c:ScriptExpressionEvaluatorType">
<c:code>
import com.evolveum.midpoint.wf.impl.processes.common.SpringApplicationContextHolder
import com.evolveum.midpoint.model.api.AccessCertificationService
import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder
SpringApplicationContextHolder.getBean("modelController", AccessCertificationService.class)
.closeCurrentStage("some-oid", 1, ModelExpressionThreadLocalHolder.getCurrentTask(), ModelExpressionThreadLocalHolder.getCurrentResult())
</c:code>
</c:value>
</s:parameter>
<s:parameter>
<s:name>forWholeInput</s:name>
<c:value>true</c:value>
</s:parameter>
</s:action>
</scext:executeScript>
</extension>
<ownerRef oid="00000000-0000-0000-0000-000000000002" />
<category>BulkActions</category>
<handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/scripting/handler-3</handlerUri>
<recurrence>single</recurrence>
</task>
56 changes: 56 additions & 0 deletions samples/tasks/bulk-actions/script-async-execution.xml
@@ -0,0 +1,56 @@
<!--
~ Copyright (c) 2010-2018 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.
-->

<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3"
xmlns:scext="http://midpoint.evolveum.com/xml/ns/public/model/scripting/extension-3"
xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<name>Execute arbitrary script</name>
<executionStatus>runnable</executionStatus>
<extension>
<scext:executeScript>
<s:action>
<s:type>execute-script</s:type>
<s:parameter>
<s:name>script</s:name>
<c:value xsi:type="c:ScriptExpressionEvaluatorType">
<c:code>
import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder
import com.evolveum.midpoint.xml.ns._public.common.common_3.*
def delta = DeltaBuilder.deltaFor(UserType.class, prismContext)
.item(UserType.F_DESCRIPTION).replace('hi')
.asObjectDelta('00000000-0000-0000-0000-000000000002')
midpoint.executeChangesAsynchronously([delta], null, null)
</c:code>
</c:value>
</s:parameter>
<s:parameter>
<s:name>forWholeInput</s:name>
<c:value>true</c:value>
</s:parameter>
</s:action>
</scext:executeScript>
</extension>
<ownerRef oid="00000000-0000-0000-0000-000000000002" />
<category>BulkActions</category>
<handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/scripting/handler-3</handlerUri>
<recurrence>single</recurrence>
</task>

0 comments on commit cb18a35

Please sign in to comment.