com.evolveum.midpoint.model.impl.ModelObjectResolver.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.model.impl.ModelObjectResolver.java

Source

/*
 * 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;

import java.util.*;
import java.util.function.Function;

import javax.xml.namespace.QName;

import com.evolveum.midpoint.model.api.hooks.HookRegistry;
import com.evolveum.midpoint.model.api.hooks.ReadHook;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.task.api.TaskManager;

import com.evolveum.midpoint.wf.api.WorkflowManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.ResultHandler;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectResolver;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommonException;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
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.SecurityViolationException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;

/**
 * @author semancik
 *
 */
@Component
public class ModelObjectResolver implements ObjectResolver {

    @Autowired
    private transient ProvisioningService provisioning;

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

    @Autowired
    private transient PrismContext prismContext;

    @Autowired
    private transient TaskManager taskManager;

    @Autowired(required = false)
    private transient WorkflowManager workflowManager;

    @Autowired(required = false)
    private transient HookRegistry hookRegistry;

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

    @Override
    public <O extends ObjectType> O resolve(ObjectReferenceType ref, Class<O> expectedType,
            Collection<SelectorOptions<GetOperationOptions>> options, String contextDescription, Object task,
            OperationResult result) throws ObjectNotFoundException, SchemaException {
        String oid = ref.getOid();
        Class<?> typeClass = null;
        QName typeQName = ref.getType();
        if (typeQName != null) {
            typeClass = prismContext.getSchemaRegistry().determineCompileTimeClass(typeQName);
        }
        if (typeClass != null && expectedType.isAssignableFrom(typeClass)) {
            expectedType = (Class<O>) typeClass;
        }
        try {
            return getObject(expectedType, oid, options, (Task) task, result);
        } catch (SystemException ex) {
            throw ex;
        } catch (ObjectNotFoundException ex) {
            throw ex;
        } catch (CommonException ex) {
            LoggingUtils.logException(LOGGER, "Error resolving object with oid {}", ex, oid);
            // Add to result only a short version of the error, the details will be in subresults
            result.recordFatalError("Couldn't get object with oid '" + oid + "': " + ex.getErrorTypeMessage(), ex);
            throw new SystemException("Error resolving object with oid '" + oid + "': " + ex.getMessage(), ex);
        }
    }

    public <O extends ObjectType> PrismObject<O> resolve(PrismReferenceValue refVal, String string, Task task,
            OperationResult result) throws ObjectNotFoundException {
        return resolve(refVal, string, null, task, result);
    }

    public <O extends ObjectType> PrismObject<O> resolve(PrismReferenceValue refVal, String string,
            GetOperationOptions options, Task task, OperationResult result) throws ObjectNotFoundException {
        String oid = refVal.getOid();
        Class<?> typeClass = ObjectType.class;
        QName typeQName = refVal.getTargetType();
        if (typeQName == null && refVal.getParent() != null && refVal.getParent().getDefinition() != null) {
            PrismReferenceDefinition refDef = (PrismReferenceDefinition) refVal.getParent().getDefinition();
            typeQName = refDef.getTargetTypeName();
        }
        if (typeQName != null) {
            typeClass = prismContext.getSchemaRegistry().determineCompileTimeClass(typeQName);
        }
        return (PrismObject<O>) (getObjectSimple((Class<O>) typeClass, oid, options, task, result)).asPrismObject();
    }

    public <T extends ObjectType> T getObjectSimple(Class<T> clazz, String oid, GetOperationOptions options,
            Task task, OperationResult result) throws ObjectNotFoundException {
        try {
            return getObject(clazz, oid, SelectorOptions.createCollection(options), task, result);
        } catch (SystemException ex) {
            throw ex;
        } catch (ObjectNotFoundException ex) {
            throw ex;
        } catch (CommonException ex) {
            LoggingUtils.logException(LOGGER, "Error resolving object with oid {}", ex, oid);
            // Add to result only a short version of the error, the details will be in subresults
            result.recordFatalError("Couldn't get object with oid '" + oid + "': " + ex.getErrorTypeMessage(), ex);
            throw new SystemException("Error resolving object with oid '" + oid + "': " + ex.getMessage(), ex);
        }
    }

    public <T extends ObjectType> T getObject(Class<T> clazz, String oid,
            Collection<SelectorOptions<GetOperationOptions>> options, Task task, OperationResult result)
            throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException,
            SecurityViolationException, ExpressionEvaluationException {
        T objectType = null;
        try {
            PrismObject<T> object = null;
            ObjectTypes.ObjectManager manager = ObjectTypes.getObjectManagerForClass(clazz);
            final GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options);
            switch (manager) {
            case PROVISIONING:
                object = provisioning.getObject(clazz, oid, options, task, result);
                if (object == null) {
                    throw new SystemException("Got null result from provisioning.getObject while looking for "
                            + clazz.getSimpleName() + " with OID " + oid + "; using provisioning implementation "
                            + provisioning.getClass().getName());
                }
                break;
            case TASK_MANAGER:
                object = taskManager.getObject(clazz, oid, options, result);
                if (object == null) {
                    throw new SystemException("Got null result from taskManager.getObject while looking for "
                            + clazz.getSimpleName() + " with OID " + oid + "; using task manager implementation "
                            + taskManager.getClass().getName());
                }
                if (workflowManager != null && TaskType.class.isAssignableFrom(clazz)
                        && !GetOperationOptions.isRaw(rootOptions) && !GetOperationOptions.isNoFetch(rootOptions)) {
                    workflowManager.augmentTaskObject(object, options, task, result);
                }
                break;
            default:
                object = cacheRepositoryService.getObject(clazz, oid, options, result);
                if (object == null) {
                    throw new SystemException("Got null result from repository.getObject while looking for "
                            + clazz.getSimpleName() + " with OID " + oid + "; using repository implementation "
                            + cacheRepositoryService.getClass().getName());
                }
            }
            objectType = object.asObjectable();
            if (!clazz.isInstance(objectType)) {
                throw new ObjectNotFoundException(
                        "Bad object type returned for referenced oid '" + oid + "'. Expected '" + clazz
                                + "', but was '" + (objectType == null ? "null" : objectType.getClass()) + "'.");
            }

            if (hookRegistry != null) {
                for (ReadHook hook : hookRegistry.getAllReadHooks()) {
                    hook.invoke(object, options, task, result);
                }
            }
        } catch (SystemException | ObjectNotFoundException | CommunicationException | ConfigurationException
                | SecurityViolationException | ExpressionEvaluationException ex) {
            result.recordFatalError(ex);
            throw ex;
        } catch (RuntimeException | Error ex) {
            LoggingUtils.logUnexpectedException(LOGGER, "Error resolving object with oid {}, expected type was {}.",
                    ex, oid, clazz);
            throw new SystemException("Error resolving object with oid '" + oid + "': " + ex.getMessage(), ex);
        } finally {
            result.computeStatus();
        }

        return objectType;
    }

    @Override
    public <O extends ObjectType> void searchIterative(Class<O> type, ObjectQuery query,
            Collection<SelectorOptions<GetOperationOptions>> options, ResultHandler<O> handler, Object task,
            OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException,
            ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
        if (ObjectTypes.isClassManagedByProvisioning(type)) {
            provisioning.searchObjectsIterative(type, query, options, handler, (Task) task, parentResult);
        } else {
            cacheRepositoryService.searchObjectsIterative(type, query, handler, options, false, parentResult); // TODO pull up into resolver interface
        }
    }

    public <O extends ObjectType> Integer countObjects(Class<O> type, ObjectQuery query,
            Collection<SelectorOptions<GetOperationOptions>> options, Task task, OperationResult parentResult)
            throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException,
            SecurityViolationException, ExpressionEvaluationException {
        if (ObjectTypes.isClassManagedByProvisioning(type)) {
            return provisioning.countObjects(type, query, options, task, parentResult);
        } else {
            return cacheRepositoryService.countObjects(type, query, options, parentResult);
        }
    }

    public PrismObject<SystemConfigurationType> getSystemConfiguration(OperationResult result)
            throws ObjectNotFoundException, SchemaException {
        PrismObject<SystemConfigurationType> config = cacheRepositoryService.getObject(
                SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(), null, result);

        if (LOGGER.isTraceEnabled()) {
            if (config == null) {
                LOGGER.warn("No system configuration object");
            } else {
                LOGGER.trace("System configuration version read from repo: " + config.getVersion());
            }
        }
        return config;
    }

    public <O extends ObjectType, R extends ObjectType> PrismObject<R> searchOrgTreeWidthFirstReference(
            PrismObject<O> object, Function<PrismObject<OrgType>, ObjectReferenceType> function, String shortDesc,
            Task task, OperationResult result) throws SchemaException {
        if (object == null) {
            LOGGER.trace("No object provided. Cannost find security policy specific for an object.");
            return null;
        }
        PrismReference orgRef = object.findReference(ObjectType.F_PARENT_ORG_REF);
        if (orgRef == null) {
            return null;
        }
        List<PrismReferenceValue> orgRefValues = orgRef.getValues();
        List<PrismObject<OrgType>> orgs = new ArrayList<>();
        PrismObject<R> resultObject = null;

        for (PrismReferenceValue orgRefValue : orgRefValues) {
            if (orgRefValue != null) {

                try {
                    PrismObject<OrgType> org = resolve(orgRefValue, "resolving parent org ref", null, null, result);
                    orgs.add(org);
                    ObjectReferenceType ref = function.apply(org);

                    if (ref != null) {
                        PrismObject<R> resolvedObject;
                        try {
                            resolvedObject = resolve(ref.asReferenceValue(), shortDesc, task, result);
                        } catch (ObjectNotFoundException ex) {
                            // Just log the error, but do not fail on that. Failing would prohibit login
                            // and that may mean the misconfiguration could not be easily fixed.
                            LOGGER.warn("Cannot find object {} referenced in {} while resolving {}",
                                    orgRefValue.getOid(), object, shortDesc);
                            continue;
                        }
                        if (resolvedObject != null) {
                            if (resultObject == null) {
                                resultObject = resolvedObject;
                            } else if (!StringUtils.equals(resolvedObject.getOid(), resultObject.getOid())) {
                                throw new SchemaException("Found more than one object (" + resolvedObject + ", "
                                        + resultObject + ") while " + shortDesc);
                            }
                        }
                    }

                } catch (ObjectNotFoundException ex) {
                    // Just log the error, but do not fail on that. Failing would prohibit login
                    // and that may mean the misconfiguration could not be easily fixed.
                    LOGGER.warn("Cannot find organization {} referenced in {}", orgRefValue.getOid(), object);
                    result.muteLastSubresultError();
                }
            }
        }

        if (resultObject != null) {
            return resultObject;
        }

        // go deeper
        for (PrismObject<OrgType> org : orgs) {
            PrismObject<R> val = searchOrgTreeWidthFirstReference((PrismObject<O>) org, function, shortDesc, task,
                    result);
            if (val != null) {
                return val;
            }
        }

        return null;
    }

    public <R, O extends ObjectType> R searchOrgTreeWidthFirst(PrismObject<O> object,
            Function<PrismObject<OrgType>, R> function, Task task, OperationResult result) {
        PrismReference orgRef = object.findReference(ObjectType.F_PARENT_ORG_REF);
        if (orgRef == null) {
            return null;
        }
        List<PrismReferenceValue> orgRefValues = orgRef.getValues();
        List<PrismObject<OrgType>> orgs = new ArrayList<>();

        for (PrismReferenceValue orgRefValue : orgRefValues) {
            if (orgRefValue != null) {

                try {
                    PrismObject<OrgType> org = resolve(orgRefValue, "resolving parent org ref", null, null, result);
                    orgs.add(org);
                    R val = function.apply(org);

                    if (val != null) {
                        return val;
                    }
                } catch (ObjectNotFoundException ex) {
                    // Just log the error, but do not fail on that. Failing would prohibit login
                    // and that may mean the misconfiguration could not be easily fixed.
                    LOGGER.warn("Cannot find organization {} referenced in {}", orgRefValue.getOid(), object);
                }
            }
        }

        // go deeper
        for (PrismObject<OrgType> orgType : orgs) {
            R val = searchOrgTreeWidthFirst((PrismObject<O>) orgType, function, task, result);
            if (val != null) {
                return val;
            }
        }

        return null;
    }

    @Override
    public void resolveAllReferences(Collection<PrismContainerValue> pcvs, Object taskObject,
            OperationResult result) {
        Session session = openResolutionSession(null);
        Task task = (Task) taskObject;
        Visitor visitor = (o) -> {
            if (o instanceof PrismReferenceValue) {
                resolveReference((PrismReferenceValue) o, "resolving object reference", session, task, result);
            }
        };
        pcvs.forEach(pcv -> pcv.accept(visitor));
    }

    @Override
    public void resolveReference(PrismReferenceValue prv, String contextDescription, Session session,
            Object taskObject, OperationResult result) {
        Task task = (Task) taskObject;
        String oid = prv.getOid();
        if (oid == null) {
            // nothing to do
        } else if (prv.getObject() != null) {
            if (!session.contains(oid)) {
                session.put(oid, prv.getObject());
            }
        } else {
            PrismObject<?> object = session.get(oid);
            if (object == null) {
                try {
                    object = resolve(prv, "resolving object reference", session.getOptions(), task, result);
                    session.put(oid, object);
                } catch (Throwable t) {
                    LoggingUtils.logException(LOGGER, "Couldn't resolve reference {}", t, prv);
                    // but let's continue (hoping the issue is already recorded in the operation result)
                }
            }
            prv.setObject(object);
        }
    }
}