/* * 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.lens; import java.util.ArrayList; import java.util.Collection; import com.evolveum.midpoint.prism.ConsistencyCheckScope; import org.apache.commons.lang.Validate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.evolveum.midpoint.common.refinery.ShadowDiscriminatorObjectDelta; import com.evolveum.midpoint.model.api.ModelExecuteOptions; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.provisioning.api.ProvisioningService; import com.evolveum.midpoint.provisioning.api.ResourceObjectShadowChangeDescription; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.QNameUtil; 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.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; /** * @author semancik * */ @Component public class ContextFactory { @Autowired(required = true) PrismContext prismContext; @Autowired(required = true) private ProvisioningService provisioningService; @Autowired(required = true) Protector protector; public <F extends ObjectType> LensContext<F> createContext( Collection<ObjectDelta<? extends ObjectType>> deltas, ModelExecuteOptions options, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { ObjectDelta<F> focusDelta = null; Collection<ObjectDelta<ShadowType>> projectionDeltas = new ArrayList<ObjectDelta<ShadowType>>(deltas.size()); ObjectDelta<? extends ObjectType> confDelta = null; Class<F> focusClass = null; // Sort deltas to focus and projection deltas, check if the classes are correct; for (ObjectDelta<? extends ObjectType> delta: deltas) { Class<? extends ObjectType> typeClass = delta.getObjectTypeClass(); Validate.notNull(typeClass, "Object type class is null in "+delta); if (isFocalClass(typeClass)) { if (confDelta != null) { throw new IllegalArgumentException("Mixed configuration and focus deltas in one executeChanges invocation"); } focusClass = (Class<F>) typeClass; if (!delta.isAdd() && delta.getOid() == null) { throw new IllegalArgumentException("Delta "+delta+" does not have an OID"); } if (InternalsConfig.consistencyChecks) { // Focus delta has to be complete now with all the definition already in place delta.checkConsistence(false, true, true, ConsistencyCheckScope.THOROUGH); } else { delta.checkConsistence(ConsistencyCheckScope.MANDATORY_CHECKS_ONLY); // TODO is this necessary? Perhaps it would be sufficient to check on model/repo entry } if (focusDelta != null) { throw new IllegalStateException("More than one focus delta used in model operation"); } focusDelta = (ObjectDelta<F>) delta; } else if (isProjectionClass(typeClass)) { if (confDelta != null) { throw new IllegalArgumentException("Mixed configuration and projection deltas in one executeChanges invocation"); } projectionDeltas.add((ObjectDelta<ShadowType>) delta); } else { if (confDelta != null) { throw new IllegalArgumentException("More than one configuration delta in a single executeChanges invovation"); } confDelta = delta; } } if (confDelta != null) { focusClass = (Class<F>) confDelta.getObjectTypeClass(); } if (focusClass == null) { focusClass = determineFocusClass(); } LensContext<F> context = new LensContext<F>(focusClass, prismContext, provisioningService); context.setChannel(task.getChannel()); context.setOptions(options); context.setDoReconciliationForAllProjections(ModelExecuteOptions.isReconcile(options)); if (confDelta != null) { LensFocusContext<F> focusContext = context.createFocusContext(); focusContext.setPrimaryDelta((ObjectDelta<F>) confDelta); } else { if (focusDelta != null) { LensFocusContext<F> focusContext = context.createFocusContext(); focusContext.setPrimaryDelta(focusDelta); } for (ObjectDelta<ShadowType> projectionDelta: projectionDeltas) { LensProjectionContext projectionContext = context.createProjectionContext(); if (context.isDoReconciliationForAllProjections()) { projectionContext.setDoReconciliation(true); } projectionContext.setPrimaryDelta(projectionDelta); // We are little bit more liberal regarding projection deltas. // If the deltas represent shadows we tolerate missing attribute definitions. // We try to add the definitions by calling provisioning provisioningService.applyDefinition(projectionDelta, result); if (projectionDelta instanceof ShadowDiscriminatorObjectDelta) { ShadowDiscriminatorObjectDelta<ShadowType> shadowDelta = (ShadowDiscriminatorObjectDelta<ShadowType>)projectionDelta; projectionContext.setResourceShadowDiscriminator(shadowDelta.getDiscriminator()); } else { if (!projectionDelta.isAdd() && projectionDelta.getOid() == null) { throw new IllegalArgumentException("Delta "+projectionDelta+" does not have an OID"); } } } } // This forces context reload before the next projection context.rot(); if (InternalsConfig.consistencyChecks) context.checkConsistence(); return context; } public <F extends ObjectType, O extends ObjectType> LensContext<F> createRecomputeContext( PrismObject<O> object, ModelExecuteOptions options, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { Class<O> typeClass = object.getCompileTimeClass(); LensContext<F> context; if (isFocalClass(typeClass)) { context = createRecomputeFocusContext((Class<F>)typeClass, (PrismObject<F>) object, options, task, result); } else if (ShadowType.class.isAssignableFrom(typeClass)) { context = createRecomputeProjectionContext((PrismObject<ShadowType>) object, options, task, result); } else { throw new IllegalArgumentException("Cannot create recompute context for "+object); } context.setOptions(options); context.setLazyAuditRequest(true); return context; } public <F extends ObjectType> LensContext<F> createRecomputeFocusContext( Class<F> focusType, PrismObject<F> focus, ModelExecuteOptions options, Task task, OperationResult result) { LensContext<F> syncContext = new LensContext<F>(focusType, prismContext, provisioningService); LensFocusContext<F> focusContext = syncContext.createFocusContext(); focusContext.setLoadedObject(focus); focusContext.setOid(focus.getOid()); syncContext.setChannel(QNameUtil.qNameToUri(SchemaConstants.CHANGE_CHANNEL_RECOMPUTE)); syncContext.setDoReconciliationForAllProjections(ModelExecuteOptions.isReconcile(options)); return syncContext; } public <F extends ObjectType> LensContext<F> createRecomputeProjectionContext( PrismObject<ShadowType> shadow, ModelExecuteOptions options, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { provisioningService.applyDefinition(shadow, result); LensContext<F> syncContext = new LensContext<F>(null, prismContext, provisioningService); LensProjectionContext projectionContext = syncContext.createProjectionContext(); projectionContext.setLoadedObject(shadow); projectionContext.setOid(shadow.getOid()); projectionContext.setDoReconciliation(ModelExecuteOptions.isReconcile(options)); syncContext.setChannel(QNameUtil.qNameToUri(SchemaConstants.CHANGE_CHANNEL_RECOMPUTE)); return syncContext; } /** * Creates empty lens context for synchronization purposes, filling in only the very basic metadata (such as channel). */ public <F extends ObjectType> LensContext<F> createSyncContext(Class<F> focusClass, ResourceObjectShadowChangeDescription change) { LensContext<F> context = new LensContext<F>(focusClass, prismContext, provisioningService); context.setChannel(change.getSourceChannel()); return context; } public static <F extends ObjectType> Class<F> determineFocusClass() { // TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! return (Class<F>) UserType.class; } private static <T extends ObjectType> Class<T> checkProjectionClass(Class<T> oldProjectionClass, Class<T> newProjectionClass) { if (oldProjectionClass == null) { return newProjectionClass; } else { if (oldProjectionClass != oldProjectionClass) { throw new IllegalArgumentException("Mixed projection classes in the deltas, got both "+oldProjectionClass+" and "+oldProjectionClass); } return oldProjectionClass; } } public static <T extends ObjectType> boolean isFocalClass(Class<T> aClass) { return FocusType.class.isAssignableFrom(aClass); } public boolean isProjectionClass(Class<? extends ObjectType> aClass) { return ShadowType.class.isAssignableFrom(aClass); } }