/*
* 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.importer;
import com.evolveum.midpoint.model.impl.ModelConstants;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.PrismPropertyDefinitionImpl;
import com.evolveum.midpoint.provisioning.api.ChangeNotificationDispatcher;
import com.evolveum.midpoint.provisioning.api.ResourceObjectChangeListener;
import com.evolveum.midpoint.schema.result.OperationConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.*;
import com.evolveum.midpoint.task.api.TaskRunResult.TaskRunResultStatus;
import com.evolveum.midpoint.util.DOMUtil;
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.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.apache.commons.lang.NotImplementedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
import java.util.List;
/**
* Task handler for "Import objects from file" task.
* <p/>
* Import parses the input file and add all objects to the repository.
* <p/>
* The import task might be executed on a different node (as usual for async tasks), but this won't work as the file
* is not serializable. Therefore the task needs to be locked to the executing node. (TODO)
*
* @author Radovan Semancik
* @see TaskHandler
* @see ResourceObjectChangeListener
*/
@Component
public class ImportObjectsFromFileTaskHandler implements TaskHandler {
public static final String HANDLER_URI = ModelConstants.NS_IMPORT_OBJECTS_TASK_PREFIX + "/file/handler-3";
@Autowired(required = true)
private TaskManager taskManager;
@Autowired(required = true)
private ChangeNotificationDispatcher changeNotificationDispatcher;
@Autowired(required = true)
private PrismContext prismContext;
//private Map<Task,ImportAccountsFromResourceResultHandler> handlers;
private PrismPropertyDefinition filenamePropertyDefinition;
private static final Trace LOGGER = TraceManager.getTrace(ImportObjectsFromFileTaskHandler.class);
public ImportObjectsFromFileTaskHandler() {
super();
//handlers = new HashMap<Task, ImportAccountsFromResourceResultHandler>();
}
@PostConstruct
private void initialize() {
filenamePropertyDefinition = new PrismPropertyDefinitionImpl(ModelConstants.FILENAME_PROPERTY_NAME,
DOMUtil.XSD_STRING, prismContext); // must not be in the constructor, because prismContext is null at that time
taskManager.registerHandler(HANDLER_URI, this);
}
/**
* Launch an import. Calling this method will start import in a new
* thread, possibly on a different node.
*
* @param input
* @param task
* @param parentResult
*/
public void launch(File input, Task task, OperationResult parentResult) {
LOGGER.debug("Launching import accounts from file {}", input);
OperationResult result = parentResult.createSubresult(ImportObjectsFromFileTaskHandler.class.getName() + ".launch");
result.addParam("input", input);
// TODO
// Set handler URI so we will be called back
task.setHandlerUri(HANDLER_URI);
// Readable task name
PolyStringType polyString = new PolyStringType("Import from file " + input);
task.setName(polyString);
// TODO: bind task to this node
// Set filename
// Collection<? extends ItemDelta> modifications = new ArrayList<ItemDelta>(1);
// PropertyDelta objectClassDelta = new PropertyDelta<Object>(
// new PropertyPath(TaskType.F_EXTENSION, filenamePropertyDefinition.getName()),
// filenamePropertyDefinition);
// objectClassDelta.setValueToReplace(new PrismPropertyValue<Object>(input.getAbsolutePath()));
// ((Collection)modifications).add(objectClassDelta);
try {
PrismProperty filenameProp = filenamePropertyDefinition.instantiate();
filenameProp.setRealValue(input.getAbsolutePath());
task.setExtensionProperty(filenameProp);
task.savePendingModifications(result);
// task.modify(modifications, result);
} catch (ObjectNotFoundException e) {
LOGGER.error("Task object not found, expecting it to exist (task {})", task, e);
result.recordFatalError("Task object not found", e);
throw new IllegalStateException("Task object not found, expecting it to exist", e);
} catch (ObjectAlreadyExistsException e) {
LOGGER.error("Task object was not updated (task {})", task, e);
result.recordFatalError("Task object was not updated", e);
throw new IllegalStateException("Task object was not updated", e);
} catch (SchemaException e) {
LOGGER.error("Error dealing with schema (task {})", task, e);
result.recordFatalError("Error dealing with schema", e);
throw new IllegalStateException("Error dealing with schema", e);
}
// Switch task to background. This will start new thread and call
// the run(task) method.
// Note: the thread may be actually started on a different node
taskManager.switchToBackground(task, result);
result.setBackgroundTaskOid(task.getOid());
LOGGER.trace("Import objects from file {} switched to background, control thread returning with task {}", input, task);
}
/**
* The body of the task. This will start the import "loop".
*/
@Override
public TaskRunResult run(Task task) {
LOGGER.debug("Import objects from file run (task {})", task);
// This is an operation result for the entire import task. Therefore use the constant for
// operation name.
OperationResult opResult = task.getResult().createSubresult(OperationConstants.IMPORT_OBJECTS_FROM_FILE);
TaskRunResult runResult = new TaskRunResult();
runResult.setOperationResult(opResult);
runResult.setProgress(0);
// Determine the input file from task extension
PrismProperty<String> filenameProperty = task.getExtensionProperty(ModelConstants.FILENAME_PROPERTY_NAME);
if (filenameProperty == null) {
LOGGER.error("Import: No file specified");
opResult.recordFatalError("No file specified");
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
}
String filename = filenameProperty.getValue().getValue();
if (filename == null) {
LOGGER.error("Import: No file specified");
opResult.recordFatalError("No file specified");
runResult.setRunResultStatus(TaskRunResultStatus.PERMANENT_ERROR);
return runResult;
}
File input = new File(filename);
// TODO: test file existence, etc.
// TODO: do import
opResult.computeStatus("Errors during import");
// TODO: runResult.setProgress(progress);
runResult.setRunResultStatus(TaskRunResultStatus.FINISHED);
LOGGER.debug("Import objects from file run finished (task {}, run result {})", task, runResult);
return runResult;
}
@Override
public Long heartbeat(Task task) {
// Delegate heartbeat to the result handler
//TODO: return getHandler(task).heartbeat();
throw new NotImplementedException();
}
@Override
public void refreshStatus(Task task) {
// Local task. No refresh needed. The Task instance has always fresh data.
}
@Override
public String getCategoryName(Task task) {
return TaskCategory.IMPORT_FROM_FILE;
}
@Override
public List<String> getCategoryNames() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}