/* * 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.List; import java.util.Set; import javax.annotation.PreDestroy; import javax.xml.namespace.QName; import org.apache.commons.lang.Validate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; import com.evolveum.midpoint.common.crypto.CryptoUtil; import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.PrismProperty; 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.query.NoneFilter; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectPaging; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.provisioning.api.ConstraintViolationConfirmer; import com.evolveum.midpoint.provisioning.api.ConstraintsCheckingResult; import com.evolveum.midpoint.provisioning.api.GenericConnectorException; import com.evolveum.midpoint.provisioning.api.ProvisioningOperationOptions; import com.evolveum.midpoint.provisioning.api.ProvisioningService; import com.evolveum.midpoint.provisioning.impl.ShadowCacheFactory.Mode; import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; import com.evolveum.midpoint.repo.api.RepoAddOptions; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.LabeledString; import com.evolveum.midpoint.schema.ProvisioningDiag; import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; import com.evolveum.midpoint.schema.ResultHandler; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.SearchResultMetadata; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.constants.ConnectorTestOperation; 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.schema.statistics.ConnectorOperationalStatus; import com.evolveum.midpoint.schema.util.ObjectQueryUtil; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.SchemaDebugUtil; import com.evolveum.midpoint.task.api.Task; 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.CachingMetadataType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FailedOperationTypeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationProvisioningScriptsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType; 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.ShadowType; /** * Implementation of provisioning service. * * It is just a "dispatcher" that routes interface calls to appropriate places. * E.g. the operations regarding resource definitions are routed directly to the * repository, operations of shadow objects are routed to the shadow cache and * so on. * * @author Radovan Semancik */ @Service(value = "provisioningService") @Primary public class ProvisioningServiceImpl implements ProvisioningService { @Autowired(required = true) private ShadowCacheFactory shadowCacheFactory; @Autowired(required = true) private ResourceManager resourceManager; @Autowired(required = true) @Qualifier("cacheRepositoryService") private RepositoryService cacheRepositoryService; @Autowired(required = true) private ConnectorManager connectorManager; @Autowired(required = true) private PrismContext prismContext; private PrismObjectDefinition<ShadowType> resourceObjectShadowDefinition; private static final Trace LOGGER = TraceManager.getTrace(ProvisioningServiceImpl.class); private static final String DETAILS_CONNECTOR_FRAMEWORK_VERSION = "ConnId framework version"; // TODO generalize public ShadowCache getShadowCache(ShadowCacheFactory.Mode mode){ return shadowCacheFactory.getShadowCache(mode); } /** * Get the value of repositoryService. * * @return the value of repositoryService */ public RepositoryService getCacheRepositoryService() { return cacheRepositoryService; } /** * Set the value of repositoryService * * Expected to be injected. * * @param repositoryService * new value of repositoryService */ public void setCacheRepositoryService(RepositoryService repositoryService) { this.cacheRepositoryService = repositoryService; } @SuppressWarnings("unchecked") @Override public <T extends ObjectType> PrismObject<T> getObject(Class<T> type, String oid, Collection<SelectorOptions<GetOperationOptions>> options, Task task, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException { Validate.notNull(oid, "Oid of object to get must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); // Result type for this operation OperationResult result = parentResult.createMinorSubresult(ProvisioningService.class.getName() + ".getObject"); result.addParam(OperationResult.PARAM_OID, oid); result.addParam(OperationResult.PARAM_TYPE, type); result.addCollectionOfSerializablesAsParam("options", options); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options); PrismObject<T> resultingObject = null; if (ResourceType.class.isAssignableFrom(type)) { if (GetOperationOptions.isRaw(rootOptions)) { try { resultingObject = (PrismObject<T>) cacheRepositoryService.getObject(ResourceType.class, oid, null, result); } catch (ObjectNotFoundException|SchemaException ex) { // catching an exception is important because otherwise the result is UNKNOWN result.recordFatalError(ex); throw ex; } try { applyDefinition(resultingObject, result); } catch (ObjectNotFoundException ex) { // this is almost OK, we use raw for debug pages, so we want // to return resource and it can be fixed result.muteLastSubresultError(); ProvisioningUtil.logWarning(LOGGER, result, "Bad connector reference defined for resource: " + ex.getMessage(), ex); } catch (SchemaException ex){ result.muteLastSubresultError(); ProvisioningUtil.logWarning(LOGGER, result, "Schema violation: " + ex.getMessage(), ex); } catch (ConfigurationException ex){ result.muteLastSubresultError(); ProvisioningUtil.logWarning(LOGGER, result, "Configuration problem: " + ex.getMessage(), ex); } } else { // We need to handle resource specially. This is usually cached // and we do not want to get the repository // object if it is in the cache. // Make sure that the object is complete, e.g. there is a // (fresh) // schema try { resultingObject = (PrismObject<T>) resourceManager.getResource(oid, SelectorOptions.findRootOptions(options), result); } catch (ObjectNotFoundException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Resource object not found", ex); throw ex; } catch (SchemaException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Schema violation", ex); throw ex; } catch (CommunicationException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Error communicating with resource", ex); throw ex; } catch (ConfigurationException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Bad resource configuration", ex); throw ex; } } } else { // Not resource PrismObject<T> repositoryObject = getRepoObject(type, oid, rootOptions, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Retrieved repository object:\n{}", repositoryObject.debugDump()); } if (repositoryObject.canRepresent(ShadowType.class)) { try { resultingObject = (PrismObject<T>) getShadowCache(Mode.STANDARD).getShadow(oid, (PrismObject<ShadowType>) (repositoryObject), options, task, result); } catch (ObjectNotFoundException e) { if (!GetOperationOptions.isAllowNotFound(rootOptions)){ ProvisioningUtil.recordFatalError(LOGGER, result, "Error getting object OID=" + oid + ": " + e.getMessage(), e); } else{ result.muteLastSubresultError(); result.computeStatus(); } throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Error getting object OID=" + oid + ": " + e.getMessage(), e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Error getting object OID=" + oid + ": " + e.getMessage(), e); throw e; } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Error getting object OID=" + oid + ": " + e.getMessage(), e); throw e; } catch (SecurityViolationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Error getting object OID=" + oid + ": " + e.getMessage(), e); throw e; } catch (SystemException e) { // Do NOT wrap this into SystemException again ProvisioningUtil.recordFatalError(LOGGER, result, "Error getting object OID=" + oid + ": " + e.getMessage(), e); throw e; } catch (RuntimeException e){ ProvisioningUtil.recordFatalError(LOGGER, result, "Error getting object OID=" + oid + ": " + e.getMessage(), e); throw new SystemException(e); } } else { resultingObject = repositoryObject; } } result.computeStatus(); if (!GetOperationOptions.isRaw(rootOptions)) { resultingObject = resultingObject.cloneIfImmutable(); resultingObject.asObjectable().setFetchResult(result.createOperationResultType()); } result.cleanupResult(); validateObject(resultingObject); return resultingObject; } @Override public <T extends ObjectType> String addObject(PrismObject<T> object, OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws ObjectAlreadyExistsException, SchemaException, CommunicationException, ObjectNotFoundException, ConfigurationException, SecurityViolationException { Validate.notNull(object, "Object to add must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); if (InternalsConfig.encryptionChecks) { CryptoUtil.checkEncrypted(object); } OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".addObject"); result.addParam("object", object); result.addParam("scripts", scripts); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); String oid = null; if (object.canRepresent(ShadowType.class)) { try { // calling shadow cache to add object oid = getShadowCache(Mode.STANDARD).addShadow((PrismObject<ShadowType>) object, scripts, null, options, task, result); LOGGER.trace("**PROVISIONING: Added shadow object {}", oid); result.computeStatus(); } catch (GenericFrameworkException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't add object " + object + ". Reason: " + ex.getMessage(), ex); throw new CommunicationException(ex.getMessage(), ex); } catch (SchemaException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't add object. Schema violation: " + ex.getMessage(), ex); throw new SchemaException("Couldn't add object. Schema violation: " + ex.getMessage(), ex); } catch (ObjectAlreadyExistsException ex) { result.computeStatus(); if (!result.isSuccess() && !result.isHandledError()) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't add object. Object already exist: " + ex.getMessage(), ex); } else { result.recordSuccess(); } result.cleanupResult(ex); throw new ObjectAlreadyExistsException("Couldn't add object. Object already exists: " + ex.getMessage(), ex); } catch (ConfigurationException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't add object. Configuration error: " + ex.getMessage(), ex); throw ex; } catch (SecurityViolationException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't add object. Security violation: " + ex.getMessage(), ex); throw ex; } catch (RuntimeException ex){ ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't add object. Runtime error: " + ex.getMessage(), ex); throw new SystemException(ex); } } else { RepoAddOptions addOptions = null; if (ProvisioningOperationOptions.isOverwrite(options)){ addOptions = RepoAddOptions.createOverwrite(); } oid = cacheRepositoryService.addObject(object, addOptions, result); result.computeStatus(); } result.cleanupResult(); return oid; } @SuppressWarnings("rawtypes") @Override public int synchronize(ResourceShadowDiscriminator shadowCoordinates, Task task, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException { Validate.notNull(shadowCoordinates, "Coordinates oid must not be null."); String resourceOid = shadowCoordinates.getResourceOid(); Validate.notNull(resourceOid, "Resource oid must not be null."); Validate.notNull(task, "Task must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".synchronize"); result.addParam(OperationResult.PARAM_OID, resourceOid); result.addParam(OperationResult.PARAM_TASK, task.toString()); int processedChanges = 0; try { // Resolve resource PrismObject<ResourceType> resource = getObject(ResourceType.class, resourceOid, null, task, result); ResourceType resourceType = resource.asObjectable(); LOGGER.trace("**PROVISIONING: Start synchronization of resource {} ", resourceType); // getting token form task PrismProperty tokenProperty = getTokenProperty(shadowCoordinates, task, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("**PROVISIONING: Got token property: {} from the task extension.", SchemaDebugUtil.prettyPrint(tokenProperty)); } processedChanges = getShadowCache(Mode.STANDARD).synchronize(shadowCoordinates, tokenProperty, task, result); LOGGER.debug("Synchronization of {} done, token {}, {} changes", resource, tokenProperty, processedChanges); } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: object not found: " + e.getMessage(), e); throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: communication problem: " + e.getMessage(), e); throw e; } catch (ObjectAlreadyExistsException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: object already exists problem: " + e.getMessage(), e); throw new SystemException(e); } catch (GenericFrameworkException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: generic connector framework error: " + e.getMessage(), e); throw new GenericConnectorException(e.getMessage(), e); } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: schema problem: " + e.getMessage(), e); throw e; } catch (SecurityViolationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: security violation: " + e.getMessage(), e); throw e; } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: configuration problem: " + e.getMessage(), e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: unexpected problem: " + e.getMessage(), e); throw e; } result.recordSuccess(); result.cleanupResult(); return processedChanges; } @SuppressWarnings("rawtypes") private PrismProperty getTokenProperty(ResourceShadowDiscriminator shadowCoordinates, Task task, OperationResult result) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException { PrismProperty tokenProperty = null; if (task.getExtension() != null) { tokenProperty = task.getExtensionProperty(SchemaConstants.SYNC_TOKEN); } if (tokenProperty != null && (tokenProperty.getAnyRealValue() == null)) { LOGGER.warn("Sync token exists, but it is empty (null value). Ignoring it."); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Empty sync token property:\n{}", tokenProperty.debugDump()); } tokenProperty = null; } // if the token is not specified in the task, get the latest token if (tokenProperty == null) { tokenProperty = getShadowCache(Mode.STANDARD).fetchCurrentToken(shadowCoordinates, result); if (tokenProperty == null || tokenProperty.getValue() == null || tokenProperty.getValue().getValue() == null) { LOGGER.warn("Empty current sync token provided by {}", shadowCoordinates); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Empty current sync token property."); } return null; } } return tokenProperty; } @Override public <T extends ObjectType> SearchResultList<PrismObject<T>> searchObjects(Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> options, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".searchObjects"); result.addParam("objectType", type); result.addParam("query", query); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); final SearchResultList<PrismObject<T>> objListType = new SearchResultList<>(new ArrayList<PrismObject<T>>()); SearchResultMetadata metadata; try { if (!ShadowType.class.isAssignableFrom(type)) { SearchResultList<PrismObject<T>> objects = searchRepoObjects(type, query, options, result); result.computeStatus(); result.recordSuccessIfUnknown(); result.cleanupResult(); // validateObjects(objects); return objects; } final ResultHandler<T> handler = (object, parentResult1) -> objListType.add(object); metadata = searchObjectsIterative(type, query, options, handler, task, result); } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Could not search objects: configuration problem: " + e.getMessage(), e); throw e; } catch (SecurityViolationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Could not search objects: security violation: " + e.getMessage(), e); throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Could not search objects: communication problem: " + e.getMessage(), e); throw e; } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Could not search objects: resource not found: " + e.getMessage(), e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Could not search objects: schema violation: " + e.getMessage(), e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } result.computeStatus(); result.cleanupResult(); // validateObjects(objListType); objListType.setMetadata(metadata); return objListType; } @SuppressWarnings("unchecked") private <T extends ObjectType> SearchResultList<PrismObject<T>> searchRepoObjects(Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> options, OperationResult result) throws SchemaException { List<PrismObject<T>> repoObjects = null; // TODO: should searching connectors trigger rediscovery? Collection<SelectorOptions<GetOperationOptions>> repoOptions = null; if (GetOperationOptions.isReadOnly(SelectorOptions.findRootOptions(options))) { repoOptions = SelectorOptions.createCollection(GetOperationOptions.createReadOnly()); } repoObjects = getCacheRepositoryService().searchObjects(type, query, repoOptions, result); SearchResultList<PrismObject<T>> newObjListType = new SearchResultList(new ArrayList<PrismObject<T>>()); for (PrismObject<T> repoObject : repoObjects) { OperationResult objResult = new OperationResult(ProvisioningService.class.getName() + ".searchObjects.object"); try { PrismObject<T> completeResource = completeObject(type, repoObject, options, objResult); validateObject(completeResource); objResult.computeStatusIfUnknown(); if (!objResult.isSuccess()) { completeResource.asObjectable().setFetchResult(objResult.createOperationResultType()); // necessary e.g. to skip validation for resources that had issues when checked result.addSubresult(objResult); } newObjListType.add((PrismObject<T>) completeResource); // TODO: what else do to with objResult?? } catch (ObjectNotFoundException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { repoObject, e.getMessage(), e }); objResult.recordFatalError(e); repoObject.asObjectable().setFetchResult(objResult.createOperationResultType()); newObjListType.add(repoObject); result.addSubresult(objResult); result.recordPartialError(e); } catch (SchemaException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { repoObject, e.getMessage(), e }); objResult.recordFatalError(e); repoObject.asObjectable().setFetchResult(objResult.createOperationResultType()); newObjListType.add(repoObject); result.addSubresult(objResult); result.recordPartialError(e); } catch (CommunicationException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { repoObject, e.getMessage(), e }); objResult.recordFatalError(e); repoObject.asObjectable().setFetchResult(objResult.createOperationResultType()); newObjListType.add(repoObject); result.addSubresult(objResult); result.recordPartialError(e); } catch (ConfigurationException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { repoObject, e.getMessage(), e }); objResult.recordFatalError(e); repoObject.asObjectable().setFetchResult(objResult.createOperationResultType()); newObjListType.add(repoObject); result.addSubresult(objResult); result.recordPartialError(e); } catch (RuntimeException e) { // FIXME: Strictly speaking, the runtime exception should // not be handled here. // The runtime exceptions should be considered fatal anyway // ... but some of the // ICF exceptions are still translated to system exceptions. // So this provides // a better robustness now. LOGGER.error("System error while completing {}: {}. Using non-complete object.", new Object[] { repoObject, e.getMessage(), e }); objResult.recordFatalError(e); repoObject.asObjectable().setFetchResult(objResult.createOperationResultType()); newObjListType.add(repoObject); result.addSubresult(objResult); result.recordPartialError(e); } } return newObjListType; } private <T extends ObjectType> PrismObject<T> completeObject(Class<T> type, PrismObject<T> inObject, Collection<SelectorOptions<GetOperationOptions>> options, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { if (ResourceType.class.equals(type)) { PrismObject<ResourceType> completeResource = resourceManager.getResource((PrismObject<ResourceType>) inObject, SelectorOptions.findRootOptions(options), result); return (PrismObject<T>) completeResource; } else if (ShadowType.class.equals(type)) { //TODO: applyDefinition??? applyDefinition(inObject, result); setProtectedShadow((PrismObject<ShadowType>) inObject, result); return inObject; } else { //TODO: connectors etc.. } return inObject; } public <T extends ObjectType> Integer countObjects(Class<T> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> options, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { OperationResult result = parentResult.createMinorSubresult(ProvisioningService.class.getName() + ".countObjects"); result.addParam("objectType", type); result.addParam("query", query); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); ObjectFilter filter = null; if (query != null) { filter = ObjectQueryUtil.simplify(query.getFilter()); query = query.cloneEmpty(); query.setFilter(filter); } if (filter != null && filter instanceof NoneFilter) { result.recordSuccessIfUnknown(); result.cleanupResult(); LOGGER.trace("Finished counting. Nothing to do. Filter is NONE"); return 0; } GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options); if (!ShadowType.class.isAssignableFrom(type) || GetOperationOptions.isNoFetch(rootOptions) || GetOperationOptions.isRaw(rootOptions)) { int count = getCacheRepositoryService().countObjects(type, query, parentResult); result.computeStatus(); result.recordSuccessIfUnknown(); result.cleanupResult(); return count; } Integer count; try { count = getShadowCache(Mode.STANDARD).countObjects(query, task, result); result.computeStatus(); } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (Error e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } finally { result.cleanupResult(); } return count; } @SuppressWarnings("rawtypes") @Override public <T extends ObjectType> String modifyObject(Class<T> type, String oid, Collection<? extends ItemDelta> modifications, OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ObjectAlreadyExistsException { Validate.notNull(oid, "OID must not be null."); Validate.notNull(modifications, "Modifications must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); if (InternalsConfig.encryptionChecks) { CryptoUtil.checkEncrypted(modifications); } if (InternalsConfig.consistencyChecks) { ItemDelta.checkConsistence(modifications); } OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".modifyObject"); result.addCollectionOfSerializablesAsParam("modifications", modifications); result.addParam(OperationResult.PARAM_OID, oid); result.addParam("scripts", scripts); result.addParam("options", options); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); if (LOGGER.isTraceEnabled()) { LOGGER.trace("*PROVISIONING: modifyObject: object modifications:\n{}", DebugUtil.debugDump(modifications)); } // getting object to modify PrismObject<T> repoShadow = getRepoObject(type, oid, null, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("**PROVISIONING: modifyObject: object to modify (repository):\n{}.", repoShadow.debugDump()); } try { if (ShadowType.class.isAssignableFrom(type)) { // calling shadow cache to modify object oid = getShadowCache(Mode.STANDARD).modifyShadow((PrismObject<ShadowType>)repoShadow, oid, modifications, scripts, options, task, result); } else { cacheRepositoryService.modifyObject(type, oid, modifications, result); } result.computeStatus(); } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: communication problem: " + e.getMessage(), e); throw e; } catch (GenericFrameworkException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: generic error in the connector: " + e.getMessage(), e); throw new CommunicationException(e.getMessage(), e); } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: schema problem: " + e.getMessage(), e); throw e; } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: object doesn't exist: " + e.getMessage(), e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: unexpected problem: " + e.getMessage(), e); throw new SystemException("Internal error: " + e.getMessage(), e); } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: configuration problem: " + e.getMessage(), e); throw e; } catch (SecurityViolationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: security violation: " + e.getMessage(), e); throw e; } catch (ObjectAlreadyExistsException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't modify object: object after modification would conflict with another existing object: " + e.getMessage(), e); throw e; } result.cleanupResult(); return oid; } @Override public <T extends ObjectType> void deleteObject(Class<T> type, String oid, ProvisioningOperationOptions options, OperationProvisioningScriptsType scripts, Task task, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException { Validate.notNull(oid, "Oid of object to delete must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); LOGGER.trace("**PROVISIONING: Start to delete object with oid {}", oid); OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".deleteObject"); result.addParam("oid", oid); result.addParam("scripts", scripts); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); //TODO: is critical when shadow does not exits anymore?? do we need to log it?? if not, change null to allowNotFound options PrismObject<T> object = getRepoObject(type, oid, null, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("**PROVISIONING: Object from repository to delete:\n{}", object.debugDump()); } if (object.canRepresent(ShadowType.class) && !ProvisioningOperationOptions.isRaw(options)) { try { getShadowCache(Mode.STANDARD).deleteShadow((PrismObject<ShadowType>)object, options, scripts, task, result); } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't delete object: communication problem: " + e.getMessage(), e); throw new CommunicationException(e.getMessage(), e); } catch (GenericFrameworkException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't delete object: generic error in the connector: " + e.getMessage(), e); throw new CommunicationException(e.getMessage(), e); } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't delete object: schema problem: " + e.getMessage(), e); throw new SchemaException(e.getMessage(), e); } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't delete object: configuration problem: " + e.getMessage(), e); throw e; } catch (SecurityViolationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't delete object: security violation: " + e.getMessage(), e); throw e; } catch (RuntimeException e){ ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't delete object: " + e.getMessage(), e); throw new SystemException(e); } } else if (object.canRepresent(ResourceType.class)) { resourceManager.deleteResource(oid, options, task, result); } else { try { getCacheRepositoryService().deleteObject(type, oid, result); } catch (ObjectNotFoundException ex) { result.recordFatalError(ex); result.cleanupResult(ex); throw ex; } } LOGGER.trace("**PROVISIONING: Finished deleting object."); result.computeStatus(); result.cleanupResult(); } /* (non-Javadoc) * @see com.evolveum.midpoint.provisioning.api.ProvisioningService#executeScript(java.lang.Class, java.lang.String, com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningScriptType, com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult) */ @Override public <T extends ObjectType> void executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ObjectAlreadyExistsException { Validate.notNull(resourceOid, "Oid of object for script execution must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".executeScript"); result.addParam("oid", resourceOid); result.addParam("script", script); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); try { resourceManager.executeScript(resourceOid, script, task, result); } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (SecurityViolationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (RuntimeException e){ ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } result.computeStatus(); result.cleanupResult(); } @Override public OperationResult testResource(String resourceOid) throws ObjectNotFoundException { // We are not going to create parent result here. We don't want to // pollute the result with // implementation details, as this will be usually displayed in the // table of "test resource" results. Validate.notNull(resourceOid, "Resource OID to test is null."); LOGGER.trace("Start testing resource with oid {} ", resourceOid); OperationResult testResult = new OperationResult(ConnectorTestOperation.TEST_CONNECTION.getOperation()); testResult.addParam("resourceOid", resourceOid); testResult.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); PrismObject<ResourceType> resource = null; try { resource = getRepoObject(ResourceType.class, resourceOid, null, testResult); resourceManager.testConnection(resource, testResult); } catch (SchemaException ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } testResult.computeStatus("Test resource has failed"); LOGGER.trace("Finished testing {}, result: {} ", resource, testResult.getStatus()); return testResult; } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public List<PrismObject<? extends ShadowType>> listResourceObjects(String resourceOid, QName objectClass, ObjectPaging paging, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { final OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".listResourceObjects"); result.addParam("resourceOid", resourceOid); result.addParam("objectClass", objectClass); result.addParam("paging", paging); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); if (resourceOid == null) { throw new IllegalArgumentException("Resource not defined in a search query"); } if (objectClass == null) { throw new IllegalArgumentException("Objectclass not defined in a search query"); } ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(resourceOid, objectClass, prismContext); final List<PrismObject<? extends ShadowType>> objectList = new ArrayList<PrismObject<? extends ShadowType>>(); final ShadowHandler shadowHandler = new ShadowHandler() { @Override public boolean handle(ShadowType shadow) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("listResourceObjects: processing shadow: {}", SchemaDebugUtil.prettyPrint(shadow)); } objectList.add(shadow.asPrismObject()); return true; } }; try { getShadowCache(Mode.STANDARD).searchObjectsIterative(query, null, shadowHandler, false, task, result); } catch (ConfigurationException ex) { result.recordFatalError(ex.getMessage(), ex); result.cleanupResult(ex); throw ex; } catch (SecurityViolationException ex) { result.recordFatalError(ex.getMessage(), ex); result.cleanupResult(ex); throw ex; } result.cleanupResult(); return objectList; } @Override public void refreshShadow(PrismObject<ShadowType> shadow, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ObjectAlreadyExistsException, SecurityViolationException { Validate.notNull(shadow, "Shadow for refresh must not be null."); OperationResult result = parentResult.createSubresult(ProvisioningServiceImpl.class.getName() +".finishOperation"); LOGGER.debug("Refreshing shadow {}", shadow); try { getShadowCache(Mode.RECON).refreshShadow(shadow, task, result); refreshShadowLegacy(shadow, options, task, result); } catch (GenericFrameworkException e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't refresh shadow: " + e.getClass().getSimpleName() + ": "+ e.getMessage(), e); throw new CommunicationException(e.getMessage(), e); } catch (CommunicationException | SchemaException | ObjectNotFoundException | ConfigurationException | SecurityViolationException | ObjectAlreadyExistsException | RuntimeException | Error e) { ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't refresh shadow: " + e.getClass().getSimpleName() + ": "+ e.getMessage(), e); throw e; } result.computeStatus(); result.cleanupResult(); LOGGER.debug("Finished refreshing shadow {}: ", shadow, result); } private void refreshShadowLegacy(PrismObject<ShadowType> shadow, ProvisioningOperationOptions options, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ObjectAlreadyExistsException, SecurityViolationException, GenericFrameworkException { ShadowType shadowType = shadow.asObjectable(); if (shadowType.getFailedOperationType() == null) { return; } else if (FailedOperationTypeType.ADD == shadowType.getFailedOperationType()) { getShadowCache(Mode.RECON).addShadow(shadow, null, null, options, task, result); } else if (FailedOperationTypeType.MODIFY == shadowType.getFailedOperationType()) { getShadowCache(Mode.RECON).modifyShadow(shadow, shadow.getOid(), new ArrayList<ItemDelta>(), null, options, task, result); } else if (FailedOperationTypeType.DELETE == shadowType.getFailedOperationType()) { getShadowCache(Mode.RECON).deleteShadow(shadow, options, null, task, result); } else { result.recordWarning("Missing or unknown type of operation to finish: " + shadowType.getFailedOperationType()); } } private <T extends ObjectType> boolean handleRepoObject(final Class<T> type, PrismObject<T> object, final Collection<SelectorOptions<GetOperationOptions>> options, final ResultHandler<T> handler, final OperationResult objResult) { PrismObject<T> completeObject; try { completeObject = completeObject(type, object, options, objResult); } catch (SchemaException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { object, e.getMessage(), e }); objResult.recordFatalError(e); object.asObjectable().setFetchResult(objResult.createOperationResultType()); completeObject = object; } catch (ObjectNotFoundException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { object, e.getMessage(), e }); objResult.recordFatalError(e); object.asObjectable().setFetchResult(objResult.createOperationResultType()); completeObject = object; } catch (CommunicationException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { object, e.getMessage(), e }); objResult.recordFatalError(e); object.asObjectable().setFetchResult(objResult.createOperationResultType()); completeObject = object; } catch (ConfigurationException e) { LOGGER.error("Error while completing {}: {}. Using non-complete object.", new Object[] { object, e.getMessage(), e }); objResult.recordFatalError(e); object.asObjectable().setFetchResult(objResult.createOperationResultType()); completeObject = object; } validateObject(completeObject); if (ShadowType.class.isAssignableFrom(type) && GetOperationOptions.isMaxStaleness(SelectorOptions.findRootOptions(options))) { CachingMetadataType cachingMetadata = ((ShadowType)completeObject.asObjectable()).getCachingMetadata(); if (cachingMetadata == null) { objResult.recordFatalError("Requested cached data but no cached data are available in the shadow"); } } objResult.computeStatus(); objResult.recordSuccessIfUnknown(); if (!objResult.isSuccess()) { OperationResultType resultType = objResult.createOperationResultType(); completeObject.asObjectable().setFetchResult(resultType); } return handler.handle(completeObject, objResult); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public <T extends ObjectType> SearchResultMetadata searchObjectsIterative(final Class<T> type, ObjectQuery query, final Collection<SelectorOptions<GetOperationOptions>> options, final ResultHandler<T> handler, Task task, final OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { Validate.notNull(parentResult, "Operation result must not be null."); Validate.notNull(handler, "Handler must not be null."); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Start to search object. Query {}", query != null ? query.debugDump() : "(null)"); } final OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".searchObjectsIterative"); result.setSummarizeSuccesses(true); result.setSummarizeErrors(true); result.setSummarizePartialErrors(true); result.addParam("query", query); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); ObjectFilter filter = null; if (query != null) { filter = ObjectQueryUtil.simplify(query.getFilter()); query = query.cloneEmpty(); query.setFilter(filter); } if (InternalsConfig.consistencyChecks && filter != null) { // We may not have all the definitions here. We will apply the definitions later filter.checkConsistence(false); } if (filter != null && filter instanceof NoneFilter) { result.recordSuccessIfUnknown(); result.cleanupResult(); LOGGER.trace("Finished searching. Nothing to do. Filter is NONE"); SearchResultMetadata metadata = new SearchResultMetadata(); metadata.setApproxNumberOfAllResults(0); return metadata; } final GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options); if (!ShadowType.class.isAssignableFrom(type)){ ResultHandler<T> internalHandler = (object, objResult) -> handleRepoObject(type, object, options, handler, objResult); Collection<SelectorOptions<GetOperationOptions>> repoOptions = null; if (GetOperationOptions.isReadOnly(rootOptions)) { repoOptions = SelectorOptions.createCollection(GetOperationOptions.createReadOnly()); } SearchResultMetadata metadata = null; try { metadata = getCacheRepositoryService().searchObjectsIterative(type, query, internalHandler, repoOptions, false, result); // TODO think about strictSequential flag result.computeStatus(); result.recordSuccessIfUnknown(); result.cleanupResult(); } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); } catch (Error e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); } return metadata; } final boolean shouldDoRepoSearch = ProvisioningUtil.shouldDoRepoSearch(rootOptions); final ShadowHandler shadowHandler = new ShadowHandler() { @Override public boolean handle(ShadowType shadowType) { OperationResult handleResult = result.createSubresult(ProvisioningService.class.getName() + ".searchObjectsIterative.handle"); if (shouldDoRepoSearch) { return handleRepoObject(type, (PrismObject<T>) shadowType.asPrismObject(), options, handler, handleResult); } if (shadowType == null) { throw new IllegalArgumentException("Null shadow in call to handler"); } if (LOGGER.isTraceEnabled()) { LOGGER.trace("searchObjectsIterative: processing shadow: {}", SchemaDebugUtil.prettyPrint(shadowType)); } boolean doContinue; try { PrismObject shadow = shadowType.asPrismObject(); validateObject(shadow); doContinue = handler.handle(shadow, handleResult); handleResult.computeStatus(); handleResult.recordSuccessIfUnknown(); if (!handleResult.isSuccess() && !handleResult.isHandledError()) { Collection<? extends ItemDelta> shadowModificationType = PropertyDelta .createModificationReplacePropertyCollection(ShadowType.F_RESULT, getResourceObjectShadowDefinition(), handleResult.createOperationResultType()); try { ConstraintsChecker.onShadowModifyOperation(shadowModificationType); cacheRepositoryService.modifyObject(ShadowType.class, shadowType.getOid(), shadowModificationType, result); } catch (ObjectNotFoundException ex) { result.recordFatalError("Saving of result to " + shadow + " shadow failed: Not found: " + ex.getMessage(), ex); } catch (ObjectAlreadyExistsException ex) { result.recordFatalError("Saving of result to " + shadow + " shadow failed: Already exists: " + ex.getMessage(), ex); } catch (SchemaException ex) { result.recordFatalError("Saving of result to " + shadow + " shadow failed: Schema error: " + ex.getMessage(), ex); } catch (RuntimeException e) { result.recordFatalError("Saving of result to " + shadow + " shadow failed: " + e.getMessage(), e); throw e; } } } catch (RuntimeException e) { result.recordFatalError(e); throw e; } finally { handleResult.computeStatus(); handleResult.recordSuccessIfUnknown(); // FIXME: hack. Hardcoded ugly summarization of successes. something like // AbstractSummarizingResultHandler [lazyman] if (result.isSuccess()) { result.getSubresults().clear(); } result.summarize(); } return doContinue; } }; SearchResultMetadata metadata; try { metadata = getShadowCache(Mode.STANDARD).searchObjectsIterative(query, options, shadowHandler, true, task, result); result.computeStatus(); } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (Error e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } finally { result.cleanupResult(); } return metadata; } @Override public Set<ConnectorType> discoverConnectors(ConnectorHostType hostType, OperationResult parentResult) throws CommunicationException { OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".discoverConnectors"); result.addParam("host", hostType); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); Set<ConnectorType> discoverConnectors; try { discoverConnectors = connectorManager.discoverConnectors(hostType, result); } catch (CommunicationException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Discovery failed: "+ex.getMessage(), ex); throw ex; } result.computeStatus("Connector discovery failed"); result.cleanupResult(); return discoverConnectors; } @Override public List<ConnectorOperationalStatus> getConnectorOperationalStatus(String resourceOid, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { OperationResult result = parentResult.createMinorSubresult(ProvisioningService.class.getName() + ".getConnectorOperationalStatus"); result.addParam("resourceOid", resourceOid); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); PrismObject<ResourceType> resource; try { resource = resourceManager.getResource(resourceOid, null, result); } catch (SchemaException | ObjectNotFoundException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, ex.getMessage(), ex); throw ex; } List<ConnectorOperationalStatus> stats; try { stats = resourceManager.getConnectorOperationalStatus(resource, result); } catch (ObjectNotFoundException | SchemaException | CommunicationException | ConfigurationException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Getting operations status from connector for resource "+resourceOid+" failed: "+ex.getMessage(), ex); throw ex; } result.computeStatus(); result.cleanupResult(); return stats; } @SuppressWarnings("unchecked") @Override public <T extends ObjectType> void applyDefinition(ObjectDelta<T> delta, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { applyDefinition(delta, null, parentResult); } @SuppressWarnings("unchecked") @Override public <T extends ObjectType> void applyDefinition(ObjectDelta<T> delta, Objectable object, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { OperationResult result = parentResult.createMinorSubresult(ProvisioningService.class.getName() + ".applyDefinition"); result.addParam("delta", delta); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); try { if (ShadowType.class.isAssignableFrom(delta.getObjectTypeClass())){ getShadowCache(Mode.STANDARD).applyDefinition((ObjectDelta<ShadowType>) delta, (ShadowType) object, result); } else if (ResourceType.class.isAssignableFrom(delta.getObjectTypeClass())){ resourceManager.applyDefinition((ObjectDelta<ResourceType>) delta, (ResourceType) object, null, result); } else { throw new IllegalArgumentException("Could not apply definition to deltas for object type: " + delta.getObjectTypeClass()); } result.recordSuccessIfUnknown(); result.cleanupResult(); } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } } @SuppressWarnings("unchecked") public <T extends ObjectType> void applyDefinition(PrismObject<T> object, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { OperationResult result = parentResult.createMinorSubresult(ProvisioningService.class.getName() + ".applyDefinition"); result.addParam(OperationResult.PARAM_OBJECT, object); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); try { if (ShadowType.class.isAssignableFrom(object.getCompileTimeClass())){ getShadowCache(Mode.STANDARD).applyDefinition((PrismObject<ShadowType>) object, result); } else if (ResourceType.class.isAssignableFrom(object.getCompileTimeClass())){ resourceManager.applyDefinition((PrismObject<ResourceType>) object, result); } else { throw new IllegalArgumentException("Could not apply definition to object type: " + object.getCompileTimeClass()); } result.computeStatus(); result.recordSuccessIfUnknown(); } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } finally { result.cleanupResult(); } } private void setProtectedShadow(PrismObject<ShadowType> shdaow, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { getShadowCache(Mode.STANDARD).setProtectedShadow(shdaow, result); } @Override public <T extends ObjectType> void applyDefinition(Class<T> type, ObjectQuery query, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { OperationResult result = parentResult.createMinorSubresult(ProvisioningService.class.getName() + ".applyDefinition"); result.addParam(OperationResult.PARAM_TYPE, type); result.addParam(OperationResult.PARAM_QUERY, query); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); try { if (ObjectQueryUtil.hasAllDefinitions(query)) { return; } if (ShadowType.class.isAssignableFrom(type)){ getShadowCache(Mode.STANDARD).applyDefinition(query, result); } else if (ResourceType.class.isAssignableFrom(type)){ resourceManager.applyDefinition(query, result); } else { throw new IllegalArgumentException("Could not apply definition to query for object type: " + type); } result.computeStatus(); result.recordSuccessIfUnknown(); } catch (ObjectNotFoundException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (CommunicationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (ConfigurationException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (SchemaException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } catch (RuntimeException e) { ProvisioningUtil.recordFatalError(LOGGER, result, null, e); throw e; } finally { result.cleanupResult(); } } @Override public void provisioningSelfTest(OperationResult parentTestResult, Task task) { CryptoUtil.securitySelfTest(parentTestResult); connectorManager.connectorFrameworkSelfTest(parentTestResult, task); } @Override public ProvisioningDiag getProvisioningDiag() { ProvisioningDiag provisioningDiag = new ProvisioningDiag(); String frameworkVersion = connectorManager.getFrameworkVersion(); if (frameworkVersion == null) { frameworkVersion = "unknown"; } provisioningDiag.getAdditionalDetails().add(new LabeledString(DETAILS_CONNECTOR_FRAMEWORK_VERSION, frameworkVersion)); return provisioningDiag; } /* * (non-Javadoc) * * @see * com.evolveum.midpoint.provisioning.api.ProvisioningService#initialize() */ @Override public void postInit(OperationResult parentResult) { OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".initialize"); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class); // Discover local connectors Set<ConnectorType> discoverLocalConnectors = connectorManager.discoverLocalConnectors(result); for (ConnectorType connector : discoverLocalConnectors) { LOGGER.info("Discovered local connector {}" + ObjectTypeUtil.toShortString(connector)); } result.computeStatus("Provisioning post-initialization failed"); result.cleanupResult(); } @PreDestroy public void shutdown() { connectorManager.shutdown(); } @Override public ConstraintsCheckingResult checkConstraints(RefinedObjectClassDefinition shadowDefinition, PrismObject<ShadowType> shadowObject, ResourceType resourceType, String shadowOid, ResourceShadowDiscriminator resourceShadowDiscriminator, ConstraintViolationConfirmer constraintViolationConfirmer, Task task, OperationResult parentResult) throws CommunicationException, ObjectAlreadyExistsException, SchemaException, SecurityViolationException, ConfigurationException, ObjectNotFoundException { OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".checkConstraints"); ConstraintsChecker checker = new ConstraintsChecker(); checker.setCacheRepositoryService(cacheRepositoryService); checker.setProvisioningService(this); checker.setPrismContext(prismContext); checker.setShadowDefinition(shadowDefinition); checker.setShadowObject(shadowObject); checker.setResourceType(resourceType); checker.setShadowOid(shadowOid); checker.setResourceShadowDiscriminator(resourceShadowDiscriminator); checker.setConstraintViolationConfirmer(constraintViolationConfirmer); try { ConstraintsCheckingResult retval = checker.check(task, result); result.computeStatus(); return retval; } catch (CommunicationException|ObjectAlreadyExistsException|SchemaException|SecurityViolationException|ConfigurationException|ObjectNotFoundException|RuntimeException e) { result.recordFatalError(e.getMessage(), e); throw e; } } @Override public void enterConstraintsCheckerCache() { ConstraintsChecker.enterCache(); } @Override public void exitConstraintsCheckerCache() { ConstraintsChecker.exitCache(); } private PrismObjectDefinition<ShadowType> getResourceObjectShadowDefinition() { if (resourceObjectShadowDefinition == null) { resourceObjectShadowDefinition = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass( ShadowType.class); } return resourceObjectShadowDefinition; } private <T extends ObjectType> PrismObject<T> getRepoObject(Class<T> type, String oid, GetOperationOptions options, OperationResult result) throws ObjectNotFoundException, SchemaException{ try { return getCacheRepositoryService().getObject(type, oid, null, result); } catch (ObjectNotFoundException e) { if (!GetOperationOptions.isAllowNotFound(options)){ ProvisioningUtil.recordFatalError(LOGGER, result, "Can't get object with oid " + oid + ". Reason " + e.getMessage(), e); } else { result.muteLastSubresultError(); result.computeStatus(); } throw e; } catch (SchemaException ex) { ProvisioningUtil.recordFatalError(LOGGER, result, "Can't get object with oid " + oid + ". Reason " + ex.getMessage(), ex); throw ex; } } private <T extends ObjectType> void validateObjects(Collection<PrismObject<T>> objects) { for(PrismObject<T> object: objects) { validateObject(object); } } private <T extends ObjectType> void validateObject(PrismObject<T> object) { Validate.notNull(object.getOid()); if (InternalsConfig.encryptionChecks) { CryptoUtil.checkEncrypted(object); } } }