/* * Copyright (c) 2010-2013 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.ArrayList; import java.util.Collection; import java.util.List; 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.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.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReference; import com.evolveum.midpoint.prism.PrismReferenceDefinition; import com.evolveum.midpoint.prism.PrismReferenceValue; 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.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(required = true) private transient ProvisioningService provisioning; @Autowired(required = true) @Qualifier("cacheRepositoryService") private transient RepositoryService cacheRepositoryService; @Autowired(required = true) 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.getOperationResultMessage(), 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.getOperationResultMessage(), 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 { 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 ex) { result.recordFatalError(ex); throw ex; } catch (ObjectNotFoundException ex) { result.recordFatalError(ex); throw ex; } catch (CommunicationException e) { result.recordFatalError(e); throw e; } catch (SchemaException e) { result.recordFatalError(e); throw e; } catch (ConfigurationException e) { result.recordFatalError(e); throw e; } catch (SecurityViolationException e) { result.recordFatalError(e); throw e; } catch (RuntimeException ex) { LoggingUtils.logException(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; } 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 { 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 { if (ObjectTypes.isClassManagedByProvisioning(type)) { return provisioning.countObjects(type, query, options, task, parentResult); } else { return cacheRepositoryService.countObjects(type, query, 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 { 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<OrgType>>(); 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); } } } 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<PrismObject<OrgType>>(); 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; } }