org.kitodo.services.ProcessService.java Source code

Java tutorial

Introduction

Here is the source code for org.kitodo.services.ProcessService.java

Source

/*
 * (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
 *
 * This file is part of the Kitodo project.
 *
 * It is licensed under GNU General Public License version 3 or later.
 *
 * For the full copyright and license information, please read the
 * GPL3-License.txt file that was distributed with this source code.
 */

package org.kitodo.services;

import com.sun.research.ws.wadl.HTTPMethods;
import de.sub.goobi.config.ConfigMain;
import de.sub.goobi.helper.FilesystemHelper;
import de.sub.goobi.helper.Helper;
import de.sub.goobi.helper.tasks.ProcessSwapInTask;
import de.sub.goobi.metadaten.MetadatenHelper;
import de.sub.goobi.metadaten.MetadatenSperrung;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.faces.context.FacesContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.goobi.io.BackupFileRotation;
import org.goobi.io.SafeFile;
import org.goobi.production.cli.helper.WikiFieldHelper;
import org.goobi.production.export.ExportDocket;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;

import org.kitodo.data.database.beans.Batch;
import org.kitodo.data.database.beans.Batch.Type;
import org.kitodo.data.database.beans.History;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.beans.ProcessProperty;
import org.kitodo.data.database.beans.Task;
import org.kitodo.data.database.beans.User;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.database.exceptions.SwapException;
import org.kitodo.data.database.helper.enums.MetadataFormat;
import org.kitodo.data.database.helper.enums.TaskStatus;
import org.kitodo.data.database.persistence.ProcessDAO;

import org.kitodo.data.index.Indexer;
import org.kitodo.data.index.elasticsearch.type.ProcessType;
import ugh.dl.DigitalDocument;
import ugh.dl.Fileformat;
import ugh.exceptions.PreferencesException;
import ugh.exceptions.ReadException;
import ugh.exceptions.WriteException;
import ugh.fileformats.excel.RDFFile;
import ugh.fileformats.mets.MetsMods;
import ugh.fileformats.mets.MetsModsImportExport;
import ugh.fileformats.mets.XStream;

public class ProcessService {

    private static final Logger myLogger = Logger.getLogger(ProcessService.class);

    private Boolean selected = false;

    private final MetadatenSperrung msp = new MetadatenSperrung();

    Helper help = new Helper();

    public static String DIRECTORY_PREFIX = "orig";
    public static String DIRECTORY_SUFFIX = "images";

    private static final String TEMPORARY_FILENAME_PREFIX = "temporary_";

    private ProcessDAO processDao = new ProcessDAO();
    private ProcessType processType = new ProcessType();
    private Indexer<Process, ProcessType> indexer = new Indexer<>("kitodo", Process.class);
    private UserService userService = new UserService();

    public Process find(Integer id) throws DAOException {
        return processDao.find(id);
    }

    public List<Process> findAll() throws DAOException {
        return processDao.findAll();
    }

    /**
     * Method saves object to database and insert document to the index of Elastic Search.
     *
     * @param process object
     */
    public void save(Process process) throws DAOException, IOException {
        processDao.save(process, getProgress(process));
        indexer.setMethod(HTTPMethods.PUT);
        indexer.performSingleRequest(process, processType);
    }

    public void saveList(List<Process> list) throws DAOException {
        processDao.saveList(list);
    }

    /**
     * Method removes object from database and document from the index of Elastic Search.
     *
     * @param process object
     */
    public void remove(Process process) throws DAOException, IOException {
        processDao.remove(process);
        indexer.setMethod(HTTPMethods.DELETE);
        indexer.performSingleRequest(process, processType);
    }

    /**
     * Method removes object from database and document from the index of Elastic Search.
     *
     * @param id of object
     */
    public void remove(Integer id) throws DAOException, IOException {
        processDao.remove(id);
        indexer.setMethod(HTTPMethods.DELETE);
        indexer.performSingleRequest(id);
    }

    public List<Process> search(String query) throws DAOException {
        return processDao.search(query);
    }

    public Long count(String query) throws DAOException {
        return processDao.count(query);
    }

    public void refresh(Process process) {
        processDao.refresh(process);
    }

    /**
     * Method adds all object found in database to Elastic Search index.
     */
    public void addAllObjectsToIndex() throws DAOException, InterruptedException, IOException {
        indexer.setMethod(HTTPMethods.PUT);
        indexer.performMultipleRequests(findAll(), processType);
    }

    /**
     * Returns the batches of the desired type for a process.
     *
     * @param type of batches to return
     * @return all batches of the desired type
     */
    public List<Batch> getBatchesByType(Process process, Type type) {
        List<Batch> batches = getBatchesInitialized(process);
        if (type != null) {
            List<Batch> result = new ArrayList<>(batches);
            Iterator<Batch> indicator = result.iterator();
            while (indicator.hasNext()) {
                if (!type.equals(indicator.next().getType())) {
                    indicator.remove();
                }
            }
            return result;
        }
        return batches;
    }

    /**
     * The function getBatchesInitialized() returns the batches for a process and takes care that the object is
     * initialized from Hibernate already and will not be bothered if the Hibernate session ends.
     * TODO: check if it is necessary!!
     *
     * @return the batches field of the process which is loaded
     */
    public List<Batch> getBatchesInitialized(Process process) {
        if (process.getId() != null) {
            Hibernate.initialize(process.getBatches());
        }
        return process.getBatches();
    }

    /**
     * The function getHistoryInitialized() returns the history events for a process and takes care that the object
     * is initialized from Hibernate already and will not be bothered if the Hibernate session ends.
     * TODO: check if it is necessary!!
     *
     * @return the history field of the process which is loaded
     */
    public List<History> getHistoryInitialized(Process process) {
        try {
            @SuppressWarnings("unused")
            Session s = Helper.getHibernateSession();
            Hibernate.initialize(process.getHistory());
        } catch (HibernateException e) {
            myLogger.debug("Hibernate exception: ", e);
        }
        if (process.getHistory() == null) {
            process.setHistory(new ArrayList<History>());
        }
        return process.getHistory();
    }

    /**
     * The function getPropertiesInitialized() returns the descriptive fields (properties?) for a process and takes
     * care that the object is initialized from Hibernate already and will not be bothered if the
     * Hibernate session ends.
     * TODO: check if it is necessary!! <- e.g. BeanHelper uses it
     *
     * @return the properties field of the process which is loaded
     */
    public List<ProcessProperty> getPropertiesInitialized(Process process) {
        try {
            Hibernate.initialize(process.getProperties());
        } catch (HibernateException e) {
            myLogger.debug("Hibernate exception: ", e);
        }
        return process.getProperties();
    }

    /**
     * Get blocked user.
     *
     * @return blocked metadata (user)
     */
    public User getBlockedUsers(Process process) {
        User result = null;
        if (MetadatenSperrung.isLocked(process.getId())) {
            String userID = this.msp.getLockBenutzer(process.getId());
            try {
                result = userService.find(Integer.valueOf(userID));
            } catch (Exception e) {
                Helper.setFehlerMeldung(Helper.getTranslation("userNotFound"), e);
            }
        }
        return result;
    }

    public long getBlockedMinutes(Process process) {
        return this.msp.getLockSekunden(process.getId()) / 60;
    }

    public long getBlockedSeconds(Process process) {
        return this.msp.getLockSekunden(process.getId()) % 60;
    }

    /**
     * Get directory for tig images.
     *
     * @param useFallBack add description
     * @param process object
     * @return tif directory
     */
    public String getImagesTifDirectory(boolean useFallBack, Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        SafeFile dir = new SafeFile(getImagesDirectory(process));
        DIRECTORY_SUFFIX = ConfigMain.getParameter("DIRECTORY_SUFFIX", "tif");
        DIRECTORY_PREFIX = ConfigMain.getParameter("DIRECTORY_PREFIX", "orig");
        /* nur die _tif-Ordner anzeigen, die nicht mir orig_ anfangen */
        FilenameFilter filterVerz = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return (name.endsWith("_" + DIRECTORY_SUFFIX) && !name.startsWith(DIRECTORY_PREFIX + "_"));
            }
        };

        String tifOrdner = "";
        String[] verzeichnisse = dir.list(filterVerz);

        if (verzeichnisse != null) {
            for (int i = 0; i < verzeichnisse.length; i++) {
                tifOrdner = verzeichnisse[i];
            }
        }

        if (tifOrdner.equals("") && useFallBack) {
            String suffix = ConfigMain.getParameter("MetsEditorDefaultSuffix", "");
            if (!suffix.equals("")) {
                String[] folderList = dir.list();
                for (String folder : folderList) {
                    if (folder.endsWith(suffix)) {
                        tifOrdner = folder;
                        break;
                    }
                }
            }
        }

        if (!tifOrdner.equals("") && useFallBack) {
            String suffix = ConfigMain.getParameter("MetsEditorDefaultSuffix", "");
            if (!suffix.equals("")) {
                SafeFile tif = new SafeFile(tifOrdner);
                String[] files = tif.list();
                if (files == null || files.length == 0) {
                    String[] folderList = dir.list();
                    for (String folder : folderList) {
                        if (folder.endsWith(suffix) && !folder.startsWith(DIRECTORY_PREFIX)) {
                            tifOrdner = folder;
                            break;
                        }
                    }
                }
            }
        }

        if (tifOrdner.equals("")) {
            tifOrdner = process.getTitle() + "_" + DIRECTORY_SUFFIX;
        }

        String result = getImagesDirectory(process) + tifOrdner;

        if (!result.endsWith(File.separator)) {
            result += File.separator;
        }
        if (!ConfigMain.getBooleanParameter("useOrigFolder", true)
                && ConfigMain.getBooleanParameter("createOrigFolderIfNotExists", false)) {
            FilesystemHelper.createDirectory(result);
        }
        return result;
    }

    /**
     * Check if Tif directory exists.
     * @return true if the Tif-Image-Directory exists, false if not
     */
    public Boolean checkIfTifDirectoryExists(Process process) {
        SafeFile testMe;
        try {
            testMe = new SafeFile(getImagesTifDirectory(true, process));
        } catch (DAOException | IOException | InterruptedException | SwapException e) {
            return false;
        }
        if (testMe.list() == null) {
            return false;
        }

        return testMe.exists() && testMe.list().length > 0;
    }

    /**
     * Get images origin directory.
     *
     * @param useFallBack
     * @param process
     * @return path
     */
    public String getImagesOrigDirectory(boolean useFallBack, Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        if (ConfigMain.getBooleanParameter("useOrigFolder", true)) {
            SafeFile dir = new SafeFile(getImagesDirectory(process));
            DIRECTORY_SUFFIX = ConfigMain.getParameter("DIRECTORY_SUFFIX", "tif");
            DIRECTORY_PREFIX = ConfigMain.getParameter("DIRECTORY_PREFIX", "orig");
            /* nur die _tif-Ordner anzeigen, die mit orig_ anfangen */
            FilenameFilter filterVerz = new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    return (name.endsWith("_" + DIRECTORY_SUFFIX) && name.startsWith(DIRECTORY_PREFIX + "_"));
                }
            };

            String origOrdner = "";
            String[] verzeichnisse = dir.list(filterVerz);
            for (int i = 0; i < verzeichnisse.length; i++) {
                origOrdner = verzeichnisse[i];
            }

            if (origOrdner.equals("") && useFallBack) {
                String suffix = ConfigMain.getParameter("MetsEditorDefaultSuffix", "");
                if (!suffix.equals("")) {
                    String[] folderList = dir.list();
                    for (String folder : folderList) {
                        if (folder.endsWith(suffix)) {
                            origOrdner = folder;
                            break;
                        }
                    }
                }
            }

            if (!origOrdner.equals("") && useFallBack) {
                String suffix = ConfigMain.getParameter("MetsEditorDefaultSuffix", "");
                if (!suffix.equals("")) {
                    SafeFile tif = new SafeFile(origOrdner);
                    String[] files = tif.list();
                    if (files == null || files.length == 0) {
                        String[] folderList = dir.list();
                        for (String folder : folderList) {
                            if (folder.endsWith(suffix)) {
                                origOrdner = folder;
                                break;
                            }
                        }
                    }
                }
            }

            if (origOrdner.equals("")) {
                origOrdner = DIRECTORY_PREFIX + "_" + process.getTitle() + "_" + DIRECTORY_SUFFIX;
            }
            String rueckgabe = getImagesDirectory(process) + origOrdner + File.separator;
            if (ConfigMain.getBooleanParameter("createOrigFolderIfNotExists", false)
                    && process.getSortHelperStatus().equals("100000000")) {
                FilesystemHelper.createDirectory(rueckgabe);
            }
            return rueckgabe;
        } else {
            return getImagesTifDirectory(useFallBack, process);
        }
    }

    /**
     * Get images directory.
     *
     * @param process object
     * @return path
     */
    public String getImagesDirectory(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        String pfad = getProcessDataDirectory(process) + "images" + File.separator;
        FilesystemHelper.createDirectory(pfad);
        return pfad;
    }

    /**
     * Get source directory.
     *
     * @param process object
     * @return path
     */
    public String getSourceDirectory(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        SafeFile dir = new SafeFile(getImagesDirectory(process));
        FilenameFilter filterVerz = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return (name.endsWith("_" + "source"));
            }
        };
        SafeFile sourceFolder = null;
        String[] verzeichnisse = dir.list(filterVerz);
        if (verzeichnisse == null || verzeichnisse.length == 0) {
            sourceFolder = new SafeFile(dir, process.getTitle() + "_source");
            if (ConfigMain.getBooleanParameter("createSourceFolder", false)) {
                sourceFolder.mkdir();
            }
        } else {
            sourceFolder = new SafeFile(dir, verzeichnisse[0]);
        }

        return sourceFolder.getAbsolutePath();
    }

    /**
     * Get process data directory.
     *
     * @param process object
     * @return path
     */
    public String getProcessDataDirectory(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        String path = getProcessDataDirectoryIgnoreSwapping(process);

        if (process.isSwappedOutGui()) {
            ProcessSwapInTask pst = new ProcessSwapInTask();
            pst.initialize(process);
            pst.setProgress(1);
            pst.setShowMessages(true);
            pst.run();
            if (pst.getException() != null) {
                if (!new SafeFile(path, "images").exists() && !new SafeFile(path, "meta.xml").exists()) {
                    throw new SwapException(pst.getException().getMessage());
                } else {
                    process.setSwappedOutGui(false);
                }
                new ProcessDAO().save(process, this.getProgress(process));
            }
        }
        return path;
    }

    public String getOcrDirectory(Process process)
            throws SwapException, DAOException, IOException, InterruptedException {
        return getProcessDataDirectory(process) + "ocr" + File.separator;
    }

    public String getTxtDirectory(Process process)
            throws SwapException, DAOException, IOException, InterruptedException {
        return getOcrDirectory(process) + process.getTitle() + "_txt" + File.separator;
    }

    public String getWordDirectory(Process process)
            throws SwapException, DAOException, IOException, InterruptedException {
        return getOcrDirectory(process) + process.getTitle() + "_wc" + File.separator;
    }

    public String getPdfDirectory(Process process)
            throws SwapException, DAOException, IOException, InterruptedException {
        return getOcrDirectory(process) + process.getTitle() + "_pdf" + File.separator;
    }

    public String getAltoDirectory(Process process)
            throws SwapException, DAOException, IOException, InterruptedException {
        return getOcrDirectory(process) + process.getTitle() + "_alto" + File.separator;
    }

    public String getImportDirectory(Process process)
            throws SwapException, DAOException, IOException, InterruptedException {
        return getProcessDataDirectory(process) + "import" + File.separator;
    }

    /**
     * Get process data directory ignoring swapping.
     *
     * @param process object
     * @return path
     */
    public String getProcessDataDirectoryIgnoreSwapping(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        String pfad = this.help.getGoobiDataDirectory() + process.getId() + File.separator;
        pfad = pfad.replaceAll(" ", "__");
        FilesystemHelper.createDirectory(pfad);
        return pfad;
    }

    /**
     * The function getBatchID returns the batches the process is associated with as readable text
     * as read-only property "batchID".
     *
     * @return the batches the process is in
     */
    public String getBatchID(Process process) {
        if (process.getBatches() == null || process.getBatches().size() == 0) {
            return null;
        }
        StringBuilder result = new StringBuilder();
        BatchService batchService = new BatchService();
        for (Batch batch : process.getBatches()) {
            if (result.length() > 0) {
                result.append(", ");
            }
            result.append(batchService.getLabel(batch));
        }
        return result.toString();
    }

    /**
     * Get size of tasks' list.
     *
     * @param process object
     * @return size
     */
    public int getTasksSize(Process process) {
        if (process.getTasks() == null) {
            return 0;
        } else {
            return process.getTasks().size();
        }
    }

    /**
     * Get size of histories' list.
     *
     * @param process object
     * @return size
     */
    public int getHistorySize(Process process) {
        if (process.getHistory() == null) {
            return 0;
        } else {
            return process.getHistory().size();
        }
    }

    /**
     * Get size of properties' list.
     *
     * @param process object
     * @return size
     */
    public int getPropertiesSize(Process process) {
        if (process.getProperties() == null) {
            return 0;
        } else {
            return process.getProperties().size();
        }
    }

    /**
     * Get size of workpieces' list.
     *
     * @param process object
     * @return size
     */
    public int getWorkpiecesSize(Process process) {
        if (process.getWorkpieces() == null) {
            return 0;
        } else {
            return process.getWorkpieces().size();
        }
    }

    /**
     * Get size of templates' list.
     *
     * @param process object
     * @return size
     */
    public int getTemplatesSize(Process process) {
        if (process.getTemplates() == null) {
            return 0;
        } else {
            return process.getTemplates().size();
        }
    }

    /**
     * Get current task.
     *
     * @param process object
     * @return current task
     */
    public Task getCurrentTask(Process process) {
        for (Task task : process.getTasks()) {
            if (task.getProcessingStatusEnum() == TaskStatus.OPEN
                    || task.getProcessingStatusEnum() == TaskStatus.INWORK) {
                return task;
            }
        }
        return null;
    }

    public String getCreationDateAsString(Process process) {
        return Helper.getDateAsFormattedString(process.getCreationDate());
    }

    private HashMap<String, Integer> calculationForProgress(Process process) {
        HashMap<String, Integer> results = new HashMap<>();
        int open = 0;
        int inProcessing = 0;
        int closed = 0;
        Hibernate.initialize(process.getTasks());
        for (Task task : process.getTasks()) {
            if (task.getProcessingStatusEnum() == TaskStatus.DONE) {
                closed++;
            } else if (task.getProcessingStatusEnum() == TaskStatus.LOCKED) {
                open++;
            } else {
                inProcessing++;
            }
        }

        results.put("open", open);
        results.put("inProcessing", inProcessing);
        results.put("closed", closed);

        if ((open + inProcessing + closed) == 0) {
            results.put("open", 1);
        }

        return results;
    }

    /**
     * Old getFortschritt().
     *
     * @param process object
     * @return string
     */
    public String getProgress(Process process) {
        HashMap<String, Integer> steps = calculationForProgress(process);
        double open = 0;
        double inProcessing = 0;
        double closed = 0;

        open = (steps.get("open") * 100)
                / (double) (steps.get("open") + steps.get("inProcessing") + steps.get("closed"));
        inProcessing = (steps.get("inProcessing") * 100)
                / (double) (steps.get("open") + steps.get("inProcessing") + steps.get("closed"));
        closed = 100 - open - inProcessing;

        java.text.DecimalFormat df = new java.text.DecimalFormat("#000");

        return df.format(closed) + df.format(inProcessing) + df.format(closed);
    }

    /**
     * Old getFortschritt1().
     *
     * @param process object
     * @return return  progress for open steps
     */
    public int getProgressOpen(Process process) {
        HashMap<String, Integer> steps = calculationForProgress(process);
        return (steps.get("open") * 100) / (steps.get("open") + steps.get("inProcessing") + steps.get("closed"));
    }

    /**
     * Old getFortschritt2().
     *
     * @param process object
     * @return progress for processed steps
     */
    public int getProgressInProcessing(Process process) {
        HashMap<String, Integer> steps = calculationForProgress(process);

        return (steps.get("inProcessing") * 100)
                / (steps.get("open") + steps.get("inProcessing") + steps.get("closed"));
    }

    /**
     * Old getFortschritt3().
     *
     * @param process object
     * @return progress for closed steps
     */
    public int getProgressClosed(Process process) {
        HashMap<String, Integer> steps = calculationForProgress(process);

        double open = 0;
        double inProcessing = 0;
        double closed = 0;

        open = ((steps.get("open") * 100)
                / (double) (steps.get("open") + steps.get("inProcessing") + steps.get("closed")));
        inProcessing = (steps.get("inProcessing") * 100)
                / (double) (steps.get("open") + steps.get("inProcessing") + steps.get("closed"));
        closed = 100 - open - inProcessing;
        return (int) closed;
    }

    public String getMetadataFilePath(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        return getProcessDataDirectory(process) + "meta.xml";
    }

    public String getTemplateFilePath(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        return getProcessDataDirectory(process) + "template.xml";
    }

    public String getFulltextFilePath(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        return getProcessDataDirectory(process) + "fulltext.xml";
    }

    /**
     * Read metadata file.
     *
     * @param process object
     * @return filer format
     */
    public Fileformat readMetadataFile(Process process) throws ReadException, IOException, InterruptedException,
            PreferencesException, SwapException, DAOException {
        if (!checkForMetadataFile(process)) {
            throw new IOException(
                    Helper.getTranslation("metadataFileNotFound") + " " + getMetadataFilePath(process));
        }
        Hibernate.initialize(process.getRuleset());
        /* prfen, welches Format die Metadaten haben (Mets, xstream oder rdf */
        String type = MetadatenHelper.getMetaFileType(getMetadataFilePath(process));
        if (myLogger.isDebugEnabled()) {
            myLogger.debug("current meta.xml file type for id " + process.getId() + ": " + type);
        }

        Fileformat ff = determineFileFormat(type, process);

        try {
            ff.read(getMetadataFilePath(process));
        } catch (ReadException e) {
            if (e.getMessage().startsWith("Parse error at line -1")) {
                Helper.setFehlerMeldung("metadataCorrupt");
            } else {
                throw e;
            }
        }
        return ff;
    }

    private Fileformat determineFileFormat(String type, Process process) throws PreferencesException {
        Fileformat fileFormat = null;
        RulesetService rulesetService = new RulesetService();

        switch (type) {
        case "metsmods":
            fileFormat = new MetsModsImportExport(rulesetService.getPreferences(process.getRuleset()));
            break;
        case "mets":
            fileFormat = new MetsMods(rulesetService.getPreferences(process.getRuleset()));
            break;
        case "xstream":
            fileFormat = new XStream(rulesetService.getPreferences(process.getRuleset()));
            break;
        default:
            fileFormat = new RDFFile(rulesetService.getPreferences(process.getRuleset()));
            break;
        }
        return fileFormat;
    }

    // backup of meta.xml
    private void createBackupFile(Process process)
            throws IOException, InterruptedException, SwapException, DAOException {
        int numberOfBackups = 0;

        if (ConfigMain.getIntParameter("numberOfMetaBackups") != 0) {
            numberOfBackups = ConfigMain.getIntParameter("numberOfMetaBackups");
        }

        if (numberOfBackups != 0) {
            BackupFileRotation bfr = new BackupFileRotation();
            bfr.setNumberOfBackups(numberOfBackups);
            bfr.setFormat("meta.*\\.xml");
            bfr.setProcessDataDirectory(getProcessDataDirectory(process));
            bfr.performBackup();
        } else {
            myLogger.warn("No backup configured for meta data files.");
        }
    }

    private boolean checkForMetadataFile(Process process)
            throws IOException, InterruptedException, SwapException, DAOException, PreferencesException {
        boolean result = true;
        SafeFile f = new SafeFile(getMetadataFilePath(process));
        if (!f.exists()) {
            result = false;
        }

        return result;
    }

    private String getTemporaryMetadataFileName(String fileName) {

        SafeFile temporaryFile = new SafeFile(fileName);
        String directoryPath = temporaryFile.getParentFile().getPath();
        String temporaryFileName = TEMPORARY_FILENAME_PREFIX + temporaryFile.getName();

        return directoryPath + File.separator + temporaryFileName;
    }

    private void removePrefixFromRelatedMetsAnchorFilesFor(String temporaryMetadataFilename) throws IOException {
        SafeFile temporaryFile = new SafeFile(temporaryMetadataFilename);
        SafeFile directoryPath = new SafeFile(temporaryFile.getParentFile().getPath());
        for (SafeFile temporaryAnchorFile : directoryPath.listFiles()) {
            String temporaryAnchorFileName = temporaryAnchorFile.toString();
            if (temporaryAnchorFile.isFile()
                    && FilenameUtils.getBaseName(temporaryAnchorFileName).startsWith(TEMPORARY_FILENAME_PREFIX)) {
                String anchorFileName = FilenameUtils.concat(FilenameUtils.getFullPath(temporaryAnchorFileName),
                        temporaryAnchorFileName.replace(TEMPORARY_FILENAME_PREFIX, ""));
                temporaryAnchorFileName = FilenameUtils.concat(FilenameUtils.getFullPath(temporaryAnchorFileName),
                        temporaryAnchorFileName);
                FilesystemHelper.renameFile(temporaryAnchorFileName, anchorFileName);
            }
        }
    }

    /**
     * Write metadata file.
     *
     * @param gdzfile file format
     * @param process object
     */
    public void writeMetadataFile(Fileformat gdzfile, Process process) throws IOException, InterruptedException,
            SwapException, DAOException, WriteException, PreferencesException {
        RulesetService rulesetService = new RulesetService();
        boolean backupCondition;
        boolean writeResult;
        SafeFile temporaryMetadataFile;
        Fileformat ff;
        String metadataFileName;
        String temporaryMetadataFileName;

        Hibernate.initialize(process.getRuleset());
        switch (MetadataFormat.findFileFormatsHelperByName(process.getProject().getFileFormatInternal())) {
        case METS:
            ff = new MetsMods(rulesetService.getPreferences(process.getRuleset()));
            break;
        case RDF:
            ff = new RDFFile(rulesetService.getPreferences(process.getRuleset()));
            break;
        default:
            ff = new XStream(rulesetService.getPreferences(process.getRuleset()));
            break;
        }
        // createBackupFile();
        metadataFileName = getMetadataFilePath(process);
        temporaryMetadataFileName = getTemporaryMetadataFileName(metadataFileName);

        ff.setDigitalDocument(gdzfile.getDigitalDocument());
        // ff.write(getMetadataFilePath());
        writeResult = ff.write(temporaryMetadataFileName);
        temporaryMetadataFile = new SafeFile(temporaryMetadataFileName);
        backupCondition = writeResult && temporaryMetadataFile.exists() && (temporaryMetadataFile.length() > 0);
        if (backupCondition) {
            createBackupFile(process);
            FilesystemHelper.renameFile(temporaryMetadataFileName, metadataFileName);
            removePrefixFromRelatedMetsAnchorFilesFor(temporaryMetadataFileName);
        }
    }

    public void writeMetadataAsTemplateFile(Fileformat inFile, Process process) throws IOException,
            InterruptedException, SwapException, DAOException, WriteException, PreferencesException {
        inFile.write(getTemplateFilePath(process));
    }

    /**
     * Read metadata as template file.
     *
     * @param process object
     * @return file format
     */
    public Fileformat readMetadataAsTemplateFile(Process process) throws ReadException, IOException,
            InterruptedException, PreferencesException, SwapException, DAOException {
        RulesetService rulesetService = new RulesetService();
        Hibernate.initialize(process.getRuleset());
        if (new SafeFile(getTemplateFilePath(process)).exists()) {
            Fileformat ff = null;
            String type = MetadatenHelper.getMetaFileType(getTemplateFilePath(process));
            if (myLogger.isDebugEnabled()) {
                myLogger.debug("current template.xml file type: " + type);
            }
            ff = determineFileFormat(type, process);
            /*if (type.equals("mets")) {
            ff = new MetsMods(rulesetService.getPreferences(process.getRuleset()));
            } else if (type.equals("xstream")) {
            ff = new XStream(rulesetService.getPreferences(process.getRuleset()));
            } else {
            ff = new RDFFile(rulesetService.getPreferences(process.getRuleset()));
            }*/
            ff.read(getTemplateFilePath(process));
            return ff;
        } else {
            throw new IOException("File does not exist: " + getTemplateFilePath(process));
        }
    }

    /**
     * Check whether the operation contains steps that are not assigned to a user or user group.
     */
    public boolean getContainsUnreachableSteps(Process process) {
        TaskService taskService = new TaskService();
        if (process.getTasks().size() == 0) {
            return true;
        }
        for (Task task : process.getTasks()) {
            if (taskService.getUserGroupsSize(task) == 0 && taskService.getUsersSize(task) == 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if there is one task in edit mode, where the user has the rights to write to image folder.
     */
    public boolean isImageFolderInUse(Process process) {
        for (Task task : process.getTasks()) {
            if (task.getProcessingStatusEnum() == TaskStatus.INWORK && task.isTypeImagesWrite()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Get user of task in edit mode with rights to write to image folder.
     */
    public User getImageFolderInUseUser(Process process) {
        for (Task task : process.getTasks()) {
            if (task.getProcessingStatusEnum() == TaskStatus.INWORK && task.isTypeImagesWrite()) {
                return task.getProcessingUser();
            }
        }
        return null;
    }

    /**
     * Download docket.
     *
     * @param process object
     * @return empty string?
     */
    public String downloadDocket(Process process) {

        if (myLogger.isDebugEnabled()) {
            myLogger.debug("generate docket for process " + process.getId());
        }
        String rootPath = ConfigMain.getParameter("xsltFolder");
        SafeFile xsltFile = new SafeFile(rootPath, "docket.xsl");
        if (process.getDocket() != null) {
            xsltFile = new SafeFile(rootPath, process.getDocket().getFile());
            if (!xsltFile.exists()) {
                Helper.setFehlerMeldung("docketMissing");
                return "";
            }
        }
        FacesContext facesContext = FacesContext.getCurrentInstance();
        if (!facesContext.getResponseComplete()) {
            HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
            String fileName = process.getTitle() + ".pdf";
            ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext();
            String contentType = servletContext.getMimeType(fileName);
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");

            // write run note to servlet output stream
            try {
                ServletOutputStream out = response.getOutputStream();
                ExportDocket ern = new ExportDocket();
                ern.startExport(process, out, xsltFile.getAbsolutePath());
                out.flush();
                facesContext.responseComplete();
            } catch (Exception e) {
                Helper.setFehlerMeldung("Exception while exporting run note.", e.getMessage());
                response.reset();
            }

        }
        return "";
    }

    /**
     * Get first open task for the process.
     *
     * @param process object
     * @return first open task
     */
    public Task getFirstOpenStep(Process process) {

        for (Task step : process.getTasks()) {
            if (step.getProcessingStatusEnum().equals(TaskStatus.OPEN)
                    || step.getProcessingStatusEnum().equals(TaskStatus.INWORK)) {
                return step;
            }
        }
        return null;
    }

    /**
     * Get method from name.
     *
     * @param methodName string
     * @param process object
     * @return method from name
     */
    public String getMethodFromName(String methodName, Process process) {
        java.lang.reflect.Method method;
        try {
            method = this.getClass().getMethod(methodName);
            Object o = method.invoke(this);
            return (String) o;
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException e) {
            myLogger.debug("exception: " + e);
        }
        try {
            String folder = this.getImagesTifDirectory(false, process);
            folder = folder.substring(0, folder.lastIndexOf("_"));
            folder = folder + "_" + methodName;
            if (new SafeFile(folder).exists()) {
                return folder;
            }
        } catch (DAOException | InterruptedException | IOException | SwapException ex) {
            myLogger.debug("exception: " + ex);
        }
        return null;
    }

    /*public List<String> getPossibleDigitalCollections(Process process) throws JDOMException, IOException {
    return DigitalCollections.possibleDigitalCollectionsForProcess(process);
    }*/

    /**
     * The addMessageToWikiField() method is a helper method which composes the new wiki field using a StringBuilder.
     * The message is encoded using HTML entities to prevent certain characters from playing merry havoc when the
     * message box shall be rendered in a browser later.
     *
     * @param message the message to append
     */
    public Process addToWikiField(String message, Process process) {
        StringBuilder composer = new StringBuilder();
        if (process.getWikiField() != null && process.getWikiField().length() > 0) {
            composer.append(process.getWikiField());
            composer.append("\r\n");
        }
        composer.append("<p>");
        composer.append(StringEscapeUtils.escapeHtml(message));
        composer.append("</p>");
        process.setWikiField(composer.toString());

        return process;
    }

    /**
     * The method addToWikiField() adds a message with a given level to the wiki field of the process. Four level
     * strings will be recognized and result in different colors:
     *
     * <dl>
     * <dt><code>debug</code></dt>
     * <dd>gray</dd>
     * <dt><code>error</code></dt>
     * <dd>red</dd>
     * <dt><code>user</code></dt>
     * <dd>green</dd>
     * <dt><code>warn</code></dt>
     * <dd>orange</dd>
     * <dt><i>any other value</i></dt>
     * <dd>blue</dd>
     * <dt>
     *
     * @param level message colour, one of: "debug", "error", "info", "user" or "warn";
     *                 any other value defaults to "info"
     * @param message text
     */
    public String addToWikiField(String level, String message, Process process) {
        return WikiFieldHelper.getWikiMessage(process, process.getWikiField(), level, message);
    }

    /**
     * The method addToWikiField() adds a message signed by the given user to the wiki field of the process.
     *
     * @param user to sign the message with
     * @param message to print
     */
    public void addToWikiField(User user, String message, Process process) {
        String text = message + " (" + user.getSurname() + ")";
        //addToWikiField("user", process, text);
    }

    /**
     * The method createProcessDirs() starts creation of directories configured by parameter processDirs within
     * goobi_config.properties
     */
    public void createProcessDirs(Process process)
            throws SwapException, DAOException, IOException, InterruptedException {

        String[] processDirs = ConfigMain.getStringArrayParameter("processDirs");

        for (String processDir : processDirs) {
            FilesystemHelper.createDirectory(FilenameUtils.concat(this.getProcessDataDirectory(process),
                    processDir.replace("(processtitle)", process.getTitle())));
        }

    }

    /**
     * The function getDigitalDocument() returns the digital act of this process.
     *
     * @return the digital act of this process
     * @throws PreferencesException if the no node corresponding to the file format is available in the rule set
     *             configured
     * @throws ReadException if the meta data file cannot be read
     * @throws SwapException if an error occurs while the process is swapped back in
     * @throws DAOException if an error occurs while saving the fact that the process has
     *             been swapped back in to the database
     * @throws IOException if creating the process directory or reading the meta data file fails
     * @throws InterruptedException if the current thread is interrupted by another thread while
     *             it is waiting for the shell script to create the directory to finish
     */
    public DigitalDocument getDigitalDocument(Process process) throws PreferencesException, ReadException,
            SwapException, DAOException, IOException, InterruptedException {
        return readMetadataFile(process).getDigitalDocument();
    }

    /**
     * Filter for correction / solution messages.
     *
     * @param lpe List of process properties
     * @return List of filtered correction / solution messages
     */
    protected List<ProcessProperty> filterForCorrectionSolutionMessages(List<ProcessProperty> lpe) {
        ArrayList<ProcessProperty> filteredList = new ArrayList<ProcessProperty>();
        List<String> listOfTranslations = new ArrayList<String>();
        String propertyTitle = "";

        listOfTranslations.add("Korrektur notwendig");
        listOfTranslations.add("Korrektur durchgefuehrt");
        listOfTranslations.add(Helper.getTranslation("Korrektur notwendig"));
        listOfTranslations.add(Helper.getTranslation("Korrektur durchgefuehrt"));

        if ((lpe == null) || (lpe.size() == 0)) {
            return filteredList;
        }

        // filtering for correction and solution messages
        for (ProcessProperty pe : lpe) {
            propertyTitle = pe.getTitle();
            if (listOfTranslations.contains(propertyTitle)) {
                filteredList.add(pe);
            }
        }
        return filteredList;
    }

    /**
     * Filter and sort after creation date list of process properties for correction and solution messages.
     *
     * @return list of ProcessProperty objects
     */
    public List<ProcessProperty> getSortedCorrectionSolutionMessages(Process process) {
        List<ProcessProperty> filteredList;
        List<ProcessProperty> lpe = process.getProperties();

        if (lpe.isEmpty()) {
            return new ArrayList<>();
        }

        filteredList = filterForCorrectionSolutionMessages(lpe);

        // sorting after creation date
        Collections.sort(filteredList, new Comparator<ProcessProperty>() {
            @Override
            public int compare(ProcessProperty o1, ProcessProperty o2) {
                Date o1Date = o1.getCreationDate();
                Date o2Date = o2.getCreationDate();
                if (o1Date == null) {
                    o1Date = new Date();
                }
                if (o2Date == null) {
                    o2Date = new Date();
                }
                return o1Date.compareTo(o2Date);
            }
        });

        return new ArrayList<>(filteredList);
    }
}