/*
* Copyright (c) 2010-2016 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.consistency.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterEntry;
import com.evolveum.midpoint.prism.query.builder.S_FilterEntryOrEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.AndFilter;
import com.evolveum.midpoint.prism.query.EqualFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.OrFilter;
import com.evolveum.midpoint.prism.query.RefFilter;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;
import com.evolveum.midpoint.provisioning.api.ResourceObjectShadowChangeDescription;
import com.evolveum.midpoint.provisioning.consistency.api.ErrorHandler;
import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException;
import com.evolveum.midpoint.schema.ResultHandler;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
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.util.QNameUtil;
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.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
@Component
public class ObjectAlreadyExistHandler extends ErrorHandler {
@Autowired(required = true)
private ProvisioningService provisioningService;
@Autowired(required = true)
private PrismContext prismContext;
private static final Trace LOGGER = TraceManager.getTrace(ObjectAlreadyExistHandler.class);
@Override
public <T extends ShadowType> T handleError(T shadow, FailedOperation op, Exception ex,
boolean doDiscovery, boolean compensate,
Task task, OperationResult parentResult) throws SchemaException, GenericFrameworkException, CommunicationException,
ObjectNotFoundException, ObjectAlreadyExistsException, ConfigurationException, SecurityViolationException {
if (!doDiscovery) {
parentResult.recordFatalError(ex);
if (ex instanceof ObjectAlreadyExistsException) {
throw (ObjectAlreadyExistsException)ex;
} else {
throw new ObjectAlreadyExistsException(ex.getMessage(), ex);
}
}
LOGGER.trace("Start to hanlde ObjectAlreadyExitsException.");
OperationResult operationResult = parentResult
.createSubresult("com.evolveum.midpoint.provisioning.consistency.impl.ObjectAlreadyExistHandler.handleError." + op.name());
operationResult.addParam("shadow", shadow);
operationResult.addParam("currentOperation", op);
operationResult.addParam("exception", ex.getMessage());
ResourceObjectShadowChangeDescription change = new ResourceObjectShadowChangeDescription();
change.setResource(shadow.getResource().asPrismObject());
change.setSourceChannel(QNameUtil.qNameToUri(SchemaConstants.CHANGE_CHANNEL_DISCOVERY));
ObjectQuery query = createQueryByIcfName(shadow);
final List<PrismObject<ShadowType>> foundAccount = getExistingAccount(query, task, operationResult);
PrismObject<ShadowType> resourceAccount = null;
if (!foundAccount.isEmpty() && foundAccount.size() == 1) {
resourceAccount = foundAccount.get(0);
}
LOGGER.trace("Found conflicting resource object: {}", resourceAccount);
try{
if (resourceAccount != null) {
// Original object and found object share the same object class, therefore they must
// also share a kind. We can use this short-cut.
resourceAccount.asObjectable().setKind(shadow.getKind());
change.setCurrentShadow(resourceAccount);
// TODO: task initialization
// Task task = taskManager.createTaskInstance();
changeNotificationDispatcher.notifyChange(change, task, operationResult);
}
} finally {
operationResult.computeStatus();
}
if (operationResult.isSuccess()) {
parentResult.recordSuccess();
parentResult.muteLastSubresultError();
}
if (compensate){
throw new ObjectAlreadyExistsException(ex.getMessage(), ex);
}
return shadow;
}
private ObjectQuery createQueryByIcfName(ShadowType shadow) throws SchemaException {
// TODO: error handling TODO TODO TODO set matching rule instead of null in equlas filter
Collection<ResourceAttribute<?>> secondaryIdentifiers = ShadowUtil.getSecondaryIdentifiers(shadow);
S_AtomicFilterEntry q = QueryBuilder.queryFor(ShadowType.class, prismContext);
// secondary identifiers connected by 'or' clause
q = q.block();
for (ResourceAttribute<?> secondaryIdentifier : secondaryIdentifiers) {
q = q.itemAs(secondaryIdentifier).or();
}
q = q.none().endBlock().and();
// resource + object class
q = q.item(ShadowType.F_RESOURCE_REF).ref(shadow.getResourceRef().getOid()).and();
return q.item(ShadowType.F_OBJECT_CLASS).eq(shadow.getObjectClass()).build();
}
private List<PrismObject<ShadowType>> getExistingAccount(ObjectQuery query, Task task, OperationResult parentResult)
throws ObjectNotFoundException, CommunicationException, ConfigurationException, SchemaException,
SecurityViolationException {
final List<PrismObject<ShadowType>> foundAccount = new ArrayList<PrismObject<ShadowType>>();
ResultHandler<ShadowType> handler = new ResultHandler() {
@Override
public boolean handle(PrismObject object, OperationResult parentResult) {
return foundAccount.add(object);
}
};
provisioningService.searchObjectsIterative(ShadowType.class, query, null, handler, task, parentResult);
return foundAccount;
}
}