/* * 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.provisioning.impl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.schema.PrismSchemaImpl; import com.evolveum.midpoint.schema.processor.*; import com.evolveum.prism.xml.ns._public.types_3.SchemaDefinitionType; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.evolveum.midpoint.prism.delta.ContainerDelta; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.delta.ReferenceDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.schema.PrismSchema; import com.evolveum.midpoint.provisioning.api.GenericConnectorException; import com.evolveum.midpoint.provisioning.api.ProvisioningOperationOptions; import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance; import com.evolveum.midpoint.provisioning.ucf.api.ExecuteProvisioningScriptOperation; import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.CapabilityUtil; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.constants.ConnectorTestOperation; import com.evolveum.midpoint.schema.internals.InternalMonitor; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus; import com.evolveum.midpoint.schema.util.ConnectorTypeUtil; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.ResourceTypeUtil; import com.evolveum.midpoint.task.api.StateReporter; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; 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.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.AvailabilityStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CachingMetadataType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CapabilitiesType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CapabilityCollectionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorInstanceSpecificationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationalStateType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningScriptType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SchemaGenerationConstraintsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.XmlSchemaType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.SchemaCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ScriptCapabilityType; @Component public class ResourceManager { @Autowired(required = true) @Qualifier("cacheRepositoryService") private RepositoryService repositoryService; @Autowired(required = true) private ResourceCache resourceCache; @Autowired(required = true) private ConnectorManager connectorManager; @Autowired(required = true) private PrismContext prismContext; private static final Trace LOGGER = TraceManager.getTrace(ResourceManager.class); private static final String OPERATION_COMPLETE_RESOURCE = ResourceManager.class.getName() + ".completeResource"; public PrismObject<ResourceType> getResource(PrismObject<ResourceType> repositoryObject, GetOperationOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException{ InternalMonitor.getResourceCacheStats().recordRequest(); PrismObject<ResourceType> cachedResource = resourceCache.get(repositoryObject, options); if (cachedResource != null) { InternalMonitor.getResourceCacheStats().recordHit(); return cachedResource; } LOGGER.debug("Storing fetched resource {}, version {} to cache (previously cached version {})", new Object[]{ repositoryObject.getOid(), repositoryObject.getVersion(), resourceCache.getVersion(repositoryObject.getOid())}); return loadAndCacheResource(repositoryObject, options, parentResult); } public PrismObject<ResourceType> getResource(String oid, GetOperationOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException{ InternalMonitor.getResourceCacheStats().recordRequest(); String version = repositoryService.getVersion(ResourceType.class, oid, parentResult); PrismObject<ResourceType> cachedResource = resourceCache.get(oid, version, options); if (cachedResource != null) { InternalMonitor.getResourceCacheStats().recordHit(); if (LOGGER.isTraceEnabled()){ LOGGER.trace("Returning resource from cache:\n{}", cachedResource.debugDump()); } return cachedResource; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Fetching resource {}, version {}, storing to cache (previously cached version {})", oid, version, resourceCache.getVersion(oid)); } Collection<SelectorOptions<GetOperationOptions>> repoOptions = null; if (GetOperationOptions.isReadOnly(options)) { repoOptions = SelectorOptions.createCollection(GetOperationOptions.createReadOnly()); } PrismObject<ResourceType> repositoryObject = repositoryService.getObject(ResourceType.class, oid, repoOptions, parentResult); return loadAndCacheResource(repositoryObject, options, parentResult); } private PrismObject<ResourceType> loadAndCacheResource(PrismObject<ResourceType> repositoryObject, GetOperationOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { PrismObject<ResourceType> completedResource = completeResource(repositoryObject, null, false, null, options, parentResult); if (!isComplete(completedResource)) { // No not cache non-complete resources (e.g. those retrieved with noFetch) return completedResource; } if (LOGGER.isTraceEnabled()) { LOGGER.trace("Putting resource in cache:\n{}", completedResource.debugDump()); Element xsdSchemaElement = ResourceTypeUtil.getResourceXsdSchema(completedResource); if (xsdSchemaElement == null) { LOGGER.trace("Schema: null"); } else { LOGGER.trace("Schema:\n{}", DOMUtil.serializeDOMToString(ResourceTypeUtil.getResourceXsdSchema(completedResource))); } } OperationResult completeResourceResult = parentResult.findSubresult(OPERATION_COMPLETE_RESOURCE); if (completeResourceResult.isSuccess()) { // Cache only resources that are completely OK resourceCache.put(completedResource); } InternalMonitor.getResourceCacheStats().recordMiss(); return completedResource; } public void deleteResource(String oid, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws ObjectNotFoundException { resourceCache.remove(oid); repositoryService.deleteObject(ResourceType.class, oid, parentResult); } /** * Make sure that the resource is complete. * * It will check if the resource has a sufficiently fresh schema, etc. * * Returned resource may be the same or may be a different instance, but it * is guaranteed that it will be "fresher" and will correspond to the * repository state (assuming that the provided resource also corresponded * to the repository state). * * The connector schema that was fetched before can be supplied to this * method. This is just an optimization. It comes handy e.g. in test * connection case. * * Note: This is not really the best place for this method. Need to figure * out correct place later. * * @param repoResource * Resource to check * @param resourceSchema * schema that was freshly pre-fetched (or null) * @param parentResult * * @return completed resource * @throws ObjectNotFoundException * connector instance was not found * @throws SchemaException * @throws CommunicationException * cannot fetch resource schema * @throws ConfigurationException */ private PrismObject<ResourceType> completeResource(PrismObject<ResourceType> repoResource, ResourceSchema resourceSchema, boolean fetchedSchema, Map<String,Collection<Object>> capabilityMap, GetOperationOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { // do not add as a subresult..it will be added later, if the completing // of resource will be successfull.if not, it will be only set as a // fetch result in the resource.. OperationResult result = parentResult.createMinorSubresult(OPERATION_COMPLETE_RESOURCE); try { applyConnectorSchemaToResource(repoResource, result); } catch (SchemaException e) { String message = "Schema error while applying connector schema to connectorConfiguration section of "+repoResource+": "+e.getMessage(); result.recordPartialError(message, e); LOGGER.warn(message, e); return repoResource; } catch (ObjectNotFoundException e) { String message = "Object not found error while processing connector configuration of "+repoResource+": "+e.getMessage(); result.recordPartialError(message, e); LOGGER.warn(message, e); return repoResource; } catch (RuntimeException e) { String message = "Unexpected error while processing connector configuration of "+repoResource+": "+e.getMessage(); result.recordPartialError(message, e); LOGGER.warn(message, e); return repoResource; } PrismObject<ResourceType> newResource; if (isComplete(repoResource)) { // The resource is complete. newResource = repoResource; } else { // The resource is NOT complete. Try to fetch schema and capabilities if (GetOperationOptions.isNoFetch(options)) { // We need to fetch schema, but the noFetch option is specified. Therefore return whatever we have. result.recordSuccessIfUnknown(); return repoResource; } try { completeSchemaAndCapabilities(repoResource, resourceSchema, fetchedSchema, capabilityMap, result); } catch (Exception ex) { // Catch the exceptions. There are not critical. We need to catch them all because the connector may // throw even undocumented runtime exceptions. // Even non-complete resource may still be usable. The fetchResult indicates that there was an error result.recordPartialError("Cannot complete resource schema and capabilities: "+ex.getMessage(), ex); return repoResource; } try { // Now we need to re-read the resource from the repository and re-aply the schemas. This ensures that we will // cache the correct version and that we avoid race conditions, etc. newResource = repositoryService.getObject(ResourceType.class, repoResource.getOid(), null, result); applyConnectorSchemaToResource(newResource, result); } catch (SchemaException e) { result.recordFatalError(e); throw e; } catch (ObjectNotFoundException e) { result.recordFatalError(e); throw e; } catch (RuntimeException e) { result.recordFatalError(e); throw e; } } try { // make sure it has parsed resource and refined schema. We are going to cache // it, so we want to cache it with the parsed schemas RefinedResourceSchemaImpl.getResourceSchema(newResource, prismContext); RefinedResourceSchemaImpl.getRefinedSchema(newResource); } catch (SchemaException e) { String message = "Schema error while processing schemaHandling section of "+newResource+": "+e.getMessage(); result.recordPartialError(message, e); LOGGER.warn(message, e); return newResource; } catch (RuntimeException e) { String message = "Unexpected error while processing schemaHandling section of "+newResource+": "+e.getMessage(); result.recordPartialError(message, e); LOGGER.warn(message, e); return newResource; } result.recordSuccessIfUnknown(); return newResource; } private boolean isComplete(PrismObject<ResourceType> resource) { ResourceType resourceType = resource.asObjectable(); Element xsdSchema = ResourceTypeUtil.getResourceXsdSchema(resource); if (xsdSchema == null) { return false; } CapabilitiesType capabilitiesType = resourceType.getCapabilities(); if (capabilitiesType == null) { return false; } CachingMetadataType capCachingMetadata = capabilitiesType.getCachingMetadata(); if (capCachingMetadata == null) { return false; } return true; } private void completeSchemaAndCapabilities(PrismObject<ResourceType> resource, ResourceSchema resourceSchema, boolean fetchedSchema, Map<String,Collection<Object>> capabilityMap, OperationResult result) throws SchemaException, CommunicationException, ObjectNotFoundException, GenericFrameworkException, ConfigurationException { Collection<ItemDelta<?,?>> modifications = new ArrayList<>(); // Capabilities // we need to process capabilities first. Schema is one of the connector capabilities. // We need to determine this capability to select the right connector for schema retrieval. completeCapabilities(resource, capabilityMap != null, capabilityMap, modifications, result); if (resourceSchema == null) { // Try to get existing schema from resource. We do not want to override this if it exists // (but we still want to refresh the capabilities, that happens below) resourceSchema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); } if (resourceSchema == null || resourceSchema.isEmpty()) { LOGGER.trace("Fetching resource schema for {}", resource); resourceSchema = fetchResourceSchema(resource, capabilityMap, result); if (resourceSchema == null) { LOGGER.warn("No resource schema fetched from {}", resource); } else if (resourceSchema.isEmpty()) { LOGGER.warn("Empty resource schema fetched from {}", resource); } else { LOGGER.debug("Fetched resource schema for {}: {} definitions", resource, resourceSchema.getDefinitions().size()); fetchedSchema = true; } } if (fetchedSchema) { adjustSchemaForSimulatedCapabilities(resource, resourceSchema); ContainerDelta<XmlSchemaType> schemaContainerDelta = createSchemaUpdateDelta(resource, resourceSchema); modifications.add(schemaContainerDelta); // We have successfully fetched the resource schema. Therefore the resource must be up. modifications.add(createResourceAvailabilityStatusDelta(resource, AvailabilityStatusType.UP)); } if (!modifications.isEmpty()) { try { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Completing {}:\n{}", resource, DebugUtil.debugDump(modifications, 1)); } repositoryService.modifyObject(ResourceType.class, resource.getOid(), modifications, result); } catch (ObjectAlreadyExistsException ex) { // This should not happen throw new SystemException(ex); } } } private void completeCapabilities(PrismObject<ResourceType> resource, boolean forceRefresh, Map<String,Collection<Object>> capabilityMap, Collection<ItemDelta<?, ?>> modifications, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { ResourceType resourceType = resource.asObjectable(); ConnectorSpec defaultConnectorSpec = getDefaultConnectorSpec(resource); CapabilitiesType resourceCapType = resourceType.getCapabilities(); if (resourceCapType == null) { resourceCapType = new CapabilitiesType(); resourceType.setCapabilities(resourceCapType); } completeConnectorCapabilities(defaultConnectorSpec, resourceCapType, new ItemPath(ResourceType.F_CAPABILITIES), forceRefresh, capabilityMap==null?null:capabilityMap.get(null), modifications, result); for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { ConnectorSpec connectorSpec = getConnectorSpec(resource, additionalConnectorType); CapabilitiesType connectorCapType = additionalConnectorType.getCapabilities(); if (connectorCapType == null) { connectorCapType = new CapabilitiesType(); additionalConnectorType.setCapabilities(connectorCapType); } ItemPath itemPath = additionalConnectorType.asPrismContainerValue().getPath().subPath(ConnectorInstanceSpecificationType.F_CAPABILITIES); LOGGER.info("PPPPPPPPPPPPPPP: {}", itemPath); completeConnectorCapabilities(connectorSpec, connectorCapType, itemPath, forceRefresh, capabilityMap==null?null:capabilityMap.get(additionalConnectorType.getName()), modifications, result); } } private void completeConnectorCapabilities(ConnectorSpec connectorSpec, CapabilitiesType capType, ItemPath itemPath, boolean forceRefresh, Collection<Object> retrievedCapabilities, Collection<ItemDelta<?, ?>> modifications, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { if (!forceRefresh && capType.getNative() != null && !capType.getNative().getAny().isEmpty()) { return; } if (retrievedCapabilities == null) { try { InternalMonitor.recordConnectorCapabilitiesFetchCount(); ConnectorInstance connector = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); retrievedCapabilities = connector.fetchCapabilities(result); } catch (GenericFrameworkException e) { throw new GenericConnectorException("Generic error in connector " + connectorSpec + ": " + e.getMessage(), e); } } CapabilityCollectionType nativeCapType = new CapabilityCollectionType(); capType.setNative(nativeCapType); nativeCapType.getAny().addAll(retrievedCapabilities); CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); capType.setCachingMetadata(cachingMetadata); ObjectDelta<ResourceType> capabilitiesReplaceDelta = ObjectDelta.createModificationReplaceContainer(ResourceType.class, connectorSpec.getResource().getOid(), itemPath, prismContext, capType.asPrismContainerValue().clone()); modifications.addAll(capabilitiesReplaceDelta.getModifications()); } private ContainerDelta<XmlSchemaType> createSchemaUpdateDelta(PrismObject<ResourceType> resource, ResourceSchema resourceSchema) throws SchemaException { Document xsdDoc = null; try { // Convert to XSD LOGGER.trace("Serializing XSD resource schema for {} to DOM", resource); xsdDoc = resourceSchema.serializeToXsd(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Serialized XSD resource schema for {}:\n{}", resource, DOMUtil.serializeDOMToString(xsdDoc)); } } catch (SchemaException e) { throw new SchemaException("Error processing resource schema for " + resource + ": " + e.getMessage(), e); } Element xsdElement = DOMUtil.getFirstChildElement(xsdDoc); if (xsdElement == null) { throw new SchemaException("No schema was generated for " + resource); } CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); // Store generated schema into repository (modify the original // Resource) LOGGER.info("Storing generated schema in resource {}", resource); ContainerDelta<XmlSchemaType> schemaContainerDelta = ContainerDelta.createDelta( ResourceType.F_SCHEMA, ResourceType.class, prismContext); PrismContainerValue<XmlSchemaType> cval = new PrismContainerValue<XmlSchemaType>(prismContext); schemaContainerDelta.setValueToReplace(cval); PrismProperty<CachingMetadataType> cachingMetadataProperty = cval .createProperty(XmlSchemaType.F_CACHING_METADATA); cachingMetadataProperty.setRealValue(cachingMetadata); List<QName> objectClasses = ResourceTypeUtil.getSchemaGenerationConstraints(resource); if (objectClasses != null) { PrismProperty<SchemaGenerationConstraintsType> generationConstraints = cval .createProperty(XmlSchemaType.F_GENERATION_CONSTRAINTS); SchemaGenerationConstraintsType constraints = new SchemaGenerationConstraintsType(); constraints.getGenerateObjectClass().addAll(objectClasses); generationConstraints.setRealValue(constraints); } PrismProperty<SchemaDefinitionType> definitionProperty = cval.createProperty(XmlSchemaType.F_DEFINITION); ObjectTypeUtil.setXsdSchemaDefinition(definitionProperty, xsdElement); return schemaContainerDelta; } /** * Apply proper definition (connector schema) to the resource. */ private void applyConnectorSchemaToResource(PrismObject<ResourceType> resource, OperationResult result) throws SchemaException, ObjectNotFoundException { synchronized (resource) { boolean immutable = resource.isImmutable(); if (immutable) { resource.setImmutable(false); } try { PrismObjectDefinition<ResourceType> newResourceDefinition = resource.getDefinition().clone(); for (ConnectorSpec connectorSpec: getAllConnectorSpecs(resource)) { applyConnectorSchemaToResource(connectorSpec, newResourceDefinition, result); } resource.setDefinition(newResourceDefinition); } finally { if (immutable) { resource.setImmutable(true); } } } } /** * Apply proper definition (connector schema) to the resource. */ private void applyConnectorSchemaToResource(ConnectorSpec connectorSpec, PrismObjectDefinition<ResourceType> resourceDefinition, OperationResult result) throws SchemaException, ObjectNotFoundException { ConnectorType connectorType = connectorManager.getConnectorTypeReadOnly(connectorSpec, result); PrismSchema connectorSchema = connectorManager.getConnectorSchema(connectorType); if (connectorSchema == null) { throw new SchemaException("No connector schema in " + connectorType); } PrismContainerDefinition<ConnectorConfigurationType> configurationContainerDefinition = ConnectorTypeUtil .findConfigurationContainerDefinition(connectorType, connectorSchema); if (configurationContainerDefinition == null) { throw new SchemaException("No configuration container definition in schema of " + connectorType); } configurationContainerDefinition = configurationContainerDefinition.clone(); PrismContainer<ConnectorConfigurationType> configurationContainer = connectorSpec.getConnectorConfiguration(); // We want element name, minOccurs/maxOccurs and similar definition to be taken from the original, not the schema // the element is global in the connector schema. therefore it does not have correct maxOccurs if (configurationContainer != null) { configurationContainerDefinition.adoptElementDefinitionFrom(configurationContainer.getDefinition()); configurationContainer.applyDefinition(configurationContainerDefinition, true); } else { configurationContainerDefinition.adoptElementDefinitionFrom( resourceDefinition.findContainerDefinition(ResourceType.F_CONNECTOR_CONFIGURATION)); } if (connectorSpec.getConnectorName() == null) { // Default connector, for compatibility // It does not make sense to update this for any other connectors. // We cannot have one definition for addiitionalConnector[1]/connectorConfiguraiton and // different definition for addiitionalConnector[2]/connectorConfiguraiton in the object definition. // The way to go is to set up definitions on the container level. resourceDefinition.replaceDefinition(ResourceType.F_CONNECTOR_CONFIGURATION, configurationContainerDefinition); } } private ResourceSchema fetchResourceSchema(PrismObject<ResourceType> resource, Map<String,Collection<Object>> capabilityMap, OperationResult parentResult) throws CommunicationException, GenericFrameworkException, ConfigurationException, ObjectNotFoundException, SchemaException { ConnectorSpec connectorSpec = selectConnectorSpec(resource, capabilityMap, SchemaCapabilityType.class); if (connectorSpec == null) { LOGGER.trace("No connector has schema capability, cannot fetch resource schema"); return null; } InternalMonitor.recordResourceSchemaFetch(); List<QName> generateObjectClasses = ResourceTypeUtil.getSchemaGenerationConstraints(resource); ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, parentResult); LOGGER.trace("Trying to get schema from {}", connectorSpec); return connectorInstance.fetchResourceSchema(generateObjectClasses, parentResult); } public void testConnection(PrismObject<ResourceType> resource, OperationResult parentResult) { List<ConnectorSpec> allConnectorSpecs; try { allConnectorSpecs = getAllConnectorSpecs(resource); } catch (SchemaException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); parentResult.recordFatalError("Configuration error: " + e.getMessage(), e); return; } Map<String,Collection<Object>> capabilityMap = new HashMap<>(); for (ConnectorSpec connectorSpec: allConnectorSpecs) { OperationResult connectorTestResult = parentResult .createSubresult(ConnectorTestOperation.CONNECTOR_TEST.getOperation()); connectorTestResult.addParam(OperationResult.PARAM_NAME, connectorSpec.getConnectorName()); connectorTestResult.addParam(OperationResult.PARAM_OID, connectorSpec.getConnectorOid()); testConnectionConnector(connectorSpec, capabilityMap, connectorTestResult); connectorTestResult.computeStatus(); } // === test SCHEMA === OperationResult schemaResult = parentResult.createSubresult(ConnectorTestOperation.RESOURCE_SCHEMA .getOperation()); ResourceSchema schema = null; try { schema = fetchResourceSchema(resource, capabilityMap, schemaResult); } catch (CommunicationException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Communication error: " + e.getMessage(), e); return; } catch (GenericFrameworkException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Generic error: " + e.getMessage(), e); return; } catch (ConfigurationException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Configuration error: " + e.getMessage(), e); return; } catch (ObjectNotFoundException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Configuration error: " + e.getMessage(), e); return; } catch (SchemaException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Configuration error: " + e.getMessage(), e); return; } if (schema == null || schema.isEmpty()) { // Resource does not support schema // If there is a static schema in resource definition this may still be OK try { schema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); } catch (SchemaException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError(e); return; } if (schema == null || schema.isEmpty()) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Connector does not support schema and no static schema available"); return; } } // Invoke completeResource(). This will store the fetched schema to the // ResourceType // if there is no <schema> definition already. Therefore the // testResource() can be used to // generate the resource schema - until we have full schema caching // capability. try { resource = completeResource(resource, schema, true, capabilityMap, null, schemaResult); } catch (ObjectNotFoundException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError( "Object not found (unexpected error, probably a bug): " + e.getMessage(), e); return; } catch (SchemaException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError( "Schema processing error (probably connector bug): " + e.getMessage(), e); return; } catch (CommunicationException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Communication error: " + e.getMessage(), e); return; } catch (ConfigurationException e) { modifyResourceAvailabilityStatus(resource, AvailabilityStatusType.BROKEN, parentResult); schemaResult.recordFatalError("Configuration error: " + e.getMessage(), e); return; } schemaResult.recordSuccess(); // TODO: connector sanity (e.g. refined schema, at least one account type, identifiers // in schema, etc.) } public void testConnectionConnector(ConnectorSpec connectorSpec, Map<String,Collection<Object>> capabilityMap, OperationResult parentResult) { // === test INITIALIZATION === OperationResult initResult = parentResult .createSubresult(ConnectorTestOperation.CONNECTOR_INITIALIZATION.getOperation()); ConnectorInstance connector; try { // TODO: this returns configured instance. Then there is another configuration down below. // this means double configuration of the connector. TODO: clean this up connector = connectorManager.getConfiguredConnectorInstance(connectorSpec, true, initResult); initResult.recordSuccess(); } catch (ObjectNotFoundException e) { // The connector was not found. The resource definition is either // wrong or the connector is not // installed. modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); initResult.recordFatalError("The connector was not found: "+e.getMessage(), e); return; } catch (SchemaException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); initResult.recordFatalError("Schema error while dealing with the connector definition: "+e.getMessage(), e); return; } catch (RuntimeException | Error e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); initResult.recordFatalError("Unexpected runtime error: "+e.getMessage(), e); return; } catch (CommunicationException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); initResult.recordFatalError("Communication error: "+e.getMessage(), e); return; } catch (ConfigurationException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); initResult.recordFatalError("Configuration error: "+e.getMessage(), e); return; } LOGGER.debug("Testing connection using {}", connectorSpec); // === test CONFIGURATION === OperationResult configResult = parentResult .createSubresult(ConnectorTestOperation.CONNECTOR_CONFIGURATION.getOperation()); try { connector.configure(connectorSpec.getConnectorConfiguration().getValue(), configResult); configResult.recordSuccess(); } catch (CommunicationException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); configResult.recordFatalError("Communication error", e); return; } catch (GenericFrameworkException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); configResult.recordFatalError("Generic error", e); return; } catch (SchemaException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); configResult.recordFatalError("Schema error", e); return; } catch (ConfigurationException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); configResult.recordFatalError("Configuration error", e); return; } catch (RuntimeException | Error e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); configResult.recordFatalError("Unexpected runtime error", e); return; } // === test CONNECTION === // delegate the main part of the test to the connector connector.test(parentResult); parentResult.computeStatus(); if (!parentResult.isAcceptable()) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.DOWN, parentResult); // No point in going on. Following tests will fail anyway, they will // just produce misleading // messages. return; } else { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.UP, parentResult); } OperationResult capabilitiesResult = parentResult .createSubresult(ConnectorTestOperation.CONNECTOR_CAPABILITIES.getOperation()); try { InternalMonitor.recordConnectorCapabilitiesFetchCount(); Collection<Object> capabilities = connector.fetchCapabilities(capabilitiesResult); capabilityMap.put(connectorSpec.getConnectorName(), capabilities); capabilitiesResult.recordSuccess(); } catch (CommunicationException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); capabilitiesResult.recordFatalError("Communication error", e); return; } catch (GenericFrameworkException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); capabilitiesResult.recordFatalError("Generic error", e); return; } catch (ConfigurationException e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); capabilitiesResult.recordFatalError("Configuration error", e); return; } catch (RuntimeException | Error e) { modifyResourceAvailabilityStatus(connectorSpec.getResource(), AvailabilityStatusType.BROKEN, parentResult); capabilitiesResult.recordFatalError("Unexpected runtime error", e); return; } } public void modifyResourceAvailabilityStatus(PrismObject<ResourceType> resource, AvailabilityStatusType status, OperationResult result){ ResourceType resourceType = resource.asObjectable(); synchronized (resource) { if (resourceType.getOperationalState() == null || resourceType.getOperationalState().getLastAvailabilityStatus() == null || resourceType.getOperationalState().getLastAvailabilityStatus() != status) { List<PropertyDelta<?>> modifications = new ArrayList<PropertyDelta<?>>(); PropertyDelta<?> statusDelta = createResourceAvailabilityStatusDelta(resource, status); modifications.add(statusDelta); try{ repositoryService.modifyObject(ResourceType.class, resourceType.getOid(), modifications, result); } catch(SchemaException ex){ throw new SystemException(ex); } catch(ObjectAlreadyExistsException ex){ throw new SystemException(ex); } catch(ObjectNotFoundException ex){ throw new SystemException(ex); } } // ugly hack: change object even if it's immutable boolean immutable = resource.isImmutable(); if (immutable) { resource.setImmutable(false); } if (resourceType.getOperationalState() == null) { OperationalStateType operationalState = new OperationalStateType(); operationalState.setLastAvailabilityStatus(status); resourceType.setOperationalState(operationalState); } else { resourceType.getOperationalState().setLastAvailabilityStatus(status); } if (immutable) { resource.setImmutable(true); } } } private PropertyDelta<?> createResourceAvailabilityStatusDelta(PrismObject<ResourceType> resource, AvailabilityStatusType status) { PropertyDelta<?> statusDelta = PropertyDelta.createModificationReplaceProperty(OperationalStateType.F_LAST_AVAILABILITY_STATUS, resource.getDefinition(), status); statusDelta.setParentPath(new ItemPath(ResourceType.F_OPERATIONAL_STATE)); return statusDelta; } /** * Adjust scheme with respect to capabilities. E.g. disable attributes that * are used for special purpose (such as account activation simulation). * * TODO treat also objectclass-specific capabilities here */ private void adjustSchemaForSimulatedCapabilities(PrismObject<ResourceType> resource, ResourceSchema resourceSchema) { ResourceType resourceType = resource.asObjectable(); if (resourceType.getCapabilities() == null || resourceType.getCapabilities().getConfigured() == null) { return; } ActivationCapabilityType activationCapability = CapabilityUtil.getCapability(resourceType .getCapabilities().getConfigured().getAny(), ActivationCapabilityType.class); if (CapabilityUtil.getEffectiveActivationStatus(activationCapability) != null) { QName attributeName = activationCapability.getStatus().getAttribute(); Boolean ignore = activationCapability.getStatus().isIgnoreAttribute(); if (attributeName != null) { // The attribute used for enable/disable simulation should be ignored in the schema // otherwise strange things may happen, such as changing the same attribute both from // activation/enable and from the attribute using its native name. for (ObjectClassComplexTypeDefinition objectClassDefinition : resourceSchema .getDefinitions(ObjectClassComplexTypeDefinition.class)) { ResourceAttributeDefinition attributeDefinition = objectClassDefinition .findAttributeDefinition(attributeName); if (attributeDefinition != null) { if (ignore != null && !ignore.booleanValue()) { ((ResourceAttributeDefinitionImpl) attributeDefinition).setIgnored(false); } else { ((ResourceAttributeDefinitionImpl) attributeDefinition).setIgnored(true); } } else { // simulated activation attribute points to something that is not in the schema // technically, this is an error. But it looks to be quite common in connectors. // The enable/disable is using operational attributes that are not exposed in the // schema, but they work if passed to the connector. // Therefore we don't want to break anything. We could log an warning here, but the // warning would be quite frequent. Maybe a better place to warn user would be import // of the object. LOGGER.debug("Simulated activation attribute " + attributeName + " for objectclass " + objectClassDefinition.getTypeName() + " in " + resource + " does not exist in the resource schema. This may work well, but it is not clean. Connector exposing such schema should be fixed."); } } } } } private void checkSchema(PrismSchema schema) throws SchemaException { // This is resource schema, it should contain only // ResourceObjectDefintions for (Definition def : schema.getDefinitions()) { if (def instanceof ComplexTypeDefinition) { // This is OK } else if (def instanceof ResourceAttributeContainerDefinition) { checkResourceObjectDefinition((ResourceAttributeContainerDefinition) def); } else { throw new SchemaException("Unexpected definition in resource schema: " + def); } } } private void checkResourceObjectDefinition(ResourceAttributeContainerDefinition rod) throws SchemaException { for (ItemDefinition def : rod.getDefinitions()) { if (!(def instanceof ResourceAttributeDefinition)) { throw new SchemaException("Unexpected definition in resource schema object " + rod + ": " + def); } } } public void applyDefinition(ObjectDelta<ResourceType> delta, ResourceType resourceWhenNoOid, GetOperationOptions options, OperationResult objectResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { if (delta.isAdd()) { PrismObject<ResourceType> resource = delta.getObjectToAdd(); applyConnectorSchemaToResource(resource, objectResult); return; } else if (delta.isModify()) { // Go on } else { return; } if (delta.hasCompleteDefinition()){ //nothing to do, all modifications has definitions..just aplly this deltas.. return; } PrismObject<ResourceType> resource; String resourceOid = delta.getOid(); if (resourceOid == null) { Validate.notNull(resourceWhenNoOid, "Resource oid not specified in the object delta, and resource is not specified as well. Could not apply definition."); resource = resourceWhenNoOid.asPrismObject(); } else { resource = getResource(resourceOid, options, objectResult); } ResourceType resourceType = resource.asObjectable(); // ResourceType resourceType = completeResource(resource.asObjectable(), null, objectResult); //TODO TODO TODO FIXME FIXME FIXME copied from ObjectImprted..union this two cases PrismContainer<ConnectorConfigurationType> configurationContainer = ResourceTypeUtil.getConfigurationContainer(resourceType); if (configurationContainer == null || configurationContainer.isEmpty()) { // Nothing to check objectResult.recordWarning("The resource has no configuration"); return; } // Check the resource configuration. The schema is in connector, so fetch the connector first String connectorOid = resourceType.getConnectorRef().getOid(); if (StringUtils.isBlank(connectorOid)) { objectResult.recordFatalError("The connector reference (connectorRef) is null or empty"); return; } //ItemDelta.findItemDelta(delta.getModifications(), ResourceType.F_SCHEMA, ContainerDelta.class) == null || ReferenceDelta connectorRefDelta = ReferenceDelta.findReferenceModification(delta.getModifications(), ResourceType.F_CONNECTOR_REF); if (connectorRefDelta != null){ Item<PrismReferenceValue,PrismReferenceDefinition> connectorRefNew = connectorRefDelta.getItemNewMatchingPath(null); if (connectorRefNew.getValues().size() == 1){ PrismReferenceValue connectorRefValue = connectorRefNew.getValues().iterator().next(); if (connectorRefValue.getOid() != null && !connectorOid.equals(connectorRefValue.getOid())){ connectorOid = connectorRefValue.getOid(); } } } PrismObject<ConnectorType> connector = null; ConnectorType connectorType = null; try { connector = repositoryService.getObject(ConnectorType.class, connectorOid, null, objectResult); connectorType = connector.asObjectable(); } catch (ObjectNotFoundException e) { // No connector, no fun. We can't check the schema. But this is referential integrity problem. // Mark the error ... there is nothing more to do objectResult.recordFatalError("Connector (OID:" + connectorOid + ") referenced from the resource is not in the repository", e); return; } catch (SchemaException e) { // Probably a malformed connector. To be kind of robust, lets allow the import. // Mark the error ... there is nothing more to do objectResult.recordPartialError("Connector (OID:" + connectorOid + ") referenced from the resource has schema problems: " + e.getMessage(), e); LOGGER.error("Connector (OID:{}) referenced from the imported resource \"{}\" has schema problems: {}", new Object[]{connectorOid, resourceType.getName(), e.getMessage(), e}); return; } Element connectorSchemaElement = ConnectorTypeUtil.getConnectorXsdSchema(connector); PrismSchema connectorSchema = null; if (connectorSchemaElement == null) { // No schema to validate with return; } try { connectorSchema = PrismSchemaImpl.parse(connectorSchemaElement, true, "schema for " + connector, prismContext); } catch (SchemaException e) { objectResult.recordFatalError("Error parsing connector schema for " + connector + ": "+e.getMessage(), e); return; } QName configContainerQName = new QName(connectorType.getNamespace(), ResourceType.F_CONNECTOR_CONFIGURATION.getLocalPart()); PrismContainerDefinition<ConnectorConfigurationType> configContainerDef = connectorSchema.findContainerDefinitionByElementName(configContainerQName); if (configContainerDef == null) { objectResult.recordFatalError("Definition of configuration container " + configContainerQName + " not found in the schema of of " + connector); return; } try { configurationContainer.applyDefinition(configContainerDef); } catch (SchemaException e) { objectResult.recordFatalError("Configuration error in " + resource + ": "+e.getMessage(), e); return; } PrismContainer configContainer = resourceType.asPrismObject().findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); configContainer.applyDefinition(configContainerDef); for (ItemDelta<?,?> itemDelta : delta.getModifications()){ applyItemDefinition(itemDelta, configContainerDef, objectResult); } } private <V extends PrismValue, D extends ItemDefinition> void applyItemDefinition(ItemDelta<V,D> itemDelta, PrismContainerDefinition<ConnectorConfigurationType> configContainerDef, OperationResult objectResult) throws SchemaException { if (itemDelta.getParentPath() == null){ LOGGER.trace("No parent path defined for item delta {}", itemDelta); return; } QName first = ItemPath.getName(itemDelta.getParentPath().first()); if (first == null){ return; } if (itemDelta.getDefinition() == null && (ResourceType.F_CONNECTOR_CONFIGURATION.equals(first) || ResourceType.F_SCHEMA.equals(first))){ ItemPath path = itemDelta.getPath().rest(); D itemDef = configContainerDef.findItemDefinition(path); if (itemDef == null){ LOGGER.warn("No definition found for item {}. Check your namespaces?", path); objectResult.recordWarning("No definition found for item delta: " + itemDelta +". Check your namespaces?" ); // throw new SchemaException("No definition found for item " + path+ ". Check your namespaces?" ); return; } itemDelta.applyDefinition(itemDef); } } public void applyDefinition(PrismObject<ResourceType> resource, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { applyConnectorSchemaToResource(resource, parentResult); } public void applyDefinition(ObjectQuery query, OperationResult result) { // TODO: not implemented yet } public Object executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { PrismObject<ResourceType> resource = getResource(resourceOid, null, result); ConnectorSpec connectorSpec = selectConnectorSpec(resource, ScriptCapabilityType.class); if (connectorSpec == null) { throw new UnsupportedOperationException("No connector supports script capability"); } ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); ExecuteProvisioningScriptOperation scriptOperation = ProvisioningUtil.convertToScriptOperation(script, "script on "+resource, prismContext); try { StateReporter reporter = new StateReporter(resourceOid, task); return connectorInstance.executeScript(scriptOperation, reporter, result); } catch (GenericFrameworkException e) { // Not expected. Transform to system exception result.recordFatalError("Generic provisioning framework error", e); throw new SystemException("Generic provisioning framework error: " + e.getMessage(), e); } } public List<ConnectorOperationalStatus> getConnectorOperationalStatus(PrismObject<ResourceType> resource, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { List<ConnectorOperationalStatus> statuses = new ArrayList<>(); for (ConnectorSpec connectorSpec: getAllConnectorSpecs(resource)) { ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); ConnectorOperationalStatus operationalStatus = connectorInstance.getOperationalStatus(); if (operationalStatus != null) { operationalStatus.setConnectorName(connectorSpec.getConnectorName()); statuses.add(operationalStatus); } } return statuses; } private List<ConnectorSpec> getAllConnectorSpecs(PrismObject<ResourceType> resource) throws SchemaException { List<ConnectorSpec> connectorSpecs = new ArrayList<>(); connectorSpecs.add(getDefaultConnectorSpec(resource)); for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { connectorSpecs.add(getConnectorSpec(resource, additionalConnectorType)); } return connectorSpecs; } public <T extends CapabilityType> ConnectorInstance getConfiguredConnectorInstance(PrismObject<ResourceType> resource, Class<T> operationCapabilityClass, boolean forceFresh, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { ConnectorSpec connectorSpec = selectConnectorSpec(resource, operationCapabilityClass); if (connectorSpec == null) { return null; } return connectorManager.getConfiguredConnectorInstance(connectorSpec, forceFresh, parentResult); } public <T extends CapabilityType> CapabilitiesType getConnectorCapabilities(PrismObject<ResourceType> resource, Class<T> operationCapabilityClass) { for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { if (supportsCapability(additionalConnectorType, operationCapabilityClass)) { return additionalConnectorType.getCapabilities(); } } return resource.asObjectable().getCapabilities(); } private <T extends CapabilityType> ConnectorSpec selectConnectorSpec(PrismObject<ResourceType> resource, Map<String,Collection<Object>> capabilityMap, Class<T> capabilityClass) throws SchemaException { if (capabilityMap == null) { return selectConnectorSpec(resource, capabilityClass); } for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { if (supportsCapability(additionalConnectorType, capabilityMap.get(additionalConnectorType.getName()), capabilityClass)) { return getConnectorSpec(resource, additionalConnectorType); } } return getDefaultConnectorSpec(resource); } private <T extends CapabilityType> ConnectorSpec selectConnectorSpec(PrismObject<ResourceType> resource, Class<T> operationCapabilityClass) throws SchemaException { for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { if (supportsCapability(additionalConnectorType, operationCapabilityClass)) { return getConnectorSpec(resource, additionalConnectorType); } } return getDefaultConnectorSpec(resource); } private <T extends CapabilityType> boolean supportsCapability(ConnectorInstanceSpecificationType additionalConnectorType, Class<T> capabilityClass) { T cap = CapabilityUtil.getEffectiveCapability(additionalConnectorType.getCapabilities(), capabilityClass); if (cap == null) { return false; } return CapabilityUtil.isCapabilityEnabled(cap); } private <T extends CapabilityType> boolean supportsCapability(ConnectorInstanceSpecificationType additionalConnectorType, Collection<Object> nativeCapabilities, Class<T> capabilityClass) { CapabilitiesType specifiedCapabilitiesType = additionalConnectorType.getCapabilities(); if (specifiedCapabilitiesType != null) { CapabilityCollectionType configuredCapCollectionType = specifiedCapabilitiesType.getConfigured(); if (configuredCapCollectionType != null) { T configuredCap = CapabilityUtil.getCapability(configuredCapCollectionType.getAny(), capabilityClass); if (configuredCap != null && !CapabilityUtil.isCapabilityEnabled(configuredCap)) { return false; } } } T cap = CapabilityUtil.getCapability(nativeCapabilities, capabilityClass); if (cap == null) { return false; } return CapabilityUtil.isCapabilityEnabled(cap); } private ConnectorSpec getDefaultConnectorSpec(PrismObject<ResourceType> resource) { return new ConnectorSpec(resource, null, ResourceTypeUtil.getConnectorOid(resource), resource.findContainer(ResourceType.F_CONNECTOR_CONFIGURATION)); } private ConnectorSpec getConnectorSpec(PrismObject<ResourceType> resource, ConnectorInstanceSpecificationType additionalConnectorType) throws SchemaException { String connectorOid = additionalConnectorType.getConnectorRef().getOid(); if (StringUtils.isBlank(connectorOid)) { throw new SchemaException("No connector OID in additional connector in "+resource); } PrismContainer<ConnectorConfigurationType> connectorConfiguration = additionalConnectorType.asPrismContainerValue().findContainer(ConnectorInstanceSpecificationType.F_CONNECTOR_CONFIGURATION); String connectorName = additionalConnectorType.getName(); if (StringUtils.isBlank(connectorName)) { throw new SchemaException("No connector name in additional connector in "+resource); } return new ConnectorSpec(resource, connectorName, connectorOid, connectorConfiguration); } }