/*******************************************************************************
* Copyright 2006 - 2014 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* 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 eu.scape_project.planning.plato;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import javax.ejb.Asynchronous;
import javax.ejb.Stateful;
import javax.inject.Inject;
import org.slf4j.Logger;
import eu.scape_project.planning.manager.ByteStreamManager;
import eu.scape_project.planning.manager.DigitalObjectManager;
import eu.scape_project.planning.manager.StorageException;
import eu.scape_project.planning.model.Alternative;
import eu.scape_project.planning.model.DetailedExperimentInfo;
import eu.scape_project.planning.model.DigitalObject;
import eu.scape_project.planning.model.Plan;
import eu.scape_project.planning.model.SampleObject;
import eu.scape_project.planning.model.beans.MigrationResult;
import eu.scape_project.planning.model.interfaces.actions.IMigrationAction;
import eu.scape_project.planning.model.interfaces.actions.IPreservationAction;
import eu.scape_project.planning.model.measurement.Measurement;
import eu.scape_project.planning.model.tree.Leaf;
import eu.scape_project.planning.model.values.Value;
import eu.scape_project.planning.plato.bean.ExperimentStatus;
import eu.scape_project.planning.services.pa.PreservationActionServiceFactory;
/**
* Experiment runner to asynchronously execute experiments.
*/
@Stateful
public class ExperimentRunner implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
private Logger log;
@Inject
private DigitalObjectManager digitalObjectManager;
@Inject
private ByteStreamManager byteStreamManager;
private Plan plan;
private ExperimentStatus experimentStatus;
/**
* Runs all experiments scheduled in experimentStatus.
*
* @param plan
* the plan of the experiments
* @param experimentStatus
* the experiment status to run
*/
@Asynchronous
public void startExperiments(Plan plan, ExperimentStatus experimentStatus) {
this.plan = plan;
this.experimentStatus = experimentStatus;
Alternative alt = experimentStatus.getNextAlternative();
while (alt != null && !experimentStatus.isCanceled()) {
if (alt.isExecutable()) {
runPreservationAction(alt);
}
alt = experimentStatus.getNextAlternative();
}
System.gc();
}
/**
* Runs preservation actions for the provided alternative.
*
* @param a
* the alternative to run
*/
private void runPreservationAction(Alternative a) {
if (!a.isExecutable()) {
return;
}
IPreservationAction action = PreservationActionServiceFactory.getPreservationAction(a.getAction());
if (action == null) {
String msg = String
.format(
"Preservation action %s - %s is not registered or accessible and cant be executed. (Please check the registry.)",
a.getAction().getShortname(), a.getAction().getInfo());
setProgramOutputForAlternative(a, msg, false);
}
if (action instanceof IMigrationAction) {
IMigrationAction migrationAction = (IMigrationAction) action;
SampleObject record = experimentStatus.getNextSample();
while (record != null) {
if (record.isDataExistent()) {
MigrationResult migrationResult = null;
try {
DigitalObject workflow = a.getExperiment().getWorkflow();
if (workflow != null) {
byte[] workflowData = byteStreamManager.load(workflow.getPid());
workflow.getData().setData(workflowData);
}
DigitalObject objectToMigrate = digitalObjectManager.getCopyOfDataFilledDigitalObject(record);
migrationResult = migrationAction.migrate(a, objectToMigrate);
} catch (StorageException e) {
log.error("Failed to load sample object", e);
} catch (NullPointerException e) {
log.error(
"Caught nullpointer exception when running a migration tool. ### WRONG CONFIGURATION? ###",
e);
} catch (Throwable t) {
log.error("Caught unchecked exception when running a migration tool: " + t.getMessage(), t);
}
// Set detailed info before moving data to storage
extractDetailedInfos(a, record, migrationResult);
if (migrationResult != null) {
try {
if (migrationResult.isSuccessful()) {
DigitalObject experimentResultObject = a.getExperiment().getResults().get(record);
experimentResultObject.assignValues(migrationResult.getMigratedObject());
digitalObjectManager.moveDataToStorage(experimentResultObject);
addCriteria(a, record, migrationResult);
}
} catch (StorageException e) {
log.error("Could not store migration result", e);
migrationResult.setSuccessful(false);
migrationResult.setReport("Migration failed - could not store result.");
}
}
}
record = experimentStatus.getNextSample();
}
}
}
/**
* Adds values from the experiment results to the tree.
*
* @param alternative
* the alternative of the experiment run.
* @param sample
* the sample object
* @param migrationResult
* experiment results
*/
private void addCriteria(Alternative alternative, SampleObject sample, MigrationResult migrationResult) {
Map<String, Measurement> measurements = migrationResult.getMeasurements();
List<SampleObject> samples = plan.getSampleRecordsDefinition().getRecords();
List<Leaf> leaves = plan.getTree().getRoot().getAllLeaves();
for (Leaf leaf : leaves) {
if (leaf.isMapped()) {
Measurement measurement = measurements.get(leaf.getMeasure().getUri());
if (measurement != null && measurement.getValue() != null) {
Value value = leaf.getScale().createValue();
value.setComment(measurement.getValue().getComment());
try {
value.parse(measurement.getValue().toString());
} catch (Exception e) {
// Catch parsing exceptions
log.debug("Error parsing measure value", e);
}
int i = samples.indexOf(sample);
leaf.getValues(alternative.getName()).setValue(i, value);
}
}
}
}
/**
* For the given alternative the program output of all experiment infos is
* set to {@code msg}.
*
* @param a
* the alternative to update
* @param msg
* the message to set
* @param successful
* successful flag to set
*/
private void setProgramOutputForAlternative(Alternative a, String msg, boolean successful) {
List<SampleObject> sampleObjects = plan.getSampleRecordsDefinition().getRecords();
for (SampleObject o : sampleObjects) {
DetailedExperimentInfo info = a.getExperiment().getDetailedInfo().get(o);
if (info == null) {
info = new DetailedExperimentInfo();
a.getExperiment().getDetailedInfo().put(o, info);
}
info.setProgramOutput(msg);
info.setSuccessful(successful);
}
}
/**
* Stores {@link MigrationResult migration results} for the given sample
* object in {@link DetailedExperimentInfo experiment info} of the
* alternative {@code a}.
*
* @param a
* the alternative
* @param sample
* sample object of the experiment
* @param migrationResult
* migration result of the experiment
*/
private void extractDetailedInfos(Alternative a, SampleObject sample, MigrationResult migrationResult) {
DetailedExperimentInfo info = a.getExperiment().getDetailedInfo().get(sample);
if (info == null) {
info = new DetailedExperimentInfo();
a.getExperiment().getDetailedInfo().put(sample, info);
}
info.clear();
if (migrationResult == null) {
info.setProgramOutput(String.format("Applying action %s to sample %s failed.",
a.getAction().getShortname(), sample.getFullname()));
} else {
info.setSuccessful(migrationResult.isSuccessful());
if (migrationResult.getReport() == null) {
info.setProgramOutput("The tool didn't provide any output.");
} else {
info.setProgramOutput(migrationResult.getReport());
}
// Execution claimed to be successful but size = 0
DigitalObject migratedObject = migrationResult.getMigratedObject();
if (migrationResult.isSuccessful() && (migratedObject == null || migratedObject.getData().getSize() == 0)) {
info.setSuccessful(false);
info.setProgramOutput(info.getProgramOutput()
+ "\nSomething went wrong during migration. No result file has been generated.");
}
if (info.getSuccessful()) {
info.getMeasurements().putAll(migrationResult.getMeasurements());
}
}
}
}