/* * Copyright (c) 2010-2013 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.provisioning.impl.mock; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.testng.AssertJUnit; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.provisioning.api.ChangeNotificationDispatcher; import com.evolveum.midpoint.provisioning.api.ResourceObjectChangeListener; import com.evolveum.midpoint.provisioning.api.ResourceObjectShadowChangeDescription; import com.evolveum.midpoint.provisioning.api.ResourceOperationDescription; import com.evolveum.midpoint.provisioning.api.ResourceOperationListener; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.ObjectChecker; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; @Service(value = "syncServiceMock") public class SynchornizationServiceMock implements ResourceObjectChangeListener, ResourceOperationListener { private static final Trace LOGGER = TraceManager.getTrace(SynchornizationServiceMock.class); private int callCountNotifyChange = 0; private int callCountNotifyOperation = 0; private boolean wasSuccess = false; private boolean wasFailure = false; private boolean wasInProgress = false; private ResourceObjectShadowChangeDescription lastChange = null; private ResourceOperationDescription lastOperationDescription = null; private ObjectChecker changeChecker; private boolean supportActivation = true; @Autowired(required=true) ChangeNotificationDispatcher notificationManager; @Autowired(required=true) RepositoryService repositoryService; @PostConstruct public void register() { notificationManager.registerNotificationListener((ResourceObjectChangeListener)this); notificationManager.registerNotificationListener((ResourceOperationListener)this); } @PreDestroy public void unregister() { notificationManager.unregisterNotificationListener((ResourceObjectChangeListener)this); notificationManager.unregisterNotificationListener((ResourceOperationListener)this); } public ObjectChecker getChangeChecker() { return changeChecker; } public void setChangeChecker(ObjectChecker changeChecker) { this.changeChecker = changeChecker; } public boolean isSupportActivation() { return supportActivation; } public void setSupportActivation(boolean supportActivation) { this.supportActivation = supportActivation; } @Override public void notifyChange(ResourceObjectShadowChangeDescription change, Task task, OperationResult parentResult) { LOGGER.debug("Notify change mock called with {}", change); // Some basic sanity checks assertNotNull("No change", change); assertNotNull("No task", task); assertNotNull("No resource", change.getResource()); assertNotNull("No parent result", parentResult); assertTrue("Either current shadow or delta must be present", change.getCurrentShadow() != null || change.getObjectDelta() != null); if (change.isUnrelatedChange() || isDryRun(task) || (change.getCurrentShadow() != null && change.getCurrentShadow().asObjectable().isProtectedObject() == Boolean.TRUE)){ return; } if (change.getCurrentShadow() != null) { ShadowType currentShadowType = change.getCurrentShadow().asObjectable(); if (currentShadowType != null) { // not a useful check..the current shadow could be null assertNotNull("Current shadow does not have an OID", change.getCurrentShadow().getOid()); assertNotNull("Current shadow does not have resourceRef", currentShadowType.getResourceRef()); assertNotNull("Current shadow has null attributes", currentShadowType.getAttributes()); assertFalse("Current shadow has empty attributes", ShadowUtil .getAttributesContainer(currentShadowType).isEmpty()); // Check if the shadow is already present in repo try { repositoryService.getObject(currentShadowType.getClass(), currentShadowType.getOid(), null, new OperationResult("mockSyncService.notifyChange")); } catch (Exception e) { AssertJUnit.fail("Got exception while trying to read current shadow "+currentShadowType+ ": "+e.getCause()+": "+e.getMessage()); } // Check resource String resourceOid = ShadowUtil.getResourceOid(currentShadowType); assertFalse("No resource OID in current shadow "+currentShadowType, StringUtils.isBlank(resourceOid)); try { repositoryService.getObject(ResourceType.class, resourceOid, null, new OperationResult("mockSyncService.notifyChange")); } catch (Exception e) { AssertJUnit.fail("Got exception while trying to read resource "+resourceOid+" as specified in current shadow "+currentShadowType+ ": "+e.getCause()+": "+e.getMessage()); } if (change.getCurrentShadow().asObjectable().getKind() == ShadowKindType.ACCOUNT) { ShadowType account = change.getCurrentShadow().asObjectable(); if (supportActivation) { assertNotNull("Current shadow does not have activation", account.getActivation()); assertNotNull("Current shadow activation status is null", account.getActivation() .getAdministrativeStatus()); } else { assertNull("Activation sneaked into current shadow", account.getActivation()); } } } } if (change.getOldShadow() != null) { assertNotNull("Old shadow does not have an OID", change.getOldShadow().getOid()); assertNotNull("Old shadow does not have an resourceRef", change.getOldShadow().asObjectable() .getResourceRef()); } if (change.getObjectDelta() != null) { assertNotNull("Delta has null OID", change.getObjectDelta().getOid()); } if (changeChecker != null) { changeChecker.check(change); } // remember ... callCountNotifyChange++; lastChange = change; } private static boolean isDryRun(Task task){ Validate.notNull(task, "Task must not be null."); if (task.getExtension() == null){ return false; } PrismProperty<Boolean> item = task.getExtensionProperty(SchemaConstants.MODEL_EXTENSION_DRY_RUN); if (item == null || item.isEmpty()){ return false; } if (item.getValues().size() > 1){ return false; // throw new SchemaException("Unexpected number of values for option 'dry run'."); } Boolean dryRun = item.getValues().iterator().next().getValue(); if (dryRun == null){ return false; } return dryRun.booleanValue(); } /* (non-Javadoc) * @see com.evolveum.midpoint.provisioning.api.ResourceObjectChangeListener#notifyFailure(com.evolveum.midpoint.provisioning.api.ResourceObjectShadowFailureDescription, com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult) */ @Override public void notifySuccess(ResourceOperationDescription opDescription, Task task, OperationResult parentResult) { notifyOp("success", opDescription, task, parentResult, false); wasSuccess = true; } /* (non-Javadoc) * @see com.evolveum.midpoint.provisioning.api.ResourceObjectChangeListener#notifyFailure(com.evolveum.midpoint.provisioning.api.ResourceObjectShadowFailureDescription, com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult) */ @Override public void notifyFailure(ResourceOperationDescription opDescription, Task task, OperationResult parentResult) { notifyOp("failure", opDescription, task, parentResult, true); wasFailure = true; } /* (non-Javadoc) * @see com.evolveum.midpoint.provisioning.api.ResourceObjectChangeListener#notifyFailure(com.evolveum.midpoint.provisioning.api.ResourceObjectShadowFailureDescription, com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult) */ @Override public void notifyInProgress(ResourceOperationDescription opDescription, Task task, OperationResult parentResult) { notifyOp("in-progress", opDescription, task, parentResult, false); wasInProgress = true; } private void notifyOp(String notificationDesc, ResourceOperationDescription opDescription, Task task, OperationResult parentResult, boolean failure) { LOGGER.debug("Notify "+notificationDesc+" mock called with:\n{}", opDescription.debugDump()); // Some basic sanity checks assertNotNull("No op description", opDescription); assertNotNull("No task", task); assertNotNull("No result", opDescription.getResult()); assertNotNull("No resource", opDescription.getResource()); assertNotNull("No parent result", parentResult); assertNotNull("Current shadow not present", opDescription.getCurrentShadow()); assertNotNull("Delta not present", opDescription.getObjectDelta()); if (opDescription.getCurrentShadow() != null) { ShadowType currentShadowType = opDescription.getCurrentShadow().asObjectable(); if (currentShadowType != null) { // not a useful check..the current shadow could be null if (!failure){ assertNotNull("Current shadow does not have an OID", opDescription.getCurrentShadow().getOid()); assertNotNull("Current shadow has null attributes", currentShadowType.getAttributes()); assertFalse("Current shadow has empty attributes", ShadowUtil .getAttributesContainer(currentShadowType).isEmpty()); } assertNotNull("Current shadow does not have resourceRef", currentShadowType.getResourceRef()); // Check if the shadow is already present in repo (if it is not a delete case) if (!opDescription.getObjectDelta().isDelete() && !failure){ try { repositoryService.getObject(currentShadowType.getClass(), currentShadowType.getOid(), null, new OperationResult("mockSyncService."+notificationDesc)); } catch (Exception e) { AssertJUnit.fail("Got exception while trying to read current shadow "+currentShadowType+ ": "+e.getCause()+": "+e.getMessage()); } } // Check resource String resourceOid = ShadowUtil.getResourceOid(currentShadowType); assertFalse("No resource OID in current shadow "+currentShadowType, StringUtils.isBlank(resourceOid)); try { repositoryService.getObject(ResourceType.class, resourceOid, null, new OperationResult("mockSyncService."+notificationDesc)); } catch (Exception e) { AssertJUnit.fail("Got exception while trying to read resource "+resourceOid+" as specified in current shadow "+currentShadowType+ ": "+e.getCause()+": "+e.getMessage()); } // FIXME: enable this check later..but for example, opendj // resource does not have native capability and if the reosurce // does not have sprecified simulated capability, this will // produce an error // if (opDescription.getCurrentShadow().asObjectable() instanceof AccountShadowType) { // AccountShadowType account = (AccountShadowType) opDescription.getCurrentShadow().asObjectable(); // assertNotNull("Current shadow does not have activation", account.getActivation()); // assertNotNull("Current shadow activation/enabled is null", account.getActivation() // .isEnabled()); // } else { // // We don't support other types now // AssertJUnit.fail("Unexpected type of shadow " + opDescription.getCurrentShadow().getClass()); // } } } if (opDescription.getObjectDelta() != null && !failure) { assertNotNull("Delta has null OID", opDescription.getObjectDelta().getOid()); } if (changeChecker != null) { changeChecker.check(opDescription); } // remember ... callCountNotifyOperation++; lastOperationDescription = opDescription; } public boolean wasCalledNotifyChange() { return (callCountNotifyChange > 0); } public void reset() { callCountNotifyChange = 0; callCountNotifyOperation = 0; lastChange = null; wasSuccess = false; wasFailure = false; wasInProgress = false; } public ResourceObjectShadowChangeDescription getLastChange() { return lastChange; } public void setLastChange(ResourceObjectShadowChangeDescription lastChange) { this.lastChange = lastChange; } public int getCallCount() { return callCountNotifyChange; } public void setCallCount(int callCount) { this.callCountNotifyChange = callCount; } public void assertNotifyChange() { assert wasCalledNotifyChange() : "Expected that notifyChange will be called but it was not"; } public void assertNoNotifyChange() { assert !wasCalledNotifyChange() : "Expected that no notifyChange will be called but it was"; } public void assertNotifySuccessOnly() { assert wasSuccess : "Expected that notifySuccess will be called but it was not"; assert !wasFailure : "Expected that notifyFailure will NOT be called but it was"; assert !wasInProgress : "Expected that notifyInProgress will NOT be called but it was"; assert callCountNotifyOperation == 1 : "Expected only a single notification call but there was "+callCountNotifyOperation+" calls"; } public void assertNotifyFailureOnly() { assert wasFailure : "Expected that notifyFailure will be called but it was not"; assert !wasSuccess : "Expected that notifySuccess will NOT be called but it was"; assert !wasInProgress : "Expected that notifyInProgress will NOT be called but it was"; assert callCountNotifyOperation == 1 : "Expected only a single notification call but there was "+callCountNotifyOperation+" calls"; } public void assertNotifyInProgressOnly() { assert wasInProgress : "Expected that notifyInProgress will be called but it was not"; assert !wasSuccess : "Expected that notifySuccess will NOT be called but it was"; assert !wasFailure : "Expected that notifyFailure will NOT be called but it was"; assert callCountNotifyOperation == 1 : "Expected only a single notification call but there was "+callCountNotifyOperation+" calls"; } public void assertNoNotifcations() { assert !wasInProgress : "Expected that notifyInProgress will NOT be called but it was"; assert !wasSuccess : "Expected that notifySuccess will NOT be called but it was"; assert !wasFailure : "Expected that notifyFailure will NOT be called but it was"; assert callCountNotifyOperation == 0 : "Expected no notification call but there was "+callCountNotifyOperation+" calls"; } /* (non-Javadoc) * @see com.evolveum.midpoint.provisioning.api.ResourceObjectChangeListener#getName() */ @Override public String getName() { return "synchronization service mock"; } }