/** * Copyright (c) 2015-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.provisioning.impl; import com.evolveum.midpoint.common.refinery.*; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance; import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.StateReporter; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.CapabilitiesType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CapabilityType; import org.jetbrains.annotations.NotNull; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * @author semancik * */ public class ProvisioningContext extends StateReporter { private static final Trace LOGGER = TraceManager.getTrace(ProvisioningContext.class); private ResourceManager resourceManager; private OperationResult parentResult; private Collection<SelectorOptions<GetOperationOptions>> getOperationOptions; private PrismObject<ShadowType> originalShadow; private ResourceShadowDiscriminator shadowCoordinates; private Collection<QName> additionalAuxiliaryObjectClassQNames; private boolean useRefinedDefinition = true; private RefinedObjectClassDefinition objectClassDefinition; private ResourceType resource; private Map<Class<? extends CapabilityType>,ConnectorInstance> connectorMap; private RefinedResourceSchema refinedSchema; public ProvisioningContext(ResourceManager resourceManager, OperationResult parentResult) { this.resourceManager = resourceManager; this.parentResult = parentResult; } public void setResourceOid(String resourceOid) { super.setResourceOid(resourceOid); this.resource = null; this.connectorMap = null; this.refinedSchema = null; } public Collection<SelectorOptions<GetOperationOptions>> getGetOperationOptions() { return getOperationOptions; } public void setGetOperationOptions(Collection<SelectorOptions<GetOperationOptions>> getOperationOptions) { this.getOperationOptions = getOperationOptions; } public ResourceShadowDiscriminator getShadowCoordinates() { return shadowCoordinates; } public void setShadowCoordinates(ResourceShadowDiscriminator shadowCoordinates) { this.shadowCoordinates = shadowCoordinates; } public PrismObject<ShadowType> getOriginalShadow() { return originalShadow; } public void setOriginalShadow(PrismObject<ShadowType> originalShadow) { this.originalShadow = originalShadow; } public Collection<QName> getAdditionalAuxiliaryObjectClassQNames() { return additionalAuxiliaryObjectClassQNames; } public void setAdditionalAuxiliaryObjectClassQNames(Collection<QName> additionalAuxiliaryObjectClassQNames) { this.additionalAuxiliaryObjectClassQNames = additionalAuxiliaryObjectClassQNames; } public boolean isUseRefinedDefinition() { return useRefinedDefinition; } public void setUseRefinedDefinition(boolean useRefinedDefinition) { this.useRefinedDefinition = useRefinedDefinition; } public ResourceType getResource() throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { if (resource == null) { if (getResourceOid() == null) { throw new SchemaException("Null resource OID "+getDesc()); } GetOperationOptions options = GetOperationOptions.createReadOnly(); resource = resourceManager.getResource(getResourceOid(), options, parentResult).asObjectable(); updateResourceName(); } return resource; } private void updateResourceName() { if (resource != null && resource.getName() != null) { super.setResourceName(resource.getName().getOrig()); } } public RefinedResourceSchema getRefinedSchema() throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { if (refinedSchema == null) { refinedSchema = ProvisioningUtil.getRefinedSchema(getResource()); } return refinedSchema; } public RefinedObjectClassDefinition getObjectClassDefinition() throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { if (objectClassDefinition == null) { if (useRefinedDefinition) { if (originalShadow != null) { objectClassDefinition = getRefinedSchema().determineCompositeObjectClassDefinition(originalShadow, additionalAuxiliaryObjectClassQNames); } else if (shadowCoordinates != null && !shadowCoordinates.isWildcard()) { objectClassDefinition = getRefinedSchema().determineCompositeObjectClassDefinition(shadowCoordinates); } } else { if (shadowCoordinates.getObjectClass() == null) { throw new IllegalStateException("No objectclass"); } ObjectClassComplexTypeDefinition origObjectClassDefinition = getRefinedSchema().getOriginalResourceSchema().findObjectClassDefinition(shadowCoordinates.getObjectClass()); if (origObjectClassDefinition == null) { throw new SchemaException("No object class definition for "+shadowCoordinates.getObjectClass()+" in original resource schema for "+getResource()); } else { objectClassDefinition = RefinedObjectClassDefinitionImpl.parseFromSchema(origObjectClassDefinition, getResource(), getRefinedSchema(), getResource().asPrismObject().getPrismContext(), "objectclass "+origObjectClassDefinition+" in "+getResource()); } } } return objectClassDefinition; } // we don't use additionalAuxiliaryObjectClassQNames as we don't know if they are initialized correctly [med] TODO: reconsider this public CompositeRefinedObjectClassDefinition computeCompositeObjectClassDefinition(@NotNull Collection<QName> auxObjectClassQNames) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { RefinedObjectClassDefinition structuralObjectClassDefinition = getObjectClassDefinition(); if (structuralObjectClassDefinition == null) { return null; } Collection<RefinedObjectClassDefinition> auxiliaryObjectClassDefinitions = new ArrayList<>(auxObjectClassQNames.size()); for (QName auxObjectClassQName : auxObjectClassQNames) { RefinedObjectClassDefinition auxObjectClassDef = refinedSchema.getRefinedDefinition(auxObjectClassQName); if (auxObjectClassDef == null) { throw new SchemaException("Auxiliary object class " + auxObjectClassQName + " specified in " + this + " does not exist"); } auxiliaryObjectClassDefinitions.add(auxObjectClassDef); } return new CompositeRefinedObjectClassDefinitionImpl(structuralObjectClassDefinition, auxiliaryObjectClassDefinitions); } public RefinedObjectClassDefinition computeCompositeObjectClassDefinition(PrismObject<ShadowType> shadow) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { return computeCompositeObjectClassDefinition(shadow.asObjectable().getAuxiliaryObjectClass()); } public String getChannel() { return getTask()==null?null:getTask().getChannel(); } public <T extends CapabilityType> ConnectorInstance getConnector(Class<T> operationCapabilityClass, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { if (connectorMap == null) { connectorMap = new HashMap<>(); } ConnectorInstance connector = connectorMap.get(operationCapabilityClass); if (connector == null) { connector = getConnectorInstance(operationCapabilityClass, parentResult); connectorMap.put(operationCapabilityClass, connector); } return connector; } public boolean isWildcard() { return (shadowCoordinates == null && originalShadow == null) || (shadowCoordinates != null && shadowCoordinates.isWildcard()); } /** * Creates a context for a different object class on the same resource. */ public ProvisioningContext spawn(ShadowKindType kind, String intent) { ProvisioningContext ctx = spawnSameResource(); ctx.shadowCoordinates = new ResourceShadowDiscriminator(getResourceOid(), kind, intent); return ctx; } /** * Creates a context for a different object class on the same resource. */ public ProvisioningContext spawn(QName objectClassQName) throws SchemaException { ProvisioningContext ctx = spawnSameResource(); ctx.shadowCoordinates = new ResourceShadowDiscriminator(getResourceOid(), null, null); ctx.shadowCoordinates.setObjectClass(objectClassQName); return ctx; } /** * Creates a context for a different object class on the same resource. */ public ProvisioningContext spawn(PrismObject<ShadowType> shadow) throws SchemaException { ProvisioningContext ctx = spawnSameResource(); ctx.setOriginalShadow(shadow); return ctx; } // /** // * Creates a context for a different object class on the same resource. // */ // public ProvisioningContext spawn(RefinedObjectClassDefinition objectClassDefinition) throws SchemaException { // ProvisioningContext ctx = spawnSameResource(); // ctx.setObjectClassDefinition(objectClassDefinition); // return ctx; // } private ProvisioningContext spawnSameResource() { ProvisioningContext ctx = new ProvisioningContext(resourceManager, parentResult); ctx.setTask(this.getTask()); ctx.setResourceOid(getResourceOid()); ctx.resource = this.resource; ctx.updateResourceName(); // TODO eliminate this mess - check if we need StateReporter any more ctx.connectorMap = this.connectorMap; ctx.refinedSchema = this.refinedSchema; return ctx; } public void assertDefinition(String message) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { if (getObjectClassDefinition() == null) { throw new SchemaException(message + " " + getDesc()); } } public void assertDefinition() throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { assertDefinition("Cannot locate object class definition"); } public String getDesc() { if (originalShadow != null) { return "for " + originalShadow + " in " + (resource==null?("resource "+getResourceOid()):resource); } else if (shadowCoordinates != null && !shadowCoordinates.isWildcard()) { return "for " + shadowCoordinates + " in " + (resource==null?("resource "+getResourceOid()):resource); } else { return "for wildcard in " + (resource==null?("resource "+getResourceOid()):resource); } } private <T extends CapabilityType> ConnectorInstance getConnectorInstance(Class<T> operationCapabilityClass, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { OperationResult connectorResult = parentResult.createMinorSubresult(ProvisioningContext.class.getName() + ".getConnectorInstance"); try { ConnectorInstance connector = resourceManager.getConfiguredConnectorInstance(getResource().asPrismObject(), operationCapabilityClass, false, parentResult); connectorResult.recordSuccess(); return connector; } catch (ObjectNotFoundException | SchemaException e){ connectorResult.recordPartialError("Could not get connector instance " + getDesc() + ": " + e.getMessage(), e); // Wrap those exceptions to a configuration exception. In the context of the provisioning operation we really cannot throw // ObjectNotFoundException exception. If we do that then the consistency code will interpret that as if the resource object // (shadow) is missing. But that's wrong. We do not have connector therefore we do not know anything about the shadow. We cannot // throw ObjectNotFoundException here. throw new ConfigurationException(e.getMessage(), e); } catch (CommunicationException | ConfigurationException | SystemException e){ connectorResult.recordPartialError("Could not get connector instance " + getDesc() + ": " + e.getMessage(), e); throw e; } } public <T extends CapabilityType> CapabilitiesType getConnectorCapabilities(Class<T> operationCapabilityClass) { return resourceManager.getConnectorCapabilities(resource.asPrismObject(), operationCapabilityClass); } @Override public String toString() { return "ProvisioningContext("+getDesc()+")"; } }