de.clusteval.framework.ClustevalBackendServer.java Source code

Java tutorial

Introduction

Here is the source code for de.clusteval.framework.ClustevalBackendServer.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Christian Wiwie.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Christian Wiwie - initial API and implementation
 ******************************************************************************/
/**
 * 
 */
package de.clusteval.framework;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.configuration.ConfigurationException;
import org.rosuda.REngine.Rserve.RserveException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import utils.Pair;
import utils.Triple;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import de.clusteval.cluster.paramOptimization.IncompatibleParameterOptimizationMethodException;
import de.clusteval.cluster.paramOptimization.InvalidOptimizationParameterException;
import de.clusteval.cluster.paramOptimization.UnknownParameterOptimizationMethodException;
import de.clusteval.cluster.quality.ClusteringQualityMeasure;
import de.clusteval.cluster.quality.UnknownClusteringQualityMeasureException;
import de.clusteval.context.IncompatibleContextException;
import de.clusteval.context.UnknownContextException;
import de.clusteval.data.DataConfigNotFoundException;
import de.clusteval.data.DataConfigurationException;
import de.clusteval.data.dataset.DataSet;
import de.clusteval.data.dataset.DataSetConfigNotFoundException;
import de.clusteval.data.dataset.DataSetConfigurationException;
import de.clusteval.data.dataset.DataSetNotFoundException;
import de.clusteval.data.dataset.IncompatibleDataSetConfigPreprocessorException;
import de.clusteval.data.dataset.NoDataSetException;
import de.clusteval.data.dataset.format.UnknownDataSetFormatException;
import de.clusteval.data.dataset.generator.DataSetGenerationException;
import de.clusteval.data.dataset.generator.DataSetGenerator;
import de.clusteval.data.dataset.generator.GoldStandardGenerationException;
import de.clusteval.data.dataset.generator.UnknownDataSetGeneratorException;
import de.clusteval.data.dataset.type.UnknownDataSetTypeException;
import de.clusteval.data.distance.UnknownDistanceMeasureException;
import de.clusteval.data.goldstandard.GoldStandardConfigNotFoundException;
import de.clusteval.data.goldstandard.GoldStandardConfigurationException;
import de.clusteval.data.goldstandard.GoldStandardNotFoundException;
import de.clusteval.data.goldstandard.format.UnknownGoldStandardFormatException;
import de.clusteval.data.preprocessing.UnknownDataPreprocessorException;
import de.clusteval.data.randomizer.DataRandomizer;
import de.clusteval.data.randomizer.UnknownDataRandomizerException;
import de.clusteval.data.statistics.UnknownDataStatisticException;
import de.clusteval.framework.repository.InvalidRepositoryException;
import de.clusteval.framework.repository.MyRengine;
import de.clusteval.framework.repository.NoRepositoryFoundException;
import de.clusteval.framework.repository.RegisterException;
import de.clusteval.framework.repository.Repository;
import de.clusteval.framework.repository.RepositoryAlreadyExistsException;
import de.clusteval.framework.repository.config.RepositoryConfigNotFoundException;
import de.clusteval.framework.repository.config.RepositoryConfigurationException;
import de.clusteval.framework.threading.RunSchedulerThread;
import de.clusteval.framework.threading.SupervisorThread;
import de.clusteval.program.NoOptimizableProgramParameterException;
import de.clusteval.program.Program;
import de.clusteval.program.UnknownParameterType;
import de.clusteval.program.UnknownProgramParameterException;
import de.clusteval.program.UnknownProgramTypeException;
import de.clusteval.program.r.UnknownRProgramException;
import de.clusteval.run.InvalidRunModeException;
import de.clusteval.run.RUN_STATUS;
import de.clusteval.run.Run;
import de.clusteval.run.RunException;
import de.clusteval.run.result.ParameterOptimizationResult;
import de.clusteval.run.result.RunResult;
import de.clusteval.run.result.RunResultParseException;
import de.clusteval.run.result.format.UnknownRunResultFormatException;
import de.clusteval.run.runnable.ExecutionRunRunnable;
import de.clusteval.run.runnable.IterationRunnable;
import de.clusteval.run.statistics.UnknownRunDataStatisticException;
import de.clusteval.run.statistics.UnknownRunStatisticException;
import de.clusteval.serverclient.BackendClient;
import de.clusteval.serverclient.IBackendServer;
import de.clusteval.utils.InvalidConfigurationFileException;
import de.clusteval.utils.MyHighlightingCompositeConverter;
import file.FileUtils;

/**
 * This class represents the server of the backend of the framework. The server
 * takes commands from the client (see {@link BackendClient}) like performing,
 * resuming or terminating runs, shutdown the framework or get status
 * information about various objects available in the repository (e.g. datasets,
 * runs, programs,...).
 * 
 * <p>
 * You can start the server by invoking the {@link #main(String[])} method. If
 * you do so, you can pass either a path to an existing repository or a new
 * repository is automatically created in the subfolder 'repository'.
 * 
 * <p>
 * When the server is started it registers itself in the RMI registry (remote
 * method invocation), either with the default port 1099 or if specified with
 * -hostport xxxx under any other port.
 * 
 * <p>
 * The start of the server requires a running Rserve instance. If this cannot be
 * found, the server will not start.
 * 
 * @author Christian Wiwie
 */
public class ClustevalBackendServer implements IBackendServer {

    /**
     * This variable holds the command line options of the backend server.
     */
    public static Options serverCLIOptions = new Options();

    protected static BackendServerConfig config = new BackendServerConfig();

    protected static String VERSION;

    /**
     * @return The configuration of this backend server.
     */
    public static BackendServerConfig getBackendServerConfiguration() {
        return config;
    }

    static {
        // read properties file with version number
        Properties prop = new Properties();
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        InputStream stream = loader.getResourceAsStream("server.date");
        try {
            prop.load(stream);
            VERSION = "Jar built: " + prop.getProperty("buildtime") + "\nGit:\n\tCommit: "
                    + prop.getProperty("gitrev") + "\n\tBranch: " + prop.getProperty("gitbranch")
                    + "\n\tRepository: " + prop.getProperty("gitrepo");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // init valid command line options
        OptionBuilder.withArgName("absRepositoryPath");
        OptionBuilder.hasArg();
        OptionBuilder.withDescription("The absolute path to the repository");
        Option optionAbsRepoPath = OptionBuilder.create("absRepoPath");
        serverCLIOptions.addOption(optionAbsRepoPath);

        OptionBuilder.withDescription("Print this help and usage information");
        Option optionHelp = OptionBuilder.create("help");
        serverCLIOptions.addOption(optionHelp);

        OptionBuilder.withDescription("Print the version of this client");
        Option optionVersion = OptionBuilder.create("version");
        serverCLIOptions.addOption(optionVersion);

        OptionBuilder.withArgName("port");
        OptionBuilder.hasArg();
        OptionBuilder.withDescription("The port this server should listen on");
        Option optionServerPort = OptionBuilder.create("port");
        serverCLIOptions.addOption(optionServerPort);

        OptionBuilder.withArgName("level");
        OptionBuilder.hasArg();
        OptionBuilder.withDescription(
                "The verbosity this server should use during the logging process. 0=ALL, 1=TRACE, 2=DEBUG, 3=INFO, 4=WARN, 5=ERROR, 6=OFF");
        OptionBuilder.withType(Integer.class);
        Option optionLogLevel = OptionBuilder.create("logLevel");
        serverCLIOptions.addOption(optionLogLevel);

        OptionBuilder.withArgName("number");
        OptionBuilder.hasArg();
        OptionBuilder.withDescription(
                "The maximal number of threads that should be created in parallel when executing runs.");
        OptionBuilder.withType(Integer.class);
        Option optionNoOfThreads = OptionBuilder.create("numberOfThreads");
        serverCLIOptions.addOption(optionNoOfThreads);

        OptionBuilder.withArgName("check");
        OptionBuilder.hasArg();
        OptionBuilder
                .withDescription("Indicates, whether this server should check for run results in its repository.");
        OptionBuilder.withType(Boolean.class);
        Option checkForRunResults = OptionBuilder.create("checkForRunResults");
        serverCLIOptions.addOption(checkForRunResults);

        OptionBuilder.withDescription("Indicates, whether this server should connect to a database.");
        Option noDatabase = OptionBuilder.create("noDatabase");
        serverCLIOptions.addOption(noDatabase);
    }

    /**
     * This variable holds the port this server will be listening on. It can be
     * specified by passing -hostport xxx to the {@link #main(String[])} method.
     */
    protected static int port;

    protected static boolean isRAvailable;

    private Logger log;

    /**
     * Every backend server has exactly one repository, which stores all the
     * data on the filesystem.
     */
    protected Repository repository;

    /**
     * The number of clients connected to this server so far. This number is
     * used to give new clients a new number for authentication in case, several
     * users connect to the server.
     */
    protected int clientCount;

    /**
     * This method returns file objects that can be used to synchronize process
     * wide access to files.
     * 
     * @param file
     *            The file object for which you want a common file object.
     * @return A common file object for the passed file, that is stored
     *         centrally such that synchronize operations on this file object
     *         affect all other methods, that also use this method.
     */
    public static File getCommonFile(final File file) {
        return FileUtils.getCommonFile(file);
    }

    /**
     * Instantiates a new backend server.
     * 
     * @param absRepositoryPath
     *            The absolute path to the repository used by this server.
     * @throws FileNotFoundException
     * @throws InvalidRepositoryException
     * @throws RepositoryAlreadyExistsException
     * @throws RepositoryConfigurationException
     * @throws RepositoryConfigNotFoundException
     * @throws InterruptedException
     */
    public ClustevalBackendServer(final String absRepositoryPath)
            throws FileNotFoundException, RepositoryAlreadyExistsException, InvalidRepositoryException,
            RepositoryConfigNotFoundException, RepositoryConfigurationException, InterruptedException {
        this(new Repository(absRepositoryPath, null));
    }

    /**
     * Instantiates a new backend server and registers the server at the RMI
     * registry.
     * 
     * @param repository
     *            The repository used by this server.
     * @throws InterruptedException
     */
    public ClustevalBackendServer(final Repository repository) throws InterruptedException {
        this(repository, true);
    }

    /**
     * @param repository
     * @param registerServer
     * @throws InterruptedException
     */
    public ClustevalBackendServer(final Repository repository, final boolean registerServer)
            throws InterruptedException {
        super();

        this.log = LoggerFactory.getLogger(this.getClass());

        this.repository = repository;

        this.log.info("Using repository at '" + this.repository.getBasePath() + "'");

        if (!registerServer || ClustevalBackendServer.registerServer(this)) {
            /*
             * Check, whether the repository is being initialized
             */
            if (this.repository.getSupervisorThread() == null)
                this.repository.initialize();
        }
    }

    /**
     * Gets the repository.
     * 
     * @return The repository used by this server.
     * @see #repository
     */
    public Repository getRepository() {
        synchronized (this.repository) {
            return this.repository;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#performRun(java.lang.String,
     * java.lang.String)
     */
    @Override
    public boolean performRun(final String clientId, final String runId) {
        boolean result = this.repository.getSupervisorThread().getRunScheduler().schedule(clientId, runId);
        return result;
    }

    @Override
    public boolean resumeRun(final String clientId, final String uniqueRunIdentifier) {
        boolean result = this.repository.getSupervisorThread().getRunScheduler().scheduleResume(clientId,
                uniqueRunIdentifier);
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#performRun(java.lang.String,
     * java.lang.String)
     */
    @Override
    public boolean terminateRun(final String clientId, final String runId) {
        boolean result = this.repository.getSupervisorThread().getRunScheduler().terminate(clientId, runId);
        return result;
    }

    /**
     * This method can be used to start a backend server. The args parameter can
     * contain options that specify the behaviour of the server:
     * <ul>
     * <li><b>-absRepositoryPath <absRepoPath></b>: An absolute path to the
     * repository</li>
     * <li><b>-port <port></b>: The port on which this server should listen</li>
     * </ul>
     * 
     * @param args
     *            Arguments to control the behaviour of the server
     * @throws FileNotFoundException
     * @throws InvalidRepositoryException
     * @throws RepositoryAlreadyExistsException
     * @throws RepositoryConfigurationException
     * @throws RepositoryConfigNotFoundException
     * @throws InterruptedException
     */
    public static void main(String[] args)
            throws FileNotFoundException, RepositoryAlreadyExistsException, InvalidRepositoryException,
            RepositoryConfigNotFoundException, RepositoryConfigurationException, InterruptedException {

        // bugfix for log4j warning
        org.apache.log4j.Logger.getRootLogger().setLevel(org.apache.log4j.Level.OFF);
        org.apache.log4j.Logger.getRootLogger().addAppender(new org.apache.log4j.ConsoleAppender(
                new org.apache.log4j.PatternLayout("%d %-5p %c - %F:%L - %m%n")));

        CommandLineParser parser = new PosixParser();
        try {
            CommandLine cmd = parser.parse(serverCLIOptions, args);

            if (cmd.hasOption("help")) {
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp("clustevalServer", "clusteval backend server " + VERSION, serverCLIOptions, "");
                System.exit(0);
            }

            if (cmd.hasOption("version")) {
                System.out.println(VERSION);
                System.exit(0);
            }

            if (cmd.getArgList().size() > 0)
                throw new ParseException("Unknown parameters: " + Arrays.toString(cmd.getArgs()));

            if (cmd.hasOption("port"))
                port = Integer.parseInt(cmd.getOptionValue("port"));
            else
                port = 1099;

            initLogging(cmd);

            if (cmd.hasOption("numberOfThreads"))
                config.numberOfThreads = Integer.parseInt(cmd.getOptionValue("numberOfThreads"));

            if (cmd.hasOption("checkForRunResults"))
                config.setCheckForRunResults(Boolean.parseBoolean(cmd.getOptionValue("checkForRunResults")));

            if (cmd.hasOption("noDatabase"))
                config.setNoDatabase(true);

            Logger log = LoggerFactory.getLogger(ClustevalBackendServer.class);

            System.out.println("Starting clusteval server");
            System.out.println(VERSION);
            System.out.println("=========================");

            try {
                // try to establish a connection to R
                @SuppressWarnings("unused")
                MyRengine myRengine = new MyRengine("");
                isRAvailable = true;
            } catch (RserveException e) {
                log.error("Connection to Rserve could not be established, "
                        + "please ensure that your Rserve instance is "
                        + "running before starting this framework.");
                log.error(
                        "Functionality that requires R will not be available" + " until Rserve has been started.");
                isRAvailable = false;
            }

            @SuppressWarnings("unused")
            ClustevalBackendServer clusteringEvalFramework;
            if (cmd.hasOption("absRepoPath"))
                clusteringEvalFramework = new ClustevalBackendServer(cmd.getOptionValue("absRepoPath"));
            else
                clusteringEvalFramework = new ClustevalBackendServer(new File("repository").getAbsolutePath());

        } catch (ParseException e1) {
            System.err.println("Parsing failed.  Reason: " + e1.getMessage());

            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp("clustevalServer", "clusteval backend server " + VERSION, serverCLIOptions, "");
        }
    }

    /**
     * @return True if R is available through Rserve, false otherwise.
     */
    public static boolean isRAvailable() {
        return isRAvailable;
    }

    /**
     * This method is responsible for creating all the appender that are added
     * to the logger.
     * <p>
     * Three appenders are created:
     * <ul>
     * <li><b>ConsoleAppender</b>: Writes the logging output to the standard out
     * </li>
     * <li><b>FileAppender</b>: Writes the logging output as formatter text to
     * the file clustevalServer.log</li>
     * <li><b>FileAppender</b>: Writes the logging output in lilith binary
     * format to the file clustevalServer.lilith</li>
     * </ul>
     * 
     * @param cmd
     *            The command line parameters including possible options of
     *            logging
     * @throws ParseException
     */
    private static void initLogging(CommandLine cmd) throws ParseException {
        Logger log = LoggerFactory.getLogger(ClustevalBackendServer.class);

        Level logLevel;
        if (cmd.hasOption("logLevel")) {
            switch (Integer.parseInt(cmd.getOptionValue("logLevel"))) {
            case 0:
                logLevel = Level.ALL;
                break;
            case 1:
                logLevel = Level.TRACE;
                break;
            case 2:
                logLevel = Level.DEBUG;
                break;
            case 3:
                logLevel = Level.INFO;
                break;
            case 4:
                logLevel = Level.WARN;
                break;
            case 5:
                logLevel = Level.ERROR;
                break;
            case 6:
                logLevel = Level.OFF;
                break;
            default:
                throw new ParseException("The logLevel argument requires one of the value of [0,1,2,3,4,5,6]");
            }
        } else {
            logLevel = Level.INFO;
        }

        ch.qos.logback.classic.Logger logger = ((ch.qos.logback.classic.Logger) LoggerFactory
                .getLogger(Logger.ROOT_LOGGER_NAME));
        logger.setLevel(logLevel);

        ConsoleAppender<ILoggingEvent> consoleApp = (ConsoleAppender<ILoggingEvent>) logger.iteratorForAppenders()
                .next();
        consoleApp.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
        consoleApp.setEncoder(new PatternLayoutEncoder());
        consoleApp.setWithJansi(true);
        PatternLayout layout = new PatternLayout();
        layout.getDefaultConverterMap().put("highlight", MyHighlightingCompositeConverter.class.getName());
        layout.setPattern("@localhost:" + port
                + " %date{dd MMM yyyy HH:mm:ss.SSS} %highlight([%thread] %-5level %logger{35} - %msg) %n");
        layout.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
        layout.start();
        consoleApp.setLayout(layout);
        consoleApp.start();
        logger.addAppender(consoleApp);

        // file appender for clustevalServer.log plaintext file
        FileAppender<ILoggingEvent> fileApp = new FileAppender<ILoggingEvent>();
        fileApp.setName("serverLogFile");
        String logFilePath = FileUtils.buildPath(System.getProperty("user.dir"), "clustevalServer.log");
        fileApp.setFile(logFilePath);

        fileApp.setAppend(true);
        fileApp.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
        fileApp.setEncoder(new PatternLayoutEncoder());
        layout = new PatternLayout();
        layout.setPattern(
                "@localhost:" + port + " %date{dd MMM yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n");
        layout.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
        layout.start();
        fileApp.setLayout(layout);
        fileApp.start();
        logger.addAppender(fileApp);

        // file appender for clustevalServer.lilith binary file
        // removed 30.01.2013
        // FileAppender fileAppLilith = new FileAppender();
        // fileAppLilith.setName("serverLogFileLilith");
        // logFilePath = FileUtils.buildPath(System.getProperty("user.dir"),
        // "clustevalServer.lilith");
        // fileAppLilith.setFile(logFilePath);
        //
        // fileAppLilith.setAppend(true);
        // fileAppLilith.setContext((LoggerContext) LoggerFactory
        // .getILoggerFactory());
        // ClassicLilithEncoder encoder = new ClassicLilithEncoder();
        // encoder.setIncludeCallerData(true);
        // fileAppLilith.setEncoder(encoder);
        //
        // fileAppLilith.start();
        // logger.addAppender(fileAppLilith);

        log.debug("Using log level " + logLevel);
    }

    /**
     * A helper method for {@link #ClusteringEvalFramework(Repository)}, which
     * registers the new backend server instance in the RMI registry.
     * 
     * @param framework
     *            The backend server to register.
     * @return True, if the server has been registered successfully
     */
    protected static boolean registerServer(ClustevalBackendServer framework) {
        Logger log = LoggerFactory.getLogger(ClustevalBackendServer.class);

        try {
            LocateRegistry.createRegistry(port);

            IBackendServer stub = (IBackendServer) UnicastRemoteObject.exportObject(framework, port);
            Registry registry = LocateRegistry.getRegistry(port);
            registry.bind("EvalServer", stub);
            log.info("Framework up and listening on port " + port);
            log.info("Used number of processors: " + config.numberOfThreads);
            return true;
        } catch (AlreadyBoundException e) {
            log.error("Another instance is already running...");
            return false;
        } catch (RemoteException e) {
            log.error("Another instance is already running...");
            return false;
        }
    }

    /**
     * A helper method for {@link #shutdown(String, long)}, which terminates
     * this framework after a certain timeout.
     * 
     * <p>
     * This method first interrupts the supervisor thread (see
     * {@link SupervisorThread}) and waits for its termination until the timeout
     * was reached.
     * 
     * <p>
     * Then the backend server instance is unregistered from the RMI registry
     * and the repository of this framework is removed from the set of all
     * registered repositories.
     * 
     * @throws InterruptedException
     * 
     */
    private void terminate(final long forceTimeout) throws InterruptedException {
        this.repository.terminateSupervisorThread();

        try {
            Registry registry = LocateRegistry.getRegistry(port);
            registry.unbind("EvalServer");
            UnicastRemoteObject.unexportObject(this, true);
        } catch (NoSuchObjectException e) {
            e.printStackTrace();
        } catch (AccessException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (NotBoundException e) {
            e.printStackTrace();
        }
        /*
         * unregister repository
         */
        Repository.unregister(this.repository);
        // should work without, just to be sure
        // System.exit(0);
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#getClientId()
     */
    @SuppressWarnings("unused")
    @Override
    public String getClientId() throws RemoteException {
        return "" + this.clientCount++;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#getRunStatusForClientId(java.lang.String)
     */
    @SuppressWarnings("unused")
    @Override
    public Map<String, Pair<RUN_STATUS, Float>> getRunStatusForClientId(String clientId) throws RemoteException {
        return this.repository.getSupervisorThread().getRunScheduler().getRunStatusForClientId(clientId);
    }

    // TODO
    @Override
    public Map<String, Pair<Pair<RUN_STATUS, Float>, Map<Pair<String, String>, Map<String, Pair<Map<String, String>, String>>>>> getOptimizationRunStatusForClientId(
            String clientId) throws RemoteException {
        return this.repository.getSupervisorThread().getRunScheduler()
                .getOptimizationRunStatusForClientId(clientId);
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#getRuns()
     */
    @Override
    public Collection<String> getRuns() {
        Collection<String> result = new HashSet<String>();
        for (Run run : this.repository.getCollectionStaticEntities(Run.class))
            result.add(run.getName());
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#getDataSets()
     */
    @Override
    public Collection<String> getDataSets() {
        Collection<String> result = new HashSet<String>();
        for (DataSet dataSet : this.repository.getCollectionStaticEntities(DataSet.class))
            result.add(dataSet.getFullName());
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#getPrograms()
     */
    @Override
    public Collection<String> getPrograms() {
        Collection<String> result = new HashSet<String>();
        for (Program program : this.repository.getCollectionStaticEntities(Program.class))
            result.add(program.getMajorName());
        return result;
    }

    // TODO: only certain clientids should be able to shutdown the framework
    @SuppressWarnings("unused")
    @Override
    public void shutdown(final String clientId, final long timeOut) {
        log.info("Shutting down framework...");
        try {
            this.terminate(timeOut);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @return True, if this framework is still running and the corresponding
     *         supervisor thread hasn't been interrupted.
     */
    public boolean isRunning() {
        return this.repository.getSupervisorThread().isAlive();
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#getRunResumes()
     */
    @SuppressWarnings("unused")
    @Override
    public Collection<String> getRunResumes() throws RemoteException {
        Collection<String> result = new HashSet<String>(this.repository.getRunResumes());
        return result;
    }

    @SuppressWarnings("unused")
    @Override
    public Collection<String> getRunResults() throws RemoteException {
        Collection<String> result = new HashSet<String>(this.repository.getRunResultIdentifier());
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.EvalServer#getRunResults(java.lang.String,
     * java.lang.String)
     */
    @SuppressWarnings("unused")
    @Override
    public Map<Pair<String, String>, Map<String, Double>> getRunResults(String uniqueRunIdentifier)
            throws RemoteException {
        Map<Pair<String, String>, Map<String, Double>> result = new HashMap<Pair<String, String>, Map<String, Double>>();

        List<ParameterOptimizationResult> list = new ArrayList<ParameterOptimizationResult>();
        try {
            ParameterOptimizationResult.parseFromRunResultFolder(repository,
                    new File(FileUtils.buildPath(repository.getBasePath(RunResult.class), uniqueRunIdentifier)),
                    list, false, false, false);
            for (ParameterOptimizationResult r : list) {
                String dataConfig = r.getMethod().getDataConfig().getName();
                String programConfig = r.getMethod().getProgramConfig().getName();
                Map<String, Double> measureToOptimalQuality = new HashMap<String, Double>();
                for (ClusteringQualityMeasure measure : r.getOptimalParameterSets().keySet()) {
                    measureToOptimalQuality.put(measure.getClass().getSimpleName(),
                            r.get(r.getOptimalParameterSets().get(measure)).get(measure).getValue());
                }
                result.put(Pair.getPair(dataConfig, programConfig), measureToOptimalQuality);
            }
        } catch (GoldStandardConfigurationException e) {
            e.printStackTrace();
        } catch (DataSetConfigurationException e) {
            e.printStackTrace();
        } catch (DataSetNotFoundException e) {
            e.printStackTrace();
        } catch (DataSetConfigNotFoundException e) {
            e.printStackTrace();
        } catch (GoldStandardConfigNotFoundException e) {
            e.printStackTrace();
        } catch (DataConfigurationException e) {
            e.printStackTrace();
        } catch (DataConfigNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (UnknownRunResultFormatException e) {
            e.printStackTrace();
        } catch (UnknownDataSetFormatException e) {
            e.printStackTrace();
        } catch (UnknownClusteringQualityMeasureException e) {
            e.printStackTrace();
        } catch (InvalidRunModeException e) {
            e.printStackTrace();
        } catch (UnknownParameterOptimizationMethodException e) {
            e.printStackTrace();
        } catch (NoOptimizableProgramParameterException e) {
            e.printStackTrace();
        } catch (UnknownProgramParameterException e) {
            e.printStackTrace();
        } catch (UnknownGoldStandardFormatException e) {
            e.printStackTrace();
        } catch (InvalidConfigurationFileException e) {
            e.printStackTrace();
        } catch (RepositoryAlreadyExistsException e) {
            e.printStackTrace();
        } catch (InvalidRepositoryException e) {
            e.printStackTrace();
        } catch (NoRepositoryFoundException e) {
            e.printStackTrace();
        } catch (GoldStandardNotFoundException e) {
            e.printStackTrace();
        } catch (InvalidOptimizationParameterException e) {
            e.printStackTrace();
        } catch (RunException e) {
            e.printStackTrace();
        } catch (UnknownDataStatisticException e) {
            e.printStackTrace();
        } catch (UnknownProgramTypeException e) {
            e.printStackTrace();
        } catch (UnknownRProgramException e) {
            e.printStackTrace();
        } catch (IncompatibleParameterOptimizationMethodException e) {
            e.printStackTrace();
        } catch (UnknownDistanceMeasureException e) {
            e.printStackTrace();
        } catch (UnknownRunStatisticException e) {
            e.printStackTrace();
        } catch (RepositoryConfigNotFoundException e) {
            e.printStackTrace();
        } catch (RepositoryConfigurationException e) {
            e.printStackTrace();
        } catch (ConfigurationException e) {
            e.printStackTrace();
        } catch (RegisterException e) {
            e.printStackTrace();
        } catch (UnknownDataSetTypeException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (NoDataSetException e) {
            e.printStackTrace();
        } catch (UnknownRunDataStatisticException e) {
            e.printStackTrace();
        } catch (RunResultParseException e) {
            e.printStackTrace();
        } catch (UnknownDataPreprocessorException e) {
            e.printStackTrace();
        } catch (IncompatibleDataSetConfigPreprocessorException e) {
            e.printStackTrace();
        } catch (UnknownContextException e) {
            e.printStackTrace();
        } catch (IncompatibleContextException e) {
            e.printStackTrace();
        } catch (UnknownParameterType e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * serverclient.IBackendServer#setLogLevel(ch.qos.logback.classic.Level)
     */
    @SuppressWarnings("unused")
    @Override
    public void setLogLevel(Level logLevel) throws RemoteException {
        ((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(logLevel);
    }

    /**
     * Change the log level of this JVM.
     * 
     * @param logLevel
     *            The new log level
     */
    public static void logLevel(Level logLevel) {
        ((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(logLevel);
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.IBackendServer#getDataSetGenerators()
     */
    @Override
    public Collection<String> getDataSetGenerators() {
        Collection<String> result = new HashSet<String>();

        Collection<Class<? extends DataSetGenerator>> dataSetGenerators = this.repository
                .getClasses(DataSetGenerator.class);

        for (Class<? extends DataSetGenerator> generatorClass : dataSetGenerators)
            result.add(generatorClass.getSimpleName());

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.IBackendServer#getDataSetRandomizers()
     */
    @Override
    public Collection<String> getDataRandomizers() {
        Collection<String> result = new HashSet<String>();

        Collection<Class<? extends DataRandomizer>> dataRandomizers = this.repository
                .getClasses(DataRandomizer.class);

        for (Class<? extends DataRandomizer> randomizerClass : dataRandomizers)
            result.add(randomizerClass.getSimpleName());

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * serverclient.IBackendServer#getOptionsForDataSetGenerator(java.lang.String
     * )
     */
    @Override
    public Options getOptionsForDataSetGenerator(String generatorName) {
        try {

            DataSetGenerator generator = DataSetGenerator.parseFromString(repository, generatorName);
            return generator.getAllOptions();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (UnknownDataSetGeneratorException e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.IBackendServer#generateDataSet(java.lang.String,
     * java.lang.String[])
     */
    @SuppressWarnings("unused")
    @Override
    public boolean generateDataSet(String generatorName, String[] args) throws RemoteException {
        try {
            DataSetGenerator generator = DataSetGenerator.parseFromString(this.repository, generatorName);
            generator.generate(args);
        } catch (UnknownDataSetGeneratorException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (DataSetGenerationException e) {
            e.printStackTrace();
        } catch (GoldStandardGenerationException e) {
            e.printStackTrace();
        }
        return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.IBackendServer#getQueue()
     */
    @SuppressWarnings("unused")
    @Override
    public Collection<String> getQueue() throws RemoteException {
        final Collection<String> result = this.repository.getSupervisorThread().getRunScheduler().getQueue();
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.clusteval.serverclient.IBackendServer#getActiveThreads()
     */
    @Override
    public Map<String, Triple<String, Integer, Long>> getActiveThreads() throws RemoteException {
        Map<String, Triple<String, Integer, Long>> result = new HashMap<String, Triple<String, Integer, Long>>();

        RunSchedulerThread scheduler = this.getRepository().getSupervisorThread().getRunScheduler();
        Map<Thread, IterationRunnable> map = scheduler.getActiveIterationRunnables();
        for (Map.Entry<Thread, IterationRunnable> e : map.entrySet()) {
            long startTime = e.getValue().getStartTime();
            ExecutionRunRunnable r = (ExecutionRunRunnable) (e.getValue().getParentRunnable());
            result.put(e.getKey().getName(),
                    Triple.getTriple(r.getRun().getName() + ": " + r.getProgramConfig() + "," + r.getDataConfig(),
                            e.getValue().getIterationNumber(), startTime));
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * serverclient.IBackendServer#getOptionsForDataRandomizer(java.lang.String
     * )
     */
    @Override
    public Options getOptionsForDataRandomizer(String randomizerName) {
        try {

            DataRandomizer randomizer = DataRandomizer.parseFromString(repository, randomizerName);
            return randomizer.getAllOptions();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (UnknownDataRandomizerException e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see serverclient.IBackendServer#randomizeDataConfig(java.lang.String,
     * java.lang.String[])
     */
    @SuppressWarnings("unused")
    @Override
    public boolean randomizeDataConfig(String randomizerName, String[] args) throws RemoteException {
        try {
            DataRandomizer randomizer = DataRandomizer.parseFromString(this.repository, randomizerName);
            randomizer.randomize(args);
        } catch (UnknownDataRandomizerException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return false;
    }
}