com.evolveum.midpoint.wf.impl.processors.BaseModelInvocationProcessingHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.wf.impl.processors.BaseModelInvocationProcessingHelper.java

Source

/*
 * Copyright (c) 2010-2014 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;

import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.model.api.context.ModelProjectionContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.ObjectTreeDeltas;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.*;
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.wf.impl.tasks.WfTask;
import com.evolveum.midpoint.wf.impl.tasks.WfTaskController;
import com.evolveum.midpoint.wf.impl.tasks.WfTaskCreationInstruction;
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.UserType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.WfConfigurationType;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Locale;

/**
 * Helper class intended to facilitate processing of model invocation.
 * (Currently deals mainly with root job creation.)
 *
 * Functionality provided here differs from the one in JobController mainly in
 * the fact that here we know about binding to model (ModelContext, model operation task),
 * and in JobController we do not.
 *
 * @author mederly
 */
@Component
public class BaseModelInvocationProcessingHelper {

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

    @Autowired
    protected WfTaskController wfTaskController;

    @Autowired
    private WfTaskUtil wfTaskUtil;

    @Autowired
    @Qualifier("cacheRepositoryService")
    private RepositoryService repositoryService;

    /**
      * Creates a root job creation instruction.
      *
      * @param changeProcessor reference to the change processor responsible for the whole operation
      * @param modelContext model context in which the original model operation is carried out
      * @param taskFromModel task in which the original model operation is carried out
      * @param contextForRoot model context that should be put into the root task (might be different from the modelContext)
      * @return the job creation instruction
      * @throws SchemaException
      */
    public WfTaskCreationInstruction createInstructionForRoot(ChangeProcessor changeProcessor,
            ModelContext modelContext, Task taskFromModel, ModelContext contextForRoot, OperationResult result)
            throws SchemaException {

        WfTaskCreationInstruction instruction;
        if (contextForRoot != null) {
            instruction = WfTaskCreationInstruction.createModelOnly(changeProcessor, contextForRoot);
        } else {
            instruction = WfTaskCreationInstruction.createEmpty(changeProcessor);
        }

        instruction.setTaskName(determineRootTaskName(modelContext));
        instruction.setTaskObject(determineRootTaskObject(modelContext));
        instruction.setTaskOwner(taskFromModel.getOwner());
        instruction.setCreateTaskAsWaiting();

        instruction.setRequesterRef(getRequester(taskFromModel, result));
        return instruction;
    }

    /**
     * More specific version of the previous method, having contextForRoot equals to modelContext.
     */
    public WfTaskCreationInstruction createInstructionForRoot(ChangeProcessor changeProcessor,
            ModelContext modelContext, Task taskFromModel, OperationResult result) throws SchemaException {
        return createInstructionForRoot(changeProcessor, modelContext, taskFromModel, modelContext, result);
    }

    /**
     * Determines the root task name (e.g. "Workflow for adding XYZ (started 1.2.2014 10:34)")
     * TODO allow change processor to influence this name
     */
    private String determineRootTaskName(ModelContext context) {

        String operation;
        if (context.getFocusContext() != null && context.getFocusContext().getPrimaryDelta() != null
                && context.getFocusContext().getPrimaryDelta().getChangeType() != null) {
            switch (context.getFocusContext().getPrimaryDelta().getChangeType()) {
            case ADD:
                operation = "creation of";
                break;
            case DELETE:
                operation = "deletion of";
                break;
            case MODIFY:
                operation = "change of";
                break;
            default:
                throw new IllegalStateException();
            }
        } else {
            operation = "change of";
        }
        String name = MiscDataUtil.getFocusObjectName(context);

        DateTimeFormatter formatter = DateTimeFormat.forStyle("MM").withLocale(Locale.getDefault());
        String time = formatter.print(System.currentTimeMillis());

        //        DateFormat dateFormat = DateFormat.getDateTimeInstance();
        //        String time = dateFormat.format(new Date());

        return "Approving and executing " + operation + " " + name + " (started " + time + ")";
    }

    /**
     * Determines where to "hang" workflow root task - whether as a subtask of model task, or nowhere (run it as a standalone task).
     */
    private Task determineParentTaskForRoot(Task taskFromModel) {

        // this is important: if existing task which we have got from model is transient (this is usual case), we create our root task as a task without parent!
        // however, if the existing task is persistent (perhaps because the model operation executes already in the context of a workflow), we create a subtask
        // todo think heavily about this; there might be a problem if a transient task from model gets (in the future) persistent
        // -- in that case, it would not wait for its workflow-related children (but that's its problem, because children could finish even before
        // that task is switched to background)

        if (taskFromModel.isTransient()) {
            return null;
        } else {
            return taskFromModel;
        }
    }

    /**
     * To which object (e.g. user) is the task related?
     */
    private PrismObject determineRootTaskObject(ModelContext context) {
        PrismObject taskObject = context.getFocusContext().getObjectNew();
        if (taskObject != null && taskObject.getOid() == null) {
            taskObject = null;
        }
        return taskObject;
    }

    /**
     * Creates a root job, based on provided job start instruction.
     * Puts a reference to the workflow root task to the model task.
     *
     * @param rootInstruction instruction to use
     * @param taskFromModel (potential) parent task
     * @param wfConfigurationType
    * @param result
    * @return reference to a newly created job
     * @throws SchemaException
     * @throws ObjectNotFoundException
     */
    public WfTask submitRootTask(WfTaskCreationInstruction rootInstruction, Task taskFromModel,
            WfConfigurationType wfConfigurationType, OperationResult result)
            throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException {
        WfTask rootWfTask = wfTaskController.submitWfTask(rootInstruction,
                determineParentTaskForRoot(taskFromModel), wfConfigurationType, result);
        result.setBackgroundTaskOid(rootWfTask.getTask().getOid());
        wfTaskUtil.setRootTaskOidImmediate(taskFromModel, rootWfTask.getTask().getOid(), result);
        return rootWfTask;
    }

    public void logJobsBeforeStart(WfTask rootWfTask, OperationResult result)
            throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException {
        if (!LOGGER.isTraceEnabled()) {
            return;
        }

        StringBuilder sb = new StringBuilder();

        sb.append("===[ Situation just before root task starts waiting for subtasks ]===\n");
        sb.append("Root job = ").append(rootWfTask).append("; task = ").append(rootWfTask.getTask().debugDump())
                .append("\n");
        if (rootWfTask.hasModelContext()) {
            sb.append("Context in root task: \n").append(rootWfTask.retrieveModelContext(result).debugDump(1))
                    .append("\n");
        }
        List<WfTask> children = rootWfTask.listChildren(result);
        for (int i = 0; i < children.size(); i++) {
            WfTask child = children.get(i);
            sb.append("Child job #").append(i).append(" = ").append(child).append(", its task:\n")
                    .append(child.getTask().debugDump(1));
            if (child.hasModelContext()) {
                sb.append("Context in child task:\n").append(child.retrieveModelContext(result).debugDump(2));
            }
        }
        LOGGER.trace("\n{}", sb.toString());
        LOGGER.trace("Now the root task starts waiting for child tasks");
    }

    public PrismObject<UserType> getRequester(Task task, OperationResult result) {
        // let's get fresh data (not the ones read on user login)
        PrismObject<UserType> requester = null;
        try {
            requester = ((PrismObject<UserType>) repositoryService.getObject(UserType.class,
                    task.getOwner().getOid(), null, result));
        } catch (ObjectNotFoundException e) {
            LoggingUtils.logException(LOGGER, "Couldn't get data about task requester (" + task.getOwner()
                    + "), because it does not exist in repository anymore. Using cached data.", e);
            requester = task.getOwner().clone();
        } catch (SchemaException e) {
            LoggingUtils.logException(LOGGER, "Couldn't get data about task requester (" + task.getOwner()
                    + "), due to schema exception. Using cached data.", e);
            requester = task.getOwner().clone();
        }
        return requester;
    }

    public ObjectTreeDeltas extractTreeDeltasFromModelContext(ModelContext<?> modelContext) {
        ObjectTreeDeltas objectTreeDeltas = new ObjectTreeDeltas(modelContext.getPrismContext());
        if (modelContext.getFocusContext() != null && modelContext.getFocusContext().getPrimaryDelta() != null) {
            objectTreeDeltas.setFocusChange(modelContext.getFocusContext().getPrimaryDelta().clone());
        }

        for (ModelProjectionContext projectionContext : modelContext.getProjectionContexts()) {
            if (projectionContext.getPrimaryDelta() != null) {
                objectTreeDeltas.addProjectionChange(projectionContext.getResourceShadowDiscriminator(),
                        projectionContext.getPrimaryDelta());
            }
        }
        return objectTreeDeltas;
    }

}