Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Evolveum/midpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
KaterynaHonchar committed Mar 31, 2019
2 parents fa35595 + 2084330 commit dc9dbe1
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 0 deletions.
Expand Up @@ -169,6 +169,15 @@
</xsd:annotation>
</xsd:element>

<xsd:element name="notUpdatedShadowsDuration" type="xsd:duration">
<xsd:annotation>
<xsd:appinfo>
<a:minOccurs>0</a:minOccurs>
<a:maxOccurs>1</a:maxOccurs>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>

<xsd:element name="objectType" type="xsd:QName">
<xsd:annotation>
<xsd:documentation>
Expand Down
Expand Up @@ -49,4 +49,5 @@ public class ModelPublicConstants {
public static final String SCRIPT_EXECUTION_TASK_HANDLER_URI = SchemaConstants.NS_MODEL + "/scripting/handler-3";
public static final String ITERATIVE_SCRIPT_EXECUTION_TASK_HANDLER_URI = SchemaConstants.NS_MODEL + "/iterative-scripting/handler-3";
public static final String EXECUTE_DELTAS_TASK_HANDLER_URI = SchemaConstants.NS_MODEL + "/execute-deltas/handler-3";
public static final String DELETE_NOT_UPDATE_SHADOW_TASK_HANDLER_URI = NS_SYNCHRONIZATION_TASK_PREFIX + "/delete-not-updated-shadow/handler-3";
}
@@ -0,0 +1,277 @@
/*
* Copyright (c) 2010-2018 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.model.impl.sync;

import java.util.Collection;
import java.util.Date;

import javax.annotation.PostConstruct;
import javax.xml.datatype.Duration;

import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition;
import com.evolveum.midpoint.task.api.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchema;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.ModelPublicConstants;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.model.impl.ModelConstants;
import com.evolveum.midpoint.model.impl.util.AbstractSearchIterativeModelTaskHandler;
import com.evolveum.midpoint.model.impl.util.ModelImplUtils;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;
import com.evolveum.midpoint.provisioning.api.ResourceObjectShadowChangeDescription;
import com.evolveum.midpoint.repo.api.PreconditionViolationException;
import com.evolveum.midpoint.repo.common.task.AbstractSearchIterativeResultHandler;
import com.evolveum.midpoint.schema.result.OperationConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.util.exception.CommonException;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.PolicyViolationException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.LayerType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskPartitionDefinitionType;
import com.evolveum.prism.xml.ns._public.query_3.QueryType;

/**
* @author skublik
*/
@Component
public class DeleteNotUpdatedShadowTaskHandler extends AbstractSearchIterativeModelTaskHandler<ShadowType, AbstractSearchIterativeResultHandler<ShadowType>> {

public static final String HANDLER_URI = ModelPublicConstants.DELETE_NOT_UPDATE_SHADOW_TASK_HANDLER_URI;
private static final ItemName NOT_UPDATED_DURATION_PROPERTY_NAME = new ItemName(ModelConstants.NS_EXTENSION, "notUpdatedShadowsDuration");

@Autowired private TaskManager taskManager;
@Autowired private PrismContext prismContext;
@Autowired private SynchronizationService synchronizationService;
@Autowired private Clock clock;
@Autowired private ModelService model;
@Autowired private ProvisioningService provisioningService;

private static final transient Trace LOGGER = TraceManager.getTrace(DeleteNotUpdatedShadowTaskHandler.class);

public DeleteNotUpdatedShadowTaskHandler() {
super("DeleteNotUpdatedShadow", OperationConstants.RECONCILIATION);
setLogFinishInfo(true);
setPreserveStatistics(false);
}

@PostConstruct
private void initialize() {
taskManager.registerHandler(HANDLER_URI, this);
}

protected Class<? extends ObjectType> getType(Task task) {
return getTypeFromTask(task, ShadowType.class);
}

@Override
protected AbstractSearchIterativeResultHandler<ShadowType> createHandler(TaskPartitionDefinitionType partition, TaskRunResult runResult, final RunningTask coordinatorTask,
OperationResult opResult) {

AbstractSearchIterativeResultHandler<ShadowType> handler = new AbstractSearchIterativeResultHandler<ShadowType>(
coordinatorTask, DeleteNotUpdatedShadowTaskHandler.class.getName(), "delete not updated shadow", "delete not updated shadow task", partition, taskManager) {

@Override
protected boolean handleObject(PrismObject<ShadowType> object, RunningTask workerTask, OperationResult result) throws CommonException, PreconditionViolationException {
deleteShadow(object, getOptions(coordinatorTask), workerTask, result);
return true;
}

};
handler.setStopOnError(false);
return handler;
}

@Override
protected Collection<SelectorOptions<GetOperationOptions>> createSearchOptions(
AbstractSearchIterativeResultHandler<ShadowType> resultHandler, TaskRunResult runResult,
Task coordinatorTask, OperationResult opResult) {
return SelectorOptions.createCollection(GetOperationOptions.createNoFetch());
}

@Override
protected ObjectQuery createQuery(AbstractSearchIterativeResultHandler<ShadowType> handler, TaskRunResult runResult,
Task task, OperationResult opResult) throws SchemaException {
Duration notUpdatedDuration = task.getExtensionPropertyRealValue(NOT_UPDATED_DURATION_PROPERTY_NAME);
if(notUpdatedDuration == null) {
throw new IllegalArgumentException("Duration for deleting not updated shadow is missing in task extension");
}

String resourceOid = task.getObjectOid();

if (resourceOid == null) {
throw new IllegalArgumentException("Resource OID is missing in task extension");
}

PrismObject<ResourceType> resource = getResource(task);
ObjectClassComplexTypeDefinition objectclassDef = null;

RefinedResourceSchema refinedSchema;
try {
refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resource, LayerType.MODEL, prismContext);
objectclassDef = ModelImplUtils.determineObjectClass(refinedSchema, task);
} catch (SchemaException ex) {
// Not sure about this. But most likely it is a misconfigured resource or connector
// It may be worth to retry. Error is fatal, but may not be permanent.
processErrorPartial("Error dealing with schema", ex, opResult);
}

LOGGER.trace("Resource {}", resource);

if(notUpdatedDuration.getSign() == 1) {
notUpdatedDuration.negate();
}
Date deletingDate = new Date(clock.currentTimeMillis());
notUpdatedDuration.addTo(deletingDate);

QueryType queryType = new QueryType();

ObjectQuery query = prismContext.queryFor(ShadowType.class)
.block()
.item(ShadowType.F_FULL_SYNCHRONIZATION_TIMESTAMP).le(XmlTypeConverter.createXMLGregorianCalendar(deletingDate))
.or().item(ShadowType.F_FULL_SYNCHRONIZATION_TIMESTAMP).isNull()
.endBlock()
.and().item(ShadowType.F_RESOURCE_REF).ref(ObjectTypeUtil.createObjectRef(resource, prismContext).asReferenceValue())
.and().item(ShadowType.F_OBJECT_CLASS).eq(objectclassDef.getTypeName())
.build();

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Shadow query:\n{}", query.debugDump());
}

return query;
//
// try {
// return prismContext.getQueryConverter().createQueryType(query);
// } catch (SchemaException e) {
// LOGGER.error("Couldn't convert query " + query + " to QueryType", e);
// }
// return null;
}

private PrismObject<ResourceType> getResource(Task task) {

String resourceOid = task.getObjectOid();
if (resourceOid == null) {
throw new IllegalArgumentException("Resource OID is missing in task extension");
}

PrismObject<ResourceType> resource = null;
ObjectClassComplexTypeDefinition objectclassDef = null;
try {
resource = provisioningService.getObject(ResourceType.class, resourceOid, null, task, task.getResult());

RefinedResourceSchema refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resource, LayerType.MODEL, prismContext);
objectclassDef = ModelImplUtils.determineObjectClass(refinedSchema, task);
} catch (ObjectNotFoundException ex) {
// This is bad. The resource does not exist. Permanent problem.
processErrorPartial("Resource does not exist, OID: " + resourceOid, ex, task.getResult());
} catch (CommunicationException ex) {
// Error, but not critical. Just try later.
processErrorPartial("Communication error", ex, task.getResult());
} catch (SchemaException ex) {
// Not sure about this. But most likely it is a misconfigured resource or connector
// It may be worth to retry. Error is fatal, but may not be permanent.
processErrorPartial("Error dealing with schema", ex, task.getResult());
} catch (RuntimeException ex) {
// Can be anything ... but we can't recover from that.
// It is most likely a programming error. Does not make much sense
// to retry.
processErrorPartial("Internal Error", ex, task.getResult());
} catch (ConfigurationException ex) {
// Not sure about this. But most likely it is a misconfigured resource or connector
// It may be worth to retry. Error is fatal, but may not be permanent.
processErrorPartial("Configuration error", ex, task.getResult());
} catch (SecurityViolationException ex) {
processErrorPartial("Security violation", ex, task.getResult());
} catch (ExpressionEvaluationException ex) {
processErrorPartial("Expression error", ex, task.getResult());
}

if(resource == null) {
throw new IllegalArgumentException("Resource is null");
}

return resource;
}

private void processErrorPartial(String errorDesc, Exception ex, OperationResult opResult) {
String message;
if (ex == null) {
message = errorDesc;
} else {
message = errorDesc+": "+ex.getMessage();
}
LOGGER.error("Reconciliation: {}-{}", new Object[]{message, ex});
opResult.recordFatalError(message, ex);
}


private ModelExecuteOptions getOptions(Task coordinatorTask) throws SchemaException {
ModelExecuteOptions modelExecuteOptions = ModelImplUtils.getModelExecuteOptions(coordinatorTask);
if (modelExecuteOptions == null) {
modelExecuteOptions = ModelExecuteOptions.createReconcile();
}
LOGGER.trace("ModelExecuteOptions: {}", modelExecuteOptions);
return modelExecuteOptions;
}

private void deleteShadow(PrismObject<ShadowType> shadow, ModelExecuteOptions options, Task task, OperationResult result) throws SchemaException,
ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ObjectAlreadyExistsException,
ConfigurationException, PolicyViolationException, SecurityViolationException, PreconditionViolationException {
ResourceObjectShadowChangeDescription change = new ResourceObjectShadowChangeDescription();
change.setObjectDelta(shadow.createDeleteDelta());
change.setResource(getResource(task));
change.setOldShadow(shadow);
change.setCurrentShadow(shadow);
synchronizationService.notifyChange(change, task, result);
}

@Override
public String getCategoryName(Task task) {
return TaskCategory.RECONCILIATION;
}

@Override
protected String getDefaultChannel() {
return SchemaConstants.CHANGE_CHANNEL_RECON_URI;
}
}
Expand Up @@ -480,6 +480,9 @@ public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegra

protected static final File TASK_MOCK_JACK_FILE = new File(COMMON_DIR, "task-mock-jack.xml");
protected static final String TASK_MOCK_JACK_OID = "10000000-0000-0000-5656-565674633311";

protected static final String TASK_DELETE_NOT_UPDATED_SHADOWS = COMMON_DIR + "/task-delete-not-updated-shadows.xml";
protected static final String TASK_DELETE_NOT_UPDATED_SHADOWS_OID = "5b2ba49f-6d4f-4618-afdf-4d138117e40a";

public static final File LOOKUP_LANGUAGES_FILE = new File(COMMON_DIR, "lookup-languages.xml");
public static final String LOOKUP_LANGUAGES_OID = "70000000-0000-0000-1111-000000000001";
Expand Down
Expand Up @@ -15,18 +15,26 @@
*/
package com.evolveum.midpoint.model.intest.sync;

import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;

import java.io.FileNotFoundException;
import java.util.concurrent.TimeUnit;

import com.evolveum.midpoint.schema.constants.SchemaConstants;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.Test;

import com.evolveum.icf.dummy.resource.DummyAccount;
import com.evolveum.icf.dummy.resource.DummySyncStyle;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.DummyResourceContoller;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;

/**
* @author semancik
Expand Down Expand Up @@ -75,5 +83,57 @@ protected String getSyncTaskOid(PrismObject<ResourceType> resource) {
throw new IllegalArgumentException("Unknown resource "+resource);
}
}

@Test
public void test999DeletingNotUpdatedShadowDummyGreen() throws Exception {
final String TEST_NAME = "test800DeletingNotUpdatedShadowDummyGreen";
displayTestTitle(TEST_NAME);
String ACCOUNT_JACK_DUMMY_USERNAME = "jack";
String ACCOUNT_CAROL_DUMMY_USERNAME = "carol";


// GIVEN
Task task = createTask(AbstractSynchronizationStoryTest.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();
rememberTimeBeforeSync();
prepareNotifications();

DummyAccount accountCarol = new DummyAccount(ACCOUNT_CAROL_DUMMY_USERNAME);
accountCarol.setEnabled(true);
accountCarol.addAttributeValues(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME, "Carol Seepgood");
accountCarol.addAttributeValues(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOCATION_NAME, "Melee Island");

DummyAccount accountJack = new DummyAccount(ACCOUNT_JACK_DUMMY_USERNAME);
accountJack.setEnabled(true);
accountJack.addAttributeValues(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME, "Jack Sevenseas");
accountJack.addAttributeValues(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOCATION_NAME, "The Seven Seas");

/// WHEN
displayWhen(TEST_NAME);

getDummyResource(RESOURCE_DUMMY_GREEN_NAME).addAccount(accountJack);
waitForSyncTaskNextRunAssertSuccess(getDummyResourceObject(RESOURCE_DUMMY_GREEN_NAME));
importObjectFromFile(TASK_DELETE_NOT_UPDATED_SHADOWS);
TimeUnit.SECONDS.sleep(6);
getDummyResource(RESOURCE_DUMMY_GREEN_NAME).addAccount(accountCarol);
waitForSyncTaskNextRunAssertSuccess(getDummyResourceObject(RESOURCE_DUMMY_GREEN_NAME));
suspendTask(getSyncTaskOid(getDummyResourceObject(RESOURCE_DUMMY_GREEN_NAME)));
rerunTask(TASK_DELETE_NOT_UPDATED_SHADOWS_OID);

// THEN
displayThen(TEST_NAME);

PrismObject<UserType> userCarol = findUserByUsername(ACCOUNT_CAROL_DUMMY_USERNAME);
display("User carol", userCarol);
assertNotNull("No carol user", userCarol);

PrismObject<UserType> userJack = findUserByUsername(ACCOUNT_JACK_DUMMY_USERNAME);
display("User jack", userJack);
assertNull("User jack is not null", userJack);

// notifications
displayAllNotifications();
notificationManager.setDisabled(true);
}

}

0 comments on commit dc9dbe1

Please sign in to comment.