de.sub.goobi.helper.HelperSchritte.java Source code

Java tutorial

Introduction

Here is the source code for de.sub.goobi.helper.HelperSchritte.java

Source

package de.sub.goobi.helper;

/**
 * This file is part of the Goobi Application - a Workflow tool for the support of mass digitization.
 * 
 * Visit the websites for more information.
 *          - https://goobi.io
 *          - https://www.intranda.com
 *          - https://github.com/intranda/goobi
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 * 
 * Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions
 * of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to
 * link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and
 * distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and
 * conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but you are not obliged to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 */

import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.log4j.Logger;
import org.goobi.api.mail.SendMail;
import org.goobi.beans.LogEntry;
import org.goobi.beans.Process;
import org.goobi.beans.Step;
import org.goobi.beans.User;
import org.goobi.managedbeans.LoginBean;
import org.goobi.production.enums.LogType;
import org.goobi.production.enums.PluginType;
import org.goobi.production.flow.jobs.HistoryAnalyserJob;
import org.goobi.production.plugin.PluginLoader;
import org.goobi.production.plugin.interfaces.IExportPlugin;
import org.goobi.production.plugin.interfaces.IValidatorPlugin;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import de.sub.goobi.config.ConfigurationHelper;
import de.sub.goobi.export.dms.ExportDms;
import de.sub.goobi.helper.enums.HistoryEventType;
import de.sub.goobi.helper.enums.StepEditType;
import de.sub.goobi.helper.enums.StepStatus;
import de.sub.goobi.helper.exceptions.DAOException;
import de.sub.goobi.helper.exceptions.ExportFileException;
import de.sub.goobi.helper.exceptions.SwapException;
import de.sub.goobi.helper.exceptions.UghHelperException;
import de.sub.goobi.persistence.managers.HistoryManager;
import de.sub.goobi.persistence.managers.MetadataManager;
import de.sub.goobi.persistence.managers.ProcessManager;
import de.sub.goobi.persistence.managers.StepManager;
import ugh.dl.DigitalDocument;
import ugh.dl.Fileformat;
import ugh.dl.Prefs;
import ugh.exceptions.DocStructHasNoTypeException;
import ugh.exceptions.UGHException;

public class HelperSchritte {
    private static final Logger logger = Logger.getLogger(HelperSchritte.class);
    public final static String DIRECTORY_PREFIX = "orig_";
    private static final Namespace goobiNamespace = Namespace.getNamespace("goobi",
            "http://meta.goobi.org/v1.5.1/");
    private static final Namespace mets = Namespace.getNamespace("mets", "http://www.loc.gov/METS/");
    private static final Namespace mods = Namespace.getNamespace("mods", "http://www.loc.gov/mods/v3");

    /**
     * Schritt abschliessen und dabei parallele Schritte bercksichtigen ================================================================
     */

    public void CloseStepObjectAutomatic(Step currentStep) {
        closeStepObject(currentStep, currentStep.getProcessId());
    }

    private void closeStepObject(Step currentStep, int processId) {
        currentStep.setBearbeitungsstatusEnum(StepStatus.DONE);
        Date myDate = new Date();

        currentStep.setBearbeitungszeitpunkt(myDate);
        try {
            LoginBean lf = (LoginBean) Helper.getManagedBeanValue("#{LoginForm}");
            if (lf != null) {
                User ben = lf.getMyBenutzer();
                if (ben != null) {
                    currentStep.setBearbeitungsbenutzer(ben);
                }
            }
        } catch (Exception e) {

        }
        currentStep.setBearbeitungsende(myDate);
        try {
            StepManager.saveStep(currentStep);
            Helper.addMessageToProcessLog(currentStep.getProcessId(), LogType.DEBUG,
                    "Step '" + currentStep.getTitel() + "' closed.");
        } catch (DAOException e) {
            logger.error("An exception occurred while closing the step '" + currentStep.getTitel()
                    + "' of process with ID " + processId, e);
        }

        if (currentStep.isUpdateMetadataIndex()) {
            try {
                String metdatdaPath = currentStep.getProzess().getMetadataFilePath();
                String anchorPath = metdatdaPath.replace("meta.xml", "meta_anchor.xml");
                Path metadataFile = Paths.get(metdatdaPath);
                Path anchorFile = Paths.get(anchorPath);
                Map<String, List<String>> pairs = new HashMap<>();

                extractMetadata(metadataFile, pairs);

                if (StorageProvider.getInstance().isFileExists(anchorFile)) {
                    extractMetadata(anchorFile, pairs);
                }

                MetadataManager.updateMetadata(currentStep.getProzess().getId(), pairs);

                // now add all authority fields to the metadata pairs
                extractAuthorityMetadata(metadataFile, pairs);
                if (StorageProvider.getInstance().isFileExists(anchorFile)) {
                    extractAuthorityMetadata(anchorFile, pairs);
                }
                MetadataManager.updateJSONMetadata(processId, pairs);

                HistoryAnalyserJob.updateHistory(currentStep.getProzess());

                if (!currentStep.getProzess().isMediaFolderExists() && StorageProvider.getInstance()
                        .isFileExists(Paths.get(currentStep.getProzess().getImagesDirectory()))) {
                    currentStep.getProzess().setMediaFolderExists(true);
                    ProcessManager.saveProcessInformation(currentStep.getProzess());
                }

            } catch (SwapException | DAOException | IOException | InterruptedException e1) {
                logger.error("An exception occurred while updating the metadata file process with ID " + processId,
                        e1);
            }
        }

        List<Step> automatischeSchritte = new ArrayList<>();
        List<Step> stepsToFinish = new ArrayList<>();
        SendMail.getInstance().sendMailToAssignedUser(currentStep, StepStatus.DONE);
        HistoryManager.addHistory(myDate, new Integer(currentStep.getReihenfolge()).doubleValue(),
                currentStep.getTitel(), HistoryEventType.stepDone.getValue(), processId);

        /* prfen, ob es Schritte gibt, die parallel stattfinden aber noch nicht abgeschlossen sind */
        List<Step> steps = StepManager.getStepsForProcess(processId);
        List<Step> allehoeherenSchritte = new ArrayList<>();
        int offeneSchritteGleicherReihenfolge = 0;
        for (Step so : steps) {
            if (so.getReihenfolge() == currentStep.getReihenfolge()
                    && !(so.getBearbeitungsstatusEnum().equals(StepStatus.DONE)
                            || so.getBearbeitungsstatusEnum().equals(StepStatus.DEACTIVATED))
                    && so.getId() != currentStep.getId()) {
                offeneSchritteGleicherReihenfolge++;
            } else if (so.getReihenfolge() > currentStep.getReihenfolge()) {
                allehoeherenSchritte.add(so);
            }
        }

        /* wenn keine offenen parallelschritte vorhanden sind, die nchsten Schritte aktivieren */
        if (offeneSchritteGleicherReihenfolge == 0) {
            int reihenfolge = 0;
            boolean matched = false;
            for (Step myStep : allehoeherenSchritte) {
                if (reihenfolge < myStep.getReihenfolge() && !matched) {
                    reihenfolge = myStep.getReihenfolge();
                }

                if (reihenfolge == myStep.getReihenfolge()
                        && !(myStep.getBearbeitungsstatusEnum().equals(StepStatus.DONE)
                                || myStep.getBearbeitungsstatusEnum().equals(StepStatus.DEACTIVATED))) {
                    /*
                     * open step, if it is locked, otherwise stop
                     */

                    if (myStep.getBearbeitungsstatusEnum().equals(StepStatus.LOCKED)) {
                        myStep.setBearbeitungsstatusEnum(StepStatus.OPEN);
                        myStep.setBearbeitungszeitpunkt(myDate);
                        myStep.setEditTypeEnum(StepEditType.AUTOMATIC);
                        SendMail.getInstance().sendMailToAssignedUser(myStep, StepStatus.OPEN);
                        HistoryManager.addHistory(myDate, new Integer(myStep.getReihenfolge()).doubleValue(),
                                myStep.getTitel(), HistoryEventType.stepOpen.getValue(), processId);
                        /* wenn es ein automatischer Schritt mit Script ist */
                        if (myStep.isTypAutomatisch()) {
                            automatischeSchritte.add(myStep);
                        } else if (myStep.isTypBeimAnnehmenAbschliessen()) {
                            stepsToFinish.add(myStep);
                        }
                        try {
                            StepManager.saveStep(myStep);
                            Helper.addMessageToProcessLog(currentStep.getProcessId(), LogType.DEBUG,
                                    "Step '" + myStep.getTitel() + "' opened.");
                        } catch (DAOException e) {
                            logger.error("An exception occurred while saving a step for process with ID "
                                    + myStep.getProcessId(), e);
                        }
                    }
                    matched = true;

                } else {
                    if (matched) {
                        break;
                    }
                }
            }
        }
        Process po = ProcessManager.getProcessById(processId);

        try {
            int numberOfFiles = StorageProvider.getInstance()
                    .getNumberOfFiles(Paths.get(po.getImagesOrigDirectory(true)));
            if (numberOfFiles > 0 && po.getSortHelperImages() != numberOfFiles) {
                ProcessManager.updateImages(numberOfFiles, processId);
            }

        } catch (Exception e) {
            logger.error("An exception occurred while closing a step for process with ID " + po.getId(), e);
        }

        updateProcessStatus(processId);
        for (Step automaticStep : automatischeSchritte) {
            automaticStep.setBearbeitungsbeginn(new Date());
            automaticStep.setBearbeitungsbenutzer(null);
            automaticStep.setBearbeitungsstatusEnum(StepStatus.INWORK);
            automaticStep.setEditTypeEnum(StepEditType.AUTOMATIC);
            SendMail.getInstance().sendMailToAssignedUser(currentStep, StepStatus.INWORK);
            HistoryManager.addHistory(automaticStep.getBearbeitungsbeginn(),
                    automaticStep.getReihenfolge().doubleValue(), automaticStep.getTitel(),
                    HistoryEventType.stepInWork.getValue(), automaticStep.getProzess().getId());
            try {
                StepManager.saveStep(automaticStep);
                Helper.addMessageToProcessLog(currentStep.getProcessId(), LogType.DEBUG,
                        "Step '" + automaticStep.getTitel() + "' started to work automatically.");
            } catch (DAOException e) {
                logger.error("An exception occurred while saving an automatic step for process with ID "
                        + automaticStep.getProcessId(), e);
            }
            // save
            if (logger.isDebugEnabled()) {
                logger.debug("Starting scripts for step with stepId " + automaticStep.getId() + " and processId "
                        + automaticStep.getProcessId());
            }
            ScriptThreadWithoutHibernate myThread = new ScriptThreadWithoutHibernate(automaticStep);
            myThread.start();
        }
        for (Step finish : stepsToFinish) {
            CloseStepObjectAutomatic(finish);
        }

    }

    public void updateProcessStatus(int processId) {

        int offen = 0;
        int inBearbeitung = 0;
        int abgeschlossen = 0;
        List<Step> stepsForProcess = StepManager.getStepsForProcess(processId);
        for (Step step : stepsForProcess) {
            if (step.getBearbeitungsstatusEnum().equals(StepStatus.DONE)
                    || step.getBearbeitungsstatusEnum().equals(StepStatus.DEACTIVATED)) {
                abgeschlossen++;
            } else if (step.getBearbeitungsstatusEnum().equals(StepStatus.LOCKED)) {
                offen++;
            } else {
                inBearbeitung++;
            }
        }
        double offen2 = 0;
        double inBearbeitung2 = 0;
        double abgeschlossen2 = 0;

        if ((offen + inBearbeitung + abgeschlossen) == 0) {
            offen = 1;
        }

        offen2 = (offen * 100) / (double) (offen + inBearbeitung + abgeschlossen);
        inBearbeitung2 = (inBearbeitung * 100) / (double) (offen + inBearbeitung + abgeschlossen);
        abgeschlossen2 = 100 - offen2 - inBearbeitung2;
        // (abgeschlossen * 100) / (offen + inBearbeitung + abgeschlossen);
        java.text.DecimalFormat df = new java.text.DecimalFormat("#000");
        String value = df.format(abgeschlossen2) + df.format(inBearbeitung2) + df.format(offen2);

        ProcessManager.updateProcessStatus(value, processId);
    }

    public ShellScriptReturnValue executeAllScriptsForStep(Step step, boolean automatic) {
        List<String> scriptpaths = step.getAllScriptPaths();
        int count = 1;
        int size = scriptpaths.size();
        ShellScriptReturnValue returnParameter = null;
        outerloop: for (String script : scriptpaths) {
            if (logger.isDebugEnabled()) {
                logger.debug("Starting script " + script + " for process with ID " + step.getProcessId());
            }

            if (script != null && !script.equals(" ") && script.length() != 0) {
                if (automatic && (count == size)) {
                    returnParameter = executeScriptForStepObject(step, script, true);
                } else {
                    returnParameter = executeScriptForStepObject(step, script, false);
                }
            }

            if (automatic) {
                switch (returnParameter.getReturnCode()) {
                // return code 99 means wait for finishing
                case 99:

                    break;
                // return code 98: re-open task
                case 98:
                    reOpenStep(step);
                    break;
                // return code 0: script returned without error
                case 0:
                    break;
                // everything else: error
                default:
                    errorStep(step);
                    break outerloop;

                }
            }
            //            if (returnParameter != 0 && automatic && returnParameter != 99) {
            //                errorStep(step);
            //                break;
            //            }
            count++;
        }
        return returnParameter;
    }

    public void runHttpStep(Step step) {
        if (!step.isTypAutomatisch()) {
            return;
        }
        DigitalDocument dd = null;
        Process po = step.getProzess();
        Prefs prefs = null;
        try {
            prefs = po.getRegelsatz().getPreferences();
            Fileformat ff = po.readMetadataFile();
            if (ff == null) {
                logger.error("Metadata file is not readable for process with ID " + step.getProcessId());
                LogEntry le = new LogEntry();
                le.setProcessId(step.getProzess().getId());
                le.setContent("Metadata file is not readable");
                le.setType(LogType.ERROR);
                le.setUserName("http step");
                ProcessManager.saveLogEntry(le);
                errorStep(step);
                return;
            }
            dd = ff.getDigitalDocument();
        } catch (Exception e2) {
            logger.error("An exception occurred while reading the metadata file for process with ID "
                    + step.getProcessId(), e2);
            LogEntry le = new LogEntry();
            le.setProcessId(step.getProzess().getId());
            le.setContent("error reading metadata file");
            le.setType(LogType.ERROR);
            le.setUserName("http step");
            ProcessManager.saveLogEntry(le);
            errorStep(step);
            return;
        }
        VariableReplacer replacer = new VariableReplacer(dd, prefs, step.getProzess(), step);
        String bodyStr = null;
        if (step.isHttpEscapeBodyJson()) {
            //first parse String to JSON, then replace every value with the replacer
            Gson gson = new Gson();
            JsonElement jel = gson.fromJson(step.getHttpJsonBody(), JsonElement.class);
            replaceJsonElement(jel, replacer);
            bodyStr = gson.toJson(jel);
        } else {
            bodyStr = replacer.replace(step.getHttpJsonBody());
        }
        String url = replacer.replace(step.getHttpUrl());
        // START dirty hack to allow testing with certs with wrong hostnames, this should be removed when we have correct hostnames/certificates
        SSLConnectionSocketFactory scsf = null;
        try {
            scsf = new SSLConnectionSocketFactory(
                    SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(),
                    NoopHostnameVerifier.INSTANCE);
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e1) {
            LogEntry le = new LogEntry();
            le.setCreationDate(new Date());
            le.setProcessId(step.getProzess().getId());
            le.setContent("error executing http request: " + e1.getMessage());
            le.setType(LogType.ERROR);
            le.setUserName("http step");
            ProcessManager.saveLogEntry(le);
            errorStep(step);
            logger.error(e1);
            return;
        }
        // END dirty hack
        HttpClient httpclient = HttpClients.custom().setSSLSocketFactory(scsf).build();
        Executor executor = Executor.newInstance(httpclient);
        try {
            HttpResponse resp = null;
            switch (step.getHttpMethod()) {
            case "POST":
                resp = executor.execute(Request.Post(url).bodyString(bodyStr, ContentType.APPLICATION_JSON))
                        .returnResponse();
                break;
            case "PUT":
                resp = executor.execute(Request.Put(url).bodyString(bodyStr, ContentType.APPLICATION_JSON))
                        .returnResponse();
                break;
            case "PATCH":
                resp = executor.execute(Request.Patch(url).bodyString(bodyStr, ContentType.APPLICATION_JSON))
                        .returnResponse();
                break;
            default:
                //TODO: error to process log
                break;
            }
            if (resp != null) {
                String respStr = "- no response body -";
                if (resp.getEntity() != null && resp.getEntity().getContentLength() < 20000) {
                    StringWriter writer = new StringWriter();
                    Charset encoding = Charset.forName("utf-8");
                    if (resp.getEntity().getContentEncoding() != null) {
                        try {
                            encoding = Charset.forName(resp.getEntity().getContentEncoding().getValue());
                        } catch (Exception e) {
                            //we can do nothing here
                            logger.error(e);
                        }
                    }
                    IOUtils.copy(resp.getEntity().getContent(), writer, encoding);
                    respStr = writer.toString();
                }
                int statusCode = resp.getStatusLine().getStatusCode();
                if (statusCode >= 400) {
                    LogEntry le = new LogEntry();
                    le.setCreationDate(new Date());
                    le.setProcessId(step.getProzess().getId());
                    le.setContent(String.format("Server returned status code %d, response body was: '%s'",
                            statusCode, respStr));
                    le.setType(LogType.ERROR);
                    le.setUserName("http step");
                    ProcessManager.saveLogEntry(le);
                    errorStep(step);
                    logger.error(respStr);
                    return;
                }
                LogEntry le = new LogEntry();
                le.setCreationDate(new Date());
                le.setProcessId(step.getProzess().getId());
                le.setContent(respStr);
                le.setType(LogType.INFO);
                le.setUserName("http step");
                ProcessManager.saveLogEntry(le);
                if (step.isHttpCloseStep()) {
                    CloseStepObjectAutomatic(step);
                }
                logger.info(respStr);
            } else {
                LogEntry le = new LogEntry();
                le.setCreationDate(new Date());
                le.setProcessId(step.getProzess().getId());
                le.setContent("error executing http request");
                le.setType(LogType.ERROR);
                le.setUserName("http step");
                ProcessManager.saveLogEntry(le);
            }
        } catch (IOException e) {
            LogEntry le = new LogEntry();
            le.setCreationDate(new Date());
            le.setProcessId(step.getProzess().getId());
            le.setContent("error executing http request: " + e.getMessage());
            le.setType(LogType.ERROR);
            le.setUserName("http step");
            ProcessManager.saveLogEntry(le);
            errorStep(step);
            logger.error(e);
        }
    }

    private void replaceJsonElement(JsonElement jel, VariableReplacer replacer) {
        if (jel.isJsonObject()) {
            JsonObject obj = jel.getAsJsonObject();
            for (Entry<String, JsonElement> objEntry : obj.entrySet()) {
                if (objEntry.getValue().isJsonPrimitive()) {
                    JsonPrimitive jPrim = objEntry.getValue().getAsJsonPrimitive();
                    if (jPrim.isString()) {
                        String newVal = replacer.replace(jPrim.getAsString());
                        obj.addProperty(objEntry.getKey(), newVal);
                    }
                } else {
                    replaceJsonElement(objEntry.getValue(), replacer);
                }
            }
        } else if (jel.isJsonArray()) {
            JsonArray jArr = jel.getAsJsonArray();
            for (int i = 0; i < jArr.size(); i++) {
                JsonElement innerJel = jArr.get(i);
                if (innerJel.isJsonPrimitive()) {
                    JsonPrimitive jPrim = innerJel.getAsJsonPrimitive();
                    if (jPrim.isString()) {
                        String newVal = replacer.replace(jPrim.getAsString());
                        jArr.set(i, new JsonPrimitive(newVal));
                    }
                } else {
                    replaceJsonElement(innerJel, replacer);
                }
            }
        }

    }

    public ShellScriptReturnValue executeScriptForStepObject(Step step, String script, boolean automatic) {
        if (script == null || script.length() == 0) {
            return new ShellScriptReturnValue(-1, null, null);
        }

        DigitalDocument dd = null;
        Process po = step.getProzess();
        Prefs prefs = null;
        try {
            prefs = po.getRegelsatz().getPreferences();
            Fileformat ff = po.readMetadataFile();
            if (ff == null) {
                logger.error("Metadata file is not readable for process with ID " + step.getProcessId());
                return new ShellScriptReturnValue(-1, null, null);
            }
            dd = ff.getDigitalDocument();
        } catch (Exception e2) {
            logger.error("An exception occurred while reading the metadata file for process with ID "
                    + step.getProcessId(), e2);
        }
        VariableReplacer replacer = new VariableReplacer(dd, prefs, step.getProzess(), step);
        List<String> parameterList = replacer.replaceBashScript(script);
        //        script = replacer.replace(script);
        ShellScriptReturnValue rueckgabe = null;
        try {
            logger.info("Calling the shell: " + script + " for process with ID " + step.getProcessId());

            StringBuilder message = new StringBuilder();
            message.append("Calling the shell: ");
            message.append(script);

            Helper.addMessageToProcessLog(step.getProcessId(), LogType.DEBUG, message.toString());

            rueckgabe = ShellScript.callShell(parameterList, step.getProcessId());
            if (automatic) {
                if (rueckgabe.getReturnCode() == 0) {
                    step.setEditTypeEnum(StepEditType.AUTOMATIC);
                    step.setBearbeitungsstatusEnum(StepStatus.DONE);
                    if (step.getValidationPlugin() != null && step.getValidationPlugin().length() > 0) {
                        IValidatorPlugin ivp = (IValidatorPlugin) PluginLoader
                                .getPluginByTitle(PluginType.Validation, step.getValidationPlugin());
                        ivp.setStep(step);
                        if (!ivp.validate()) {
                            step.setBearbeitungsstatusEnum(StepStatus.OPEN);
                            StepManager.saveStep(step);
                            Helper.addMessageToProcessLog(step.getProcessId(), LogType.DEBUG,
                                    "Step '" + step.getTitel() + "' opened.");
                        } else {
                            CloseStepObjectAutomatic(step);
                        }
                    } else {
                        CloseStepObjectAutomatic(step);
                    }

                } else {
                    if (rueckgabe.getReturnCode() != 99 && rueckgabe.getReturnCode() != 98) {
                        step.setEditTypeEnum(StepEditType.AUTOMATIC);
                        step.setBearbeitungsstatusEnum(StepStatus.ERROR);
                        SendMail.getInstance().sendMailToAssignedUser(step, StepStatus.ERROR);
                        StepManager.saveStep(step);
                        Helper.addMessageToProcessLog(step.getProcessId(), LogType.ERROR,
                                "Script for '" + step.getTitel() + "' did not finish successfully. Return code: "
                                        + rueckgabe.getReturnCode() + ". The script returned: "
                                        + rueckgabe.getErrorText());
                        logger.error("Script for '" + step.getTitel()
                                + "' did not finish successfully for process with ID " + step.getProcessId()
                                + ". Return code: " + rueckgabe.getReturnCode() + ". The script returned: "
                                + rueckgabe.getErrorText());
                    }
                }
            }
        } catch (Exception e) {
            Helper.setFehlerMeldung("An exception occured while running a script", e.getMessage());
            Helper.addMessageToProcessLog(step.getProcessId(), LogType.ERROR,
                    "Exception while executing a script for '" + step.getTitel() + "': " + e.getMessage());
            logger.error("Exception occurred while running a script for process with ID " + step.getProcessId(), e);
        }
        return rueckgabe;
    }

    public boolean executeDmsExport(Step step, boolean automatic) {
        IExportPlugin dms = null;
        if (StringUtils.isNotBlank(step.getStepPlugin())) {
            try {
                dms = (IExportPlugin) PluginLoader.getPluginByTitle(PluginType.Export, step.getStepPlugin());
            } catch (Exception e) {
                logger.error(
                        "Can't load export plugin, use default plugin for process with ID " + step.getProcessId(),
                        e);
                dms = new ExportDms(ConfigurationHelper.getInstance().isAutomaticExportWithImages());
                //                dms = new AutomaticDmsExport(ConfigurationHelper.getInstance().isAutomaticExportWithImages());
            }
        }
        if (dms == null) {
            dms = new ExportDms(ConfigurationHelper.getInstance().isAutomaticExportWithImages());
            //            dms = new AutomaticDmsExport(ConfigurationHelper.getInstance().isAutomaticExportWithImages());
        }
        if (!ConfigurationHelper.getInstance().isAutomaticExportWithOcr()) {
            dms.setExportFulltext(false);
        }
        //        ProcessObject po = ProcessManager.getProcessObjectForId(step.getProcessId());
        try {
            boolean validate = dms.startExport(step.getProzess());
            if (validate) {
                Helper.addMessageToProcessLog(step.getProcessId(), LogType.DEBUG,
                        "The export for process with ID '" + step.getProcessId() + "' was done successfully.");
                CloseStepObjectAutomatic(step);
            } else {
                Helper.addMessageToProcessLog(step.getProcessId(), LogType.ERROR,
                        "The export for process with ID '" + step.getProcessId()
                                + "' was cancelled because of validation errors: " + dms.getProblems().toString());
                errorStep(step);
            }
            return validate;
        } catch (DAOException | UGHException | SwapException | IOException | InterruptedException
                | DocStructHasNoTypeException | UghHelperException | ExportFileException e) {
            logger.error("Exception occurred while trying to export process with ID " + step.getProcessId(), e);
            Helper.addMessageToProcessLog(step.getProcessId(), LogType.ERROR,
                    "An exception occurred during the export for process with ID " + step.getProcessId() + ": "
                            + e.getMessage());
            errorStep(step);
            return false;
        }
    }

    public void errorStep(Step step) {
        SendMail.getInstance().sendMailToAssignedUser(step, StepStatus.ERROR);
        step.setBearbeitungsstatusEnum(StepStatus.ERROR);
        step.setEditTypeEnum(StepEditType.AUTOMATIC);
        try {
            StepManager.saveStep(step);
        } catch (DAOException e) {
            logger.error("Error while saving a workflow step for process with ID " + step.getProcessId(), e);
        }
    }

    private void reOpenStep(Step step) {
        if (!step.getBearbeitungsstatusEnum().equals(StepStatus.OPEN)) {
            step.setBearbeitungsstatusEnum(StepStatus.OPEN);
            step.setEditTypeEnum(StepEditType.AUTOMATIC);
            step.setBearbeitungsende(new Date());
            try {
                StepManager.saveStep(step);
            } catch (DAOException e) {
                logger.error("Error while saving a workflow step for process with ID " + step.getProcessId(), e);
            }
        }

    }

    public static void extractAuthorityMetadata(Path metadataFile, Map<String, List<String>> metadataPairs) {
        XPathFactory xFactory = XPathFactory.instance();
        XPathExpression<Element> authorityMetaXpath = xFactory.compile(
                "//mets:xmlData/mods:mods/mods:extension/goobi:goobi/goobi:metadata[goobi:authorityValue]",
                Filters.element(), null, mods, mets, goobiNamespace);
        SAXBuilder builder = new SAXBuilder();
        Document doc;
        try {
            doc = builder.build(metadataFile.toString());
        } catch (JDOMException | IOException e1) {
            return;
        }
        for (Element meta : authorityMetaXpath.evaluate(doc)) {
            String name = meta.getAttributeValue("name");
            if (name == null) {
                continue;
            } else {
                String key = name + "_authority";
                List<String> values = metadataPairs.get(key);
                if (values == null) {
                    values = new ArrayList<>();
                    metadataPairs.put(key, values);
                }
                values.add(meta.getChildText("authorityValue", goobiNamespace));
            }
        }
    }

    public static void extractMetadata(Path metadataFile, Map<String, List<String>> metadataPairs) {
        SAXBuilder builder = new SAXBuilder();
        Document doc;
        try {
            doc = builder.build(metadataFile.toString());
        } catch (JDOMException | IOException e1) {
            return;
        }
        Element root = doc.getRootElement();
        try {
            Element goobi = root.getChildren("dmdSec", mets).get(0).getChild("mdWrap", mets)
                    .getChild("xmlData", mets).getChild("mods", mods).getChild("extension", mods)
                    .getChild("goobi", goobiNamespace);
            List<Element> metadataList = goobi.getChildren();
            addMetadata(metadataList, metadataPairs);
            for (Element el : root.getChildren("dmdSec", mets)) {
                if (el.getAttributeValue("ID").equals("DMDPHYS_0000")) {
                    Element phys = el.getChild("mdWrap", mets).getChild("xmlData", mets).getChild("mods", mods)
                            .getChild("extension", mods).getChild("goobi", goobiNamespace);
                    List<Element> physList = phys.getChildren();
                    addMetadata(physList, metadataPairs);
                }
            }
            // create field for "DocStruct"
            String docType = root.getChildren("structMap", mets).get(0).getChild("div", mets)
                    .getAttributeValue("TYPE");
            metadataPairs.put("DocStruct", Collections.singletonList(docType));

        } catch (Exception e) {
            logger.error(e);
            logger.error("Cannot extract metadata from " + metadataFile.toString());
        }
    }

    private static void addMetadata(List<Element> elements, Map<String, List<String>> metadataPairs) {
        for (Element goobimetadata : elements) {
            String metadataType = goobimetadata.getAttributeValue("name");
            String metadataValue = "";
            if (goobimetadata.getAttributeValue("type") != null
                    && goobimetadata.getAttributeValue("type").equals("person")) {
                Element displayName = goobimetadata.getChild("displayName", goobiNamespace);
                if (displayName != null && !displayName.getValue().equals(",")) {
                    metadataValue = displayName.getValue();
                }
            } else if (goobimetadata.getAttributeValue("type") != null
                    && goobimetadata.getAttributeValue("type").equals("group")) {
                List<Element> groupMetadataList = goobimetadata.getChildren();
                addMetadata(groupMetadataList, metadataPairs);
            } else {
                metadataValue = goobimetadata.getValue();
            }
            if (!metadataValue.equals("")) {

                if (metadataPairs.containsKey(metadataType)) {
                    List<String> oldValue = metadataPairs.get(metadataType);
                    if (!oldValue.contains(metadataValue)) {
                        oldValue.add(metadataValue);
                        metadataPairs.put(metadataType, oldValue);
                    }
                } else {
                    List<String> list = new ArrayList<>();
                    list.add(metadataValue);
                    metadataPairs.put(metadataType, list);
                }
            }

        }
        return;
    }

}