Experimenter.java
package progen.experimenter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import progen.ProGenException;
import progen.context.ProGenContext;
import progen.kernel.error.Error;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Interface que define los métodos de acceso a un experimento
*
* @author jirsis
* @since 2.0
*/
public abstract class Experimenter {
private static final String PROGEN_OUTPUT_DIR_PROPERTY = "progen.output.dir";
private static final String PROGEN_OPTIONAL_FILES_PROPERTY = "progen.optional.files";
private static final String PROGEN_EXPERIMENT_ABSOLUTE_FILE_PROPERTY = "progen.experiment.file.absolute";
private static final String SPACE_SYMBOL = " ";
private static final int PROGEN_ID_ERROR = 31;
public Experimenter() {
this.generateOutputDir();
this.dumpContext();
}
/**
* Copia de los ficheros que definen el contexto del experimento a la carpeta
* de salida definida en la propiedad "progen.output.dir" o en
* <user.bin>/outputs.
*/
private void dumpContext() {
final String masterFile = ProGenContext.getMandatoryProperty("progen.masterfile");
final String experimentFile = ProGenContext.getMandatoryProperty(PROGEN_EXPERIMENT_ABSOLUTE_FILE_PROPERTY);
final String optionalFiles = ProGenContext.getOptionalProperty(PROGEN_OPTIONAL_FILES_PROPERTY, "");
copyFile(masterFile, ProGenContext.getMandatoryProperty(PROGEN_OUTPUT_DIR_PROPERTY));
copyFile(experimentFile, ProGenContext.getMandatoryProperty(PROGEN_OUTPUT_DIR_PROPERTY));
for (String file : optionalFiles.trim().split(",[ ]*")) {
if (file.length() > 0) {
copyFile(file, ProGenContext.getMandatoryProperty(PROGEN_OUTPUT_DIR_PROPERTY));
}
}
}
/**
* Copia el fichero original definido como un path absoluto, en el path de
* destino, manteniendo el nombre del fichero original.
*
* @param original
* Path absoluto del fichero original a copiar.
* @param copyPath
* Path de destino de la copia.
*/
private void copyFile(String original, String copyPath) {
if (original == null || copyPath == null) {
throw new IllegalArgumentException(Error.get(PROGEN_ID_ERROR));
} else {
copyFileSecure(original, copyPath);
}
}
private void copyFileSecure(String original, String copyPath) {
final File originalFile = new File(original);
final File destinationFile = new File(String.format("%s%s%s", copyPath, File.separator, originalFile.getName()));
checkOriginalFileExists(original, originalFile);
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(originalFile);
outputStream = new FileOutputStream(destinationFile);
copyFile(inputStream, outputStream);
} catch (FileNotFoundException e) {
throw new ProGenException(String.format("%s[%s]", Error.get(PROGEN_ID_ERROR), original), e);
} catch (IOException e) {
throw new ProGenException(e.getLocalizedMessage(), e);
} finally {
closeSilentlyStream(inputStream);
closeSilentlyStream(outputStream);
}
}
private void checkOriginalFileExists(String original, final File originalFile) {
if (!originalFile.exists()) {
throw new ProGenException(String.format("%s%s[%s]", Error.get(PROGEN_ID_ERROR), SPACE_SYMBOL, original));
}
}
private void closeSilentlyStream(Closeable stream) {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
throw new ProGenException(e.getLocalizedMessage(), e);
}
}
private void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException {
final int bufferSize = 1024;
final byte[] buffer = new byte[bufferSize];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
}
public boolean deleteDirectory(File path) {
boolean deleted = true;
if (path.exists()) {
for (File file : path.listFiles()) {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
deleted = file.delete();
}
}
deleted = path.delete() && deleted;
} else {
deleted = false;
}
return deleted;
}
/**
* Crea la carpeta en la que se generarán todos los ficheros de salida que
* estén configurados. Se almacena esta ruta en la variable
* <code>progen.output.dir</code>
*
* @return <code>true</code> si se pudieron crear todas las carpetas
* necesarias y <code>false</code> en caso contrario.
*/
private boolean generateOutputDir() {
final StringBuilder defaultPath = generateDefaultPath();
final File dir = generateOutputDir(defaultPath);
ProGenContext.setProperty(PROGEN_OUTPUT_DIR_PROPERTY, dir.getAbsolutePath() + File.separator);
if (dir.exists()) {
deleteDirectory(dir);
}
return dir.mkdirs();
}
private File generateOutputDir(final StringBuilder defaultPath) {
final String outputDir = ProGenContext.getOptionalProperty(PROGEN_OUTPUT_DIR_PROPERTY, defaultPath.toString());
return new File(outputDir);
}
private StringBuilder generateDefaultPath() {
final StringBuilder defaultPath = new StringBuilder(100);
defaultPath.append("outputs");
defaultPath.append(File.separator);
defaultPath.append(ProGenContext.getMandatoryProperty("progen.experiment.name"));
return defaultPath;
}
/**
* Prepara la carpeta que contendrá los resultados de la ejecución de un
* experimento. El flujo de ejecución es: - defineExperimentDir() <br>
* - crea la carpeta <br>
* - generateResults()
*/
@SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "I don't really care makeDirs result")
public final void generateOutputs() {
String experimentDir = defineExperimentDir();
if (!experimentDir.endsWith(File.separator)) {
experimentDir += File.separator;
}
ProGenContext.setProperty("progen.output.experiment", experimentDir);
final File dir = new File(ProGenContext.getMandatoryProperty(PROGEN_OUTPUT_DIR_PROPERTY) + experimentDir);
dir.mkdir();
generateResults();
}
/**
* Comprueba si se dan las condiciones para dar por acabado el experimento.
*
* @return <code>true</code> si se acabó y <code>false</code> en caso
* contrario.
*/
public abstract boolean isDone();
/**
* Define, en ProGenContext, el valor de la propiedad que se está procesando
* en un momento determinado.
*
* @see ProGenContext
*/
public abstract void defineValues();
/**
* Incrementa el valor de las propiedades para generar un nuevo experimento
* concreto.
*/
public abstract void updateValues();
/**
* Define la propiedad <code>progen.output.experiment</code>, encargada de
* definir en que subcarpeta se almacenarán los resultados de un experimento
* concreto.
*
* @return Devuelve el subdirectorio en el que se guardarán los resultados
* concretos de un experimento.
*/
public abstract String defineExperimentDir();
/**
* Genera los ficheros de salida correspondiente al exeperimento que se haya
* ejecutado.
*/
public abstract void generateResults();
/**
* Devuelve una cadena con información de la finalización del experimento.
*
* @return La cadena con informacióin acerca de la finalización del
* experimento.
*/
public abstract String finishMessage();
}