de.escidoc.bwelabs.depositor.service.SessionManager.java Source code

Java tutorial

Introduction

Here is the source code for de.escidoc.bwelabs.depositor.service.SessionManager.java

Source

/**
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at license/ESCIDOC.LICENSE
 * or https://www.escidoc.org/license/ESCIDOC.LICENSE .
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at license/ESCIDOC.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *
 * Copyright 2011 Fachinformationszentrum Karlsruhe Gesellschaft
 * fuer wissenschaftlich-technische Information mbH and Max-Planck-
 * Gesellschaft zur Foerderung der Wissenschaft e.V.
 * All rights reserved.  Use is subject to license terms.
 */
package de.escidoc.bwelabs.depositor.service;

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.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InvalidPropertiesFormatException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import javax.help.UnsupportedOperationException;

import org.escidoc.core.client.ingest.exceptions.ConfigurationException;
import org.escidoc.core.client.ingest.exceptions.IngestException;
import org.escidoc.core.client.ingest.filesystem.FileIngester;
import org.jfree.util.Log;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;

import de.escidoc.bwelabs.deposit.Configuration;
import de.escidoc.bwelabs.depositor.error.AlreadyExistException;
import de.escidoc.bwelabs.depositor.error.AlreadyExpiredException;
import de.escidoc.bwelabs.depositor.error.ApplicationException;
import de.escidoc.bwelabs.depositor.error.ConnectionException;
import de.escidoc.bwelabs.depositor.error.DepositorException;
import de.escidoc.bwelabs.depositor.error.InfrastructureException;
import de.escidoc.bwelabs.depositor.utility.Utility;
import de.escidoc.core.resources.common.properties.PublicStatus;

/**
 * Provides methods which execute requests to a deposit servlet, administers threads.
 * 
 * @author ROF
 * 
 */
public class SessionManager extends Thread {

    private static final Logger LOG = LoggerFactory.getLogger(SessionManager.class.getName());

    public static final String PROP_BASEDIR = "depositor.sessionBaseDir";

    public static final String PROP_MAX_THREAD_NUMBER = "depositor.maxThreadNumber";

    public static final String PROP_PING_INTERVAL = "depositor.pingIntervalSeconds";

    public static final String ERR_MAX_THREADS_ = "The depositor service is unavalible. The maximal number of threads is reached. Please try later.";

    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";

    private static final String PATH_FORMAT = "yyyy_MM_dd_HH_mm_ss_SSS";

    private File baseDir;

    private int maxThreadNumber;

    private int threadNumber;

    private int pingInterval;

    private Map<String, Properties> configurations;

    private Map<String, String> configurationDirPathes;

    private Map<String, Properties> failedConfigurations;

    private Map<String, Properties> expiredSuccessfulConfigurations;

    private Map<String, Vector<ItemSession>> sessions;

    private Map<String, String> failedExpiredConfDir;

    private Vector<String> expiredConfigurationsSinceLastRun;

    private Map<String, File> dirsFromLastRunToProcess;

    private Vector<String> isCleaning;

    private boolean isThreadNeedsToFinish;

    private boolean isThreadFinished;

    public SessionManager(Properties props) throws DepositorException {
        Preconditions.checkNotNull(props, "props is null: %s", props);

        threadNumber = 0;
        int threadNumber = loadConfigurationAndGetThreadNumber(props);

        init(new File(props.getProperty(PROP_BASEDIR)), threadNumber);
    }

    private int loadConfigurationAndGetThreadNumber(Properties props) throws DepositorException {
        if (props.getProperty(PROP_BASEDIR) == null) {
            String message = "Required property missing: " + PROP_BASEDIR;
            LOG.error(message);
            throw new DepositorException(message);
        }

        if (props.getProperty(PROP_MAX_THREAD_NUMBER) == null) {
            String message = "Required property missing: " + PROP_MAX_THREAD_NUMBER;
            LOG.error(message);
            throw new DepositorException(message);
        }
        int threadNumber;
        try {
            threadNumber = Integer.parseInt(props.getProperty(PROP_MAX_THREAD_NUMBER));
        } catch (Exception e) {
            String message = "Required property must an integer: " + PROP_MAX_THREAD_NUMBER;
            LOG.error(message);
            throw new DepositorException(message);
        }

        if (props.getProperty(PROP_PING_INTERVAL) == null) {
            String message = "Required property missing: " + PROP_PING_INTERVAL;
            LOG.error(message);
            throw new DepositorException(message);
        }

        try {
            this.pingInterval = Integer.parseInt(props.getProperty(PROP_PING_INTERVAL));
        } catch (Exception e) {
            String message = "Required property must an integer: " + PROP_PING_INTERVAL;
            LOG.error(message);
            throw new DepositorException(message);
        }
        return threadNumber;
    }

    private void init(File baseDir, int maxThreadNumber) throws DepositorException {
        this.baseDir = baseDir;
        dirsFromLastRunToProcess = new HashMap<String, File>();
        sessions = new HashMap<String, Vector<ItemSession>>();
        failedExpiredConfDir = new HashMap<String, String>();
        configurations = new HashMap<String, Properties>();
        failedConfigurations = new HashMap<String, Properties>();
        expiredSuccessfulConfigurations = new HashMap<String, Properties>();
        configurationDirPathes = new HashMap<String, String>();
        isCleaning = new Vector<String>();
        expiredConfigurationsSinceLastRun = new Vector<String>();

        if (!baseDir.exists()) {
            baseDir.mkdirs();
        }

        File[] dirs = baseDir.listFiles();
        if (dirs == null)
            throw new DepositorException(
                    "Unable to restore configuration directories within a base directory: " + baseDir.getPath());

        if (dirs.length > 0) {
            LOG.info("Restoring configurations from last run...");

            for (int i = 0; i < dirs.length; i++) {
                if (dirs[i].isDirectory()) {
                    String dirName = dirs[i].getName();
                    File configurationFile = new File(dirs[i], Constants.CONFIGURATION_FILE_NAME);
                    if (!configurationFile.exists()) {
                        String message = "Can not restore the configuration from the directory " + baseDir + "/"
                                + dirName
                                + " on start up of the Depositor: a configuration file does not exist in the directory.";
                        LOG.error(message);
                        continue;
                    }

                    FileInputStream fis = null;
                    Properties configProperties = null;
                    String configId = null;
                    try {
                        fis = new FileInputStream(configurationFile);
                        configProperties = new Properties();

                        try {
                            configProperties.loadFromXML(fis);
                            configId = configProperties.getProperty(Constants.PROPERTY_CONFIGURATION_ID);
                            if (dirName.startsWith("failed_expired_")) {
                                failedExpiredConfDir.put(configId, dirName);
                                continue;
                            }
                            if (isMonitoringTimeOver(configProperties)) {
                                expiredConfigurationsSinceLastRun.add(configId);
                            }
                            this.configurations.put(configId, configProperties);
                            this.configurationDirPathes.put(configId, dirName);
                        } catch (InvalidPropertiesFormatException e) {
                            String message = "Can not restore the configuration data from the directory " + baseDir
                                    + "/" + dirName + " on start up of the Depositor:";
                            LOG.error(message + e.getMessage());
                            continue;
                        } catch (IOException e) {
                            String message = "Can not restore the configuration from the directory " + baseDir + "/"
                                    + dirName + " on start up of the Depositor:";
                            LOG.error(message + e.getMessage());
                            continue;
                        }

                    } catch (FileNotFoundException e) {
                        String message = "Can not restore the configuration data from the directory " + baseDir
                                + "/" + dirName + " on start up of the Depositor:";
                        LOG.error(message + e.getMessage());
                        continue;
                    }
                    // save configuration directory from last run to
                    // process it later
                    dirsFromLastRunToProcess.put(configId, dirs[i]);
                } else {
                    dirs[i].delete();
                }
            }
        }
        threadNumber = 0;
        this.maxThreadNumber = maxThreadNumber;
        setName("Session-Reaper");
        start();
    }

    /**
     * Method processes not successful content files, remained from a last run of a Depositor service.
     * 
     * @param directoryToProcess
     * @param configId
     */
    void processContentFiles(final File directoryToProcess, final String configId) {
        File[] files = directoryToProcess.listFiles();
        for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
            if (!(files[fileIndex].getName().equals(Constants.CONFIGURATION_FILE_NAME)
                    || files[fileIndex].getName().startsWith("successful_"))) {
                try {
                    storeContentToInfrastructure(directoryToProcess, configId, files, fileIndex);
                } catch (DepositorException e) {
                    // FIXME give a message
                    LOG.error(e.getMessage(), e);
                    addToFailedConfigurations(configId);
                }
            }
        }
    }

    private void storeContentToInfrastructure(final File directoryToProcess, final String configId, File[] files,
            int j) throws DepositorException {
        new ReingestTask(this, configurations.get(configId), files[j], directoryToProcess, null).execute();
    }

    // ////////////////////////////////////////////////////////////////////////
    /**
     * Method increases counter holding the number of currently running threads.
     */
    protected synchronized void increaseThreadsNumber() {
        threadNumber++;
    }

    /**
     * Method decreases counter holding the number of currently running threads.
     */
    protected synchronized void decreaseThreadsNumber() {
        threadNumber--;
    }

    /**
     * Sessions administrator thread.
     */
    public void run() {
        while (!isThreadNeedsToFinish) {
            processFromLastRun();
            // iterate thru configurations, find expired configurations -->
            // clean sessions, find
            // failed configurations --> try to repair failed configurations
            if (configurations.size() > 0) {
                synchronized (configurations) {
                    // FIXME containerId is _always_ empty
                    Vector<String> containerIds = new Vector<String>();

                    for (String configId : configurations.keySet()) {
                        Properties configuration = configurations.get(configId);
                        if (isReingestNotNeeded(configId, configuration)) {
                            cleanUpExpiredConfiguration(configId);
                        } else {
                            sendPingToCore(containerIds, configuration);
                            reingestFailedIngest(configId, configuration);
                        }
                    }

                }
            }
            int waitedSeconds = 0;
            while (!isThreadNeedsToFinish && (waitedSeconds < pingInterval / 2)) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    LOG.error("Something wrong: " + e.getMessage());
                }
                waitedSeconds++;
            }
        }

        isThreadFinished = true;
    }

    private boolean isReingestNotNeeded(String configId, Properties configuration) {
        return expiredConfigurationsSinceLastRun.contains(configId) || isMonitoringTimeOver(configuration);
    }

    // if the configuration was already expired to restart
    // time of Depositor or if a monitoring time for the
    // configuration is over, clean up sessions for the
    // configuration
    private void cleanUpExpiredConfiguration(String configId) {
        cleanupSessions(configId);
        expiredConfigurationsSinceLastRun.remove(configId);
    }

    private void processFromLastRun() {
        // processing content files from last run of a Deposit service,
        // happens only once after restart of the Deposit service
        for (String configId : dirsFromLastRunToProcess.keySet()) {
            File dirToProcess = dirsFromLastRunToProcess.get(configId);
            processContentFiles(dirToProcess, configId);
        }

        dirsFromLastRunToProcess = new HashMap<String, File>();
    }

    private void reingestFailedIngest(String configId, Properties configuration) {
        // try to store failed content files of failed configurations into infrastructure
        if (isFailed(configId)) {
            boolean stillFailed = false;

            // this list contains #n item sessions. Item session is a File Ingest Thread.
            Vector<ItemSession> sessionsForConfiguration = sessions.get(configId);

            Vector<ItemSession> oldSessionsForConfiguration = new Vector<ItemSession>();

            List<ReingestTask> list = new ArrayList<ReingestTask>();

            synchronized (sessionsForConfiguration) {
                for (ItemSession itemSession : sessionsForConfiguration) {
                    if (isFinishedAndStillFailed(itemSession)) {
                        stillFailed = true;
                        // Gather failed files
                        // insert in a list
                        // ingest them sync
                        // list.add(new ReingestTask());
                        addTask(list, configuration, oldSessionsForConfiguration, itemSession);
                    }
                }
                sessionsForConfiguration.removeAll(oldSessionsForConfiguration);
            }

            for (ReingestTask reingestTask : list) {
                try {
                    reingestTask.execute();
                } catch (DepositorException e) {
                    Log.error("Can not reingest...", e);
                }
            }

            // if all currently finished sessions of the configuration was repaired in a meantime,
            // the configuration is not 'failed' any more
            // and deposit service can accept new content files for the configuration
            if (!stillFailed) {
                synchronized (failedConfigurations) {
                    failedConfigurations.remove(configId);
                }
            }
        }
    }

    private void addTask(List<ReingestTask> list, Properties configuration,
            Vector<ItemSession> oldSessionsForConfiguration, ItemSession is) {
        list.add(new ReingestTask(this, configuration, is.getContentFile(), is.getConfigurationDirectory(),
                is.getProvidedCheckSum()));
        oldSessionsForConfiguration.add(is);
    }

    private boolean isFinishedAndStillFailed(ItemSession is) {
        return is.isFinished() && is.isSessionFailed();
    }

    private boolean isFailed(String configId) {
        return failedConfigurations.containsKey(configId);
    }

    // FIXME this method does nothing
    private void sendPingToCore(Vector<String> containerIds, Properties configuration) {
        // monitoring time is not over, ping a container for
        // the configuration
        String containerId = configuration.getProperty(Constants.PROPERTY_EXPERIMENT_ID);
        if (!containerIds.contains(containerId)) {
            containerIds.add(containerId);
        }
    }

    /**
     * Method checks if the monitoring time of a provided configuration is over.
     * 
     * @param configuration
     * @return
     */
    private static boolean isMonitoringTimeOver(Properties configuration) {
        String monitoringStartTime = configuration.getProperty(Constants.PROPERTY_MONITORING_START_TIME);
        if (monitoringStartTime == null) {
            return false;
        }
        DateTimeZone.setDefault(DateTimeZone.UTC);

        DateTime startTime = new DateTime(monitoringStartTime);
        String monitoringDuration = configuration.getProperty(Constants.PROPERTY_TIME_MONITORING_DURATION);

        int monitoringDurationMinutes = Integer.parseInt(monitoringDuration);
        DateTime endTime = startTime.plusMinutes(monitoringDurationMinutes);

        return endTime.isBeforeNow();
    }

    // ////////////////////////////////////////////////////////////////////////

    /**
     * Method adds a session to the map of tracked sessions of a configuration with a provided id.
     */
    protected void addSession(ItemSession session, String configurationId) {
        synchronized (sessions) {
            Vector<ItemSession> configurationSessions = sessions.get(configurationId);
            if (configurationSessions == null) {
                configurationSessions = new Vector<ItemSession>();

            }
            configurationSessions.add(session);
            sessions.put(configurationId, configurationSessions);
        }
    }

    /**
     * Method adds a configuration to a map with failed configurations.
     * 
     * @param configurationId
     */
    public void addToFailedConfigurations(final String configurationId) {
        synchronized (failedConfigurations) {
            if (!isFailed(configurationId)) {
                failedConfigurations.put(configurationId, configurations.get(configurationId));
            }
        }
    }

    // ////////////////////////////////////////////////////////////////////////
    /**
     * Method checks if a limit of threads on Depositor is exceeded and calls a method to check a provided stream with a
     * configuration.
     * 
     * 
     * Method reads a configuration from a provided input stream. It checks if all mandatory configuration properties
     * are present and have valid values. It creates a directory for the configuration inside a base directory, save a
     * configuration file Constants.CONFIGURATION_FILE_NAME into this configuration directory and store a configuration
     * as an item in to an infrastructure with the infrastructure end point contained in the configuration.
     * 
     * 
     */
    public void storeConfiguration(Configuration configProperties)
            throws ApplicationException, DepositorException, ConnectionException, InfrastructureException {
        throw new UnsupportedOperationException("everything moved");
    }

    public void ingestConfiguration(Configuration configProperties, File configFile)
            throws ConfigurationException, IngestException {
        FileIngester ingester = buildFileIngester(configProperties, configFile,
                configurationDirPathes.get(configProperties.getProperty(Configuration.PROPERTY_CONFIGURATION_ID)));

        LOG.debug("ingesting configuration");
        ingester.setForceCreate(true);
        ingester.ingest();
    }

    private FileIngester buildFileIngester(Configuration configProperties, File configFile, String configDirName) {
        LOG.debug("prepare ingesting configuration");
        FileIngester ingester = new FileIngester(
                configProperties.getProperty(Configuration.PROPERTY_INFRASTRUCTURE_ENDPOINT),
                configProperties.getProperty(Configuration.PROPERTY_USER_HANDLE),
                configProperties.getProperty(Configuration.PROPERTY_EXPERIMENT_ID));

        LOG.debug("should be the same:[");
        LOG.debug(baseDir + "/" + configDirName + "/" + Constants.CONFIGURATION_FILE_NAME);
        LOG.debug(configFile.getPath());
        LOG.debug("]");

        ingester.addFile(baseDir + "/" + configDirName + "/" + Constants.CONFIGURATION_FILE_NAME);
        ingester.setItemContentModel(configProperties.getProperty(Configuration.PROPERTY_CONTENT_MODEL_ID));
        // FIXME
        ingester.setContainerContentModel(configProperties.getProperty(Configuration.PROPERTY_CONTENT_MODEL_ID));
        ingester.setContext(configProperties.getProperty(Configuration.PROPERTY_CONTEXT_ID));
        ingester.setContentCategory("ORIGINAL");
        ingester.setInitialLifecycleStatus(PublicStatus.PENDING);
        ingester.setMimeType("text/xml");
        ingester.setValidStatus("valid");
        ingester.setVisibility("visible");
        return ingester;
    }

    public void registerConfiguration(Configuration configProperties) {
        synchronized (configurations) {
            configurations.put(configProperties.getProperty(Configuration.PROPERTY_CONFIGURATION_ID),
                    configProperties);
        }
    }

    public void checkIfAlreadyExists(String configurationId) throws AlreadyExistException {
        if (configurations.containsKey(configurationId)
                || expiredSuccessfulConfigurations.containsKey(configurationId)
                || failedExpiredConfDir.containsKey(configurationId)) {
            String message = "Configuration " + configurationId + " already exists.";
            LOG.error(message);
            throw new AlreadyExistException(message);
        }
    }

    /**
     * Removes a configuration file and configuration directory from a file system.
     * 
     * @param configFile
     */
    private void deleteConfigFile(File configFile) {
        String confDirectoryName = configFile.getParent();
        configFile.delete();
        File parentDirectory = new File(confDirectoryName);
        if (parentDirectory.exists()) {
            parentDirectory.delete();
        }
    }

    /**
     * Method creates a new configuration directory for a provided configuration with a currently time stamp as a name
     * and save a configuration file within it.
     * 
     * @param configuration
     * @param configurationId
     * @return File with a created configuration directory.
     * @throws DepositorException
     */
    public File saveInLocalFileSystem(Properties configuration) throws DepositorException {

        LOG.debug("saving configuration in local file system");
        DateTimeZone.setDefault(DateTimeZone.UTC);
        DateTime currentTime = new DateTime();
        DateTimeFormatter fmt = DateTimeFormat.forPattern(PATH_FORMAT);
        String configurationDirectoryName = currentTime.toString(fmt);
        File configurationDirectory = new File(baseDir, configurationDirectoryName);
        configurationDirectory.mkdirs();
        File configurationFile = new File(configurationDirectory, Constants.CONFIGURATION_FILE_NAME);
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(configurationFile);
        } catch (FileNotFoundException e) {
            LOG.error(e.getMessage());
            throw new DepositorException(e.getMessage());
        }
        try {
            configuration.storeToXML(os, null);
            os.flush();
            os.close();
            synchronized (configurationDirPathes) {
                configurationDirPathes.put(configuration.getProperty(Configuration.PROPERTY_CONFIGURATION_ID),
                        configurationDirectoryName);
            }

        } catch (IOException e) {
            LOG.error(e.getMessage());
            throw new DepositorException(e.getMessage());
        }
        return configurationFile;
    }

    /**
     * Method checks if a configuration-session is being cleaned at the moment by another thread. In this case it waits
     * before the other thread finished, reads the result of the clean operation and throws an exception if the clean
     * operation could not be successful performed. If a configuration-session is not being cleaned by another thread,
     * method calls a clean method cleanupSessions() and then removes a configuration from a map with registered
     * configurations.
     * 
     * @param configId
     * @throws ApplicationException
     * @throws DepositorException
     */
    public void deleteConfiguration(final String configId) throws ApplicationException, DepositorException {

        if (failedExpiredConfDir.containsKey(configId)) {
            String message = "Can not delete the configuration: depositor could not store some content "
                    + "files for this configuration into the infrastracture.";
            LOG.error(message);
            throw new DepositorException(message);
        }

        if (!isCleaning.contains(configId)) {
            synchronized (configurations) {
                if (!configurations.containsKey(configId)) {
                    String message = "Depositor can not find a configuration with the id " + configId + ".";
                    LOG.error(message);
                    throw new ApplicationException(message);
                }
                // the configuration is not deleting at the moment by any
                // different thread and the configuration is registered,
                // cleanup session for the configuration
                cleanupSessions(configId);
                configurations.remove(configId);
            }
        } else {
            // wait until the thread cleaning this configuration is
            // finished
            while (isCleaning.contains(configId)) {
                try {
                    Thread.sleep(250);
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * Method calls a method putMonitoringStartTimeIntoConfigurationIfMissing() to check if a configuration with a
     * provided id contains a property Monitoring Start Time and put it into a configuration if it is missing. Then it
     * reads a provided input stream, checks if a provided check sum is valid and save a content into a configuration
     * directory for a configuration with a provided id. If a check sum is valid, it starts a new thread to store a
     * content as an item into a container in the infrastructure for the configuration.
     * 
     * @param configId
     * @param checkSumValue
     * @param is
     * @param fileName
     * @return true - if a check sum is valid, false - otherwise
     * @throws ApplicationException
     * @throws DepositorException
     */
    public boolean refactorNameOfThisMethod(final String configId, final String checkSumValue, final InputStream is,
            final String fileName) throws ApplicationException, DepositorException {

        checkPreconditions(configId);
        File configurationDirectory = new File(baseDir, configurationDirPathes.get(configId));
        checkIfExists(configId, configurationDirectory);
        checkFileName(configId, fileName, configurationDirectory);
        putMonitoringStartTimeIntoConfigurationIfMissing(configId);

        File content = new File(configurationDirectory, fileName);
        MessageDigest digest = storeFileAndCalculateChecksum(configId, is, content);
        if (isCheckSumEquals(digest, checkSumValue)) {
            ingestFileAsync(configId, checkSumValue, configurationDirectory, content);
            return true;
        }

        content.delete();
        return false;
    }

    private void ingestFileAsync(final String configId, final String checkSumValue, File configurationDirectory,
            File content) throws DepositorException {
        // now, content from the request is stored and validated.
        // create a session and start it. The session computed all additional
        // information and stores the content as component content in an item in
        // the eSciDoc Infrastructure.
        new ItemSession(this, configurations.get(configId), content, configurationDirectory, checkSumValue).start();
    }

    private boolean isCheckSumEquals(MessageDigest md, String checkSumValue) {

        // compare computed digest with the one send with the request
        byte[] digest = md.digest();
        String checksum = Utility.byteArraytoHexString(digest);
        LOG.debug("Checksums: send[" + checkSumValue + "] file[" + checksum + "]");

        return checksum.equals(checkSumValue);
    }

    private static void checkIfExists(final String configId, File configurationDirectory)
            throws DepositorException {
        if (!configurationDirectory.exists()) {
            String message = "Error on Depositor: can not found a directory for the configuration with the id "
                    + configId + ".";
            LOG.error(message);
            throw new DepositorException(message);
        }
    }

    private MessageDigest storeFileAndCalculateChecksum(final String configId, final InputStream is,
            File contentFile) throws DepositorException {
        MessageDigest md = getMessageDigest(configId);
        DigestInputStream dis = null;
        try {
            FileOutputStream fos = new FileOutputStream(contentFile);
            dis = new DigestInputStream(is, md);
            byte[] buf = new byte[5000];
            int readByte;
            while ((readByte = dis.read(buf)) > 0) {
                fos.write(buf, 0, readByte);
            }
        } catch (FileNotFoundException e) {
            LOG.error(e.getMessage());
            throw new DepositorException(e.getMessage());
        } catch (IOException e) {
            LOG.error(e.getMessage());
            throw new DepositorException(e.getMessage());
        } finally {
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException e) {
                    LOG.error(e.getMessage());
                }
            }
        }
        return md;
    }

    private MessageDigest getMessageDigest(final String configId) throws DepositorException {
        MessageDigest md = null;
        try {
            md = MessageDigest
                    .getInstance(configurations.get(configId).getProperty(Constants.PROPERTY_CHECKSUM_ALGORITHM));
        } catch (NoSuchAlgorithmException e) {
            LOG.error(e.getMessage());
            throw new DepositorException(e.getMessage());
        }
        return md;
    }

    // check if filename is already sent for this configuration
    private static void checkFileName(final String configId, final String fileName, File configurationDirectory)
            throws AlreadyExistException {
        File[] files = configurationDirectory.listFiles();
        for (int i = 0; i < files.length; i++) {
            String name = files[i].getName();
            if (name.endsWith(fileName)) {
                if (name.equals("successful_" + fileName) || name.equals("failed_" + fileName)
                        || name.equals("successful_failed_" + fileName) || name.equals(fileName)) {
                    String message = "A content file '" + fileName + "' for the configuration with id " + configId
                            + " already exists on Depositor.";
                    LOG.error(message);
                    throw new AlreadyExistException(message);
                }
            }
        }
    }

    private void checkPreconditions(final String configId)
            throws DepositorException, AlreadyExpiredException, ApplicationException {
        if (threadNumber == maxThreadNumber) {
            LOG.error(ERR_MAX_THREADS_);
            throw new DepositorException(ERR_MAX_THREADS_);
        }

        if (expiredSuccessfulConfigurations.containsKey(configId)
                || expiredConfigurationsSinceLastRun.contains(configId) || isCleaning.contains(configId)) {
            String message = "A session for the configuration with " + configId + " is expired.";
            LOG.error(message);
            throw new AlreadyExpiredException(message);
        }

        if (failedExpiredConfDir.containsKey(configId)) {
            String message = "A configuration with id  " + configId
                    + " is expiered and failed due to an internal failure on a deposit service or on an infrastructure.";
            LOG.error(message);
            throw new DepositorException(message);
        }

        if (isFailed(configId)) {
            String message = "Error on Depositor: can not temporary accept content files for the configuration with the id "
                    + configId + " due to an internal failure on a deposit service or on an infrastructure.";
            LOG.error(message);
            throw new DepositorException(message);
        }

        if (!configurations.containsKey(configId)) {
            String message = "Can not find a configuration with the id " + configId + ".";
            LOG.error(message);
            throw new ApplicationException(message);
        }
    }

    /**
     * Method checks if a configuration with a provided id contains a property Monitoring Start Time. It put a time
     * stamp with a current time into a configuration contained in a map with configurations and in a configuration
     * file.
     * 
     * @deprecated Monitoring start time should not be needed in configuration.
     * @param configId
     */
    @Deprecated
    private void putMonitoringStartTimeIntoConfigurationIfMissing(String configId) {
        File configurationDirectory = new File(baseDir, configurationDirPathes.get(configId));
        Properties configuration = null;
        // if a configuration does not contain a calculated monitoring start
        // time,
        // put a calculated monitoring start time in to the configuration and
        // store the
        // configuration into a configuration file
        synchronized (configurations) {
            configuration = configurations.get(configId);
            String monitoringStartTime = configuration.getProperty(Constants.PROPERTY_MONITORING_START_TIME);
            if (monitoringStartTime == null) {
                DateTimeZone.setDefault(DateTimeZone.UTC);
                DateTime currentTime = new DateTime();
                DateTimeFormatter fmt = DateTimeFormat.forPattern(DATE_TIME_FORMAT);
                String timeStamp = currentTime.toString(fmt);
                configuration.put(Constants.PROPERTY_MONITORING_START_TIME, timeStamp);

                File configurationFile = new File(configurationDirectory, Constants.CONFIGURATION_FILE_NAME);
                configurationFile.delete();
                FileOutputStream os = null;
                try {
                    os = new FileOutputStream(configurationFile);
                } catch (FileNotFoundException e) {
                    LOG.error(e.getMessage());

                }
                try {
                    configuration.storeToXML(os, null);
                    if (os != null) {
                        os.flush();
                        os.close();
                    }
                } catch (IOException e) {
                    LOG.error(e.getMessage());

                }
            }
        }
    }

    // ///////////////////////////////////////////////////////////////////////

    public void close() {
        isThreadNeedsToFinish = true;
        while (!isThreadFinished) {
            try {
                Thread.sleep(250);
            } catch (Exception e) {
            }
        }
    }

    public void finalize() {
        close();
    }

    /**
     * Method puts a provided configuration id into a set with configurations, which are being cleaned at the moment, to
     * prevent another threads from the cleaning of the configuration. It checks if all of the configuration sessions
     * finished successful or a configuration has no sessions and is not contained in a map with a failed
     * configurations. In this case it removes a configuration directory and a configuration file from a file system and
     * put the configuration id into a map with successful finished configurations. Otherwise it calls a method
     * renameConfigDirectoryToFailedExpired() to mark a configuration directory as expired and failed. A fact, that a
     * configuration has no sessions and is contained in a map with failed configurations means that content files of
     * the configuration could not be restored from a file system on restart of Demon service. Finally method removes
     * the configuration id from a set with configurations, which are being cleaned at the moment.
     * 
     * @param configurationId
     */
    public void cleanupSessions(final String configurationId) {
        // add the configuration to a set of configurations, which are being
        // cleaned at the moment
        synchronized (isCleaning) {
            isCleaning.add(configurationId);
        }
        Vector<ItemSession> sessionsForConfiguration = null;
        Properties configuration = configurations.get(configurationId);
        synchronized (sessions) {
            sessionsForConfiguration = sessions.get(configurationId);
            sessions.remove(configurationId);
        }
        if (sessionsForConfiguration != null) {
            boolean configurationFailed = false;
            Iterator<ItemSession> iter = sessionsForConfiguration.iterator();
            File configurationDirectory = null;
            // check if some sessions of the configuration failed
            while (iter.hasNext()) {
                ItemSession session = iter.next();
                configurationDirectory = session.getConfigurationDirectory();
                while (!session.isFinished()) {
                    try {
                        Thread.sleep(250);
                    } catch (Exception e) {
                    }
                }
                if (!session.isSessionFailed()) {
                    session.deleteContentFile();
                    iter.remove();
                } else {
                    configurationFailed = true;
                }

            }
            if (configurationFailed) {
                // some sessions of the configuration failed (some content files
                // could not be stored into an infrastructure)
                renameConfigDirectoryToFailedExpired(configurationDirectory, configurationId);
            } else {
                // all sessions of the configuration finished successful (all
                // content files were stored into an infrastructure)
                removeSuccessfulConfiguration(configurationDirectory, configurationId, configuration);
            }
        } else {
            // There are no sessions for the configuration
            String confDirectoryName = configurationDirPathes.get(configurationId);
            File configurationDirectory = new File(baseDir, confDirectoryName);
            if (configurationDirectory.exists()) {
                if (isFailed(configurationId)) {
                    // content files for the configuration could not be restored
                    // from a file system after restart of a Depositor
                    renameConfigDirectoryToFailedExpired(configurationDirectory, configurationId);
                } else {
                    removeSuccessfulConfiguration(configurationDirectory, configurationId, configuration);
                }
            }
        }
        synchronized (failedConfigurations) {
            failedConfigurations.remove(configurationId);
        }
        synchronized (isCleaning) {
            // remove the configuration from a set of configurations, which
            // are being cleaned at the moment
            isCleaning.remove(configurationId);
        }
    }

    /**
     * Method removes a provided configuration file and a provided configuration directory from a file system. It puts a
     * provided configuration id and a provided configuration into a map with successful finished configurations.
     * 
     * @param configurationFile
     * @param configurationDirectory
     * @param configurationId
     * @param configuration
     */
    private void removeSuccessfulConfiguration(final File configurationDirectory, final String configurationId,
            final Properties configuration) {
        File[] files = configurationDirectory.listFiles();
        for (int i = 0; i < files.length; i++) {
            files[i].delete();
        }
        configurationDirectory.delete();
        synchronized (expiredSuccessfulConfigurations) {
            expiredSuccessfulConfigurations.put(configurationId, configuration);
        }
    }

    /**
     * Method adds a a prefix 'failed_expired_' to a configuration directory name and put the configuration id into a
     * map, containing ids of failed expired configurations and configuration directory names.
     * 
     * @param configurationDirectory
     * @param configurationId
     */
    private void renameConfigDirectoryToFailedExpired(final File configurationDirectory,
            final String configurationId) {
        File[] files = configurationDirectory.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].getName().startsWith("successful_")) {
                files[i].delete();
            }
        }
        String configDirName = configurationDirectory.getName();
        synchronized (failedExpiredConfDir) {
            boolean success = configurationDirectory.renameTo(new File(baseDir, "failed_expired_" + configDirName));
            if (!success) {
                failedExpiredConfDir.put(configurationId, configDirName);
                LOG.error("Error while cleaning up sessions for the configuration with id " + configurationId
                        + " : can not rename a configuration directory to 'failed_expired_" + configDirName + "'.");
            } else {
                failedExpiredConfDir.put(configurationId, "failed_expired_" + configDirName);
            }
        }
    }

}