/*
* 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.model.impl.sync;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchema;
import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl;
import com.evolveum.midpoint.model.impl.ModelConstants;
import com.evolveum.midpoint.model.impl.util.Utils;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition;
import com.evolveum.midpoint.schema.result.OperationConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskCategory;
import com.evolveum.midpoint.task.api.TaskHandler;
import com.evolveum.midpoint.task.api.TaskManager;
import com.evolveum.midpoint.task.api.TaskRunResult;
import com.evolveum.midpoint.task.api.TaskRunResult.TaskRunResultStatus;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
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.LayerType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* The task hander for a live synchronization.
*
* This handler takes care of executing live synchronization "runs". It means that the handler "run" method will
* be called every few seconds. The responsibility is to scan for changes that happened since the last run.
*
* @author Radovan Semancik
*
*/
@Component
public class LiveSyncTaskHandler implements TaskHandler {
public static final String HANDLER_URI = ModelConstants.NS_SYNCHRONIZATION_TASK_PREFIX + "/live-sync/handler-3";
@Autowired(required=true)
private TaskManager taskManager;
@Autowired(required=true)
private ProvisioningService provisioningService;
@Autowired(required = true)
private PrismContext prismContext;
private static final transient Trace LOGGER = TraceManager.getTrace(LiveSyncTaskHandler.class);
@PostConstruct
private void initialize() {
taskManager.registerHandler(HANDLER_URI, this);
}
@Override
public TaskRunResult run(Task task) {
task.startCollectingOperationStatsFromStoredValues(true, true, true);
try {
return runInternal(task);
} finally {
task.storeOperationStats();
}
}
private TaskRunResult runInternal(Task task) {
LOGGER.trace("LiveSyncTaskHandler.run starting");
long progress = task.getProgress();
OperationResult opResult = new OperationResult(OperationConstants.LIVE_SYNC);
TaskRunResult runResult = new TaskRunResult();
runResult.setOperationResult(opResult);
String resourceOid = task.getObjectOid();
if (resourceOid == null) {
LOGGER.error("Live Sync: No resource OID specified in the task");
opResult.recordFatalError("No resource OID specified in the task");
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
}
ResourceType resource = null;
try {
resource = provisioningService.getObject(ResourceType.class, resourceOid, null, task, opResult).asObjectable();
} catch (ObjectNotFoundException ex) {
LOGGER.error("Live Sync: Resource {} not found: {}", new Object[]{resourceOid, ex.getMessage(), ex});
// This is bad. The resource does not exist. Permanent problem.
opResult.recordFatalError("Resource not found " + resourceOid, ex);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
} catch (SchemaException ex) {
LOGGER.error("Live Sync: Error dealing with schema: {}", ex.getMessage(), 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.
opResult.recordFatalError("Error dealing with schema: " + ex.getMessage(), ex);
runResult.setRunResultStatus(TaskRunResultStatus.TEMPORARY_ERROR);
return runResult;
} catch (RuntimeException ex) {
LOGGER.error("Live Sync: Internal Error: {}", ex.getMessage(), 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.
opResult.recordFatalError("Internal Error: " + ex.getMessage(), ex);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
} catch (CommunicationException ex) {
LOGGER.error("Live Sync: Error getting resource {}: {}", new Object[]{resourceOid, ex.getMessage(), ex});
opResult.recordFatalError("Error getting resource " + resourceOid+": "+ex.getMessage(), ex);
runResult.setRunResultStatus(TaskRunResultStatus.TEMPORARY_ERROR);
return runResult;
} catch (ConfigurationException ex) {
LOGGER.error("Live Sync: Error getting resource {}: {}", new Object[]{resourceOid, ex.getMessage(), ex});
opResult.recordFatalError("Error getting resource " + resourceOid+": "+ex.getMessage(), ex);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
} catch (SecurityViolationException ex) {
LOGGER.error("Live Sync: Error getting resource {}: {}", new Object[]{resourceOid, ex.getMessage(), ex});
opResult.recordFatalError("Error getting resource " + resourceOid+": "+ex.getMessage(), ex);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
}
if (resource == null) {
LOGGER.error("Live Sync: No resource specified");
opResult.recordFatalError("No resource specified");
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
}
RefinedResourceSchema refinedSchema;
try {
refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resource, LayerType.MODEL, prismContext);
} catch (SchemaException e) {
LOGGER.error("Live Sync: Schema error during processing account definition: {}",e.getMessage());
opResult.recordFatalError("Schema error during processing account definition: "+e.getMessage(),e);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
}
if (refinedSchema == null){
opResult.recordFatalError("No refined schema defined. Probably some configuration problem.");
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
LOGGER.error("Live Sync: No refined schema defined. Probably some configuration problem.");
return runResult;
}
ObjectClassComplexTypeDefinition objectClass;
try {
objectClass = Utils.determineObjectClass(refinedSchema, task);
} catch (SchemaException e) {
LOGGER.error("Live Sync: schema error: {}", e.getMessage());
opResult.recordFatalError(e);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
}
if (objectClass == null) {
LOGGER.debug("Syncing all object classes");
}
ResourceShadowDiscriminator coords = new ResourceShadowDiscriminator(resourceOid, objectClass==null?null:objectClass.getTypeName());
int changesProcessed;
try {
// MAIN PART
// Calling synchronize(..) in provisioning.
// This will detect the changes and notify model about them.
// It will use extension of task to store synchronization state
Utils.clearRequestee(task);
changesProcessed = provisioningService.synchronize(coords, task, opResult);
progress += changesProcessed;
} catch (ObjectNotFoundException ex) {
LOGGER.error("Live Sync: A required object does not exist, OID: {}", ex.getOid());
LOGGER.error("Exception stack trace", ex);
// This is bad. The resource or task or something like that does not exist. Permanent problem.
opResult.recordFatalError("A required object does not exist, OID: " + ex.getOid(), ex);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
runResult.setProgress(progress);
return runResult;
} catch (CommunicationException ex) {
LOGGER.error("Live Sync: Communication error:",ex);
// Error, but not critical. Just try later.
opResult.recordPartialError("Communication error: "+ex.getMessage(),ex);
runResult.setRunResultStatus(TaskRunResultStatus.TEMPORARY_ERROR);
runResult.setProgress(progress);
return runResult;
} catch (SchemaException ex) {
LOGGER.error("Live Sync: Error dealing with schema:",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.
opResult.recordFatalError("Error dealing with schema: "+ex.getMessage(),ex);
runResult.setRunResultStatus(TaskRunResultStatus.TEMPORARY_ERROR);
runResult.setProgress(progress);
return runResult;
} catch (RuntimeException ex) {
LOGGER.error("Live Sync: Internal Error:", 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.
opResult.recordFatalError("Internal Error: "+ex.getMessage(),ex);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
runResult.setProgress(progress);
return runResult;
} catch (ConfigurationException ex) {
LOGGER.error("Live Sync: Configuration error:",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.
opResult.recordFatalError("Configuration error: "+ex.getMessage(),ex);
runResult.setRunResultStatus(TaskRunResultStatus.TEMPORARY_ERROR);
runResult.setProgress(progress);
return runResult;
} catch (SecurityViolationException ex) {
LOGGER.error("Recompute: Security violation: {}",ex.getMessage(),ex);
opResult.recordFatalError("Security violation: "+ex.getMessage(),ex);
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
runResult.setProgress(progress);
return runResult;
}
opResult.computeStatus("Live sync run has failed");
opResult.createSubresult(OperationConstants.LIVE_SYNC_STATISTICS).recordStatus(OperationResultStatus.SUCCESS, "Changes processed: " + changesProcessed);
// This "run" is finished. But the task goes on ...
runResult.setRunResultStatus(TaskRunResultStatus.FINISHED);
runResult.setProgress(progress); // Might collide with increasing progress in provisioning module, e.g. when an exception is thrown. But that's OK for now.
LOGGER.trace("LiveSyncTaskHandler.run stopping (resource {})", resourceOid);
return runResult;
}
@Override
public Long heartbeat(Task task) {
return null; // not to reset progress information
}
@Override
public void refreshStatus(Task task) {
// Do nothing. Everything is fresh already.
}
@Override
public String getCategoryName(Task task) {
return TaskCategory.LIVE_SYNCHRONIZATION;
}
@Override
public List<String> getCategoryNames() {
return null;
}
}