org.kitodo.production.exporter.ExportXmlLog.java Source code

Java tutorial

Introduction

Here is the source code for org.kitodo.production.exporter.ExportXmlLog.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.production.exporter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jaxen.JaxenException;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.transform.XSLTransformException;
import org.jdom.transform.XSLTransformer;
import org.kitodo.config.ConfigCore;
import org.kitodo.data.database.beans.Batch;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.beans.Property;
import org.kitodo.data.database.beans.Task;
import org.kitodo.data.database.enums.TaskStatus;
import org.kitodo.production.services.ServiceManager;

/**
 * This class provides xml logfile generation. After the generation the file
 * will be written to user home directory
 *
 * @author Robert Sehr
 * @author Steffen Hankiewicz
 *
 */
public class ExportXmlLog {

    private static final Logger logger = LogManager.getLogger(ExportXmlLog.class);
    private static final String LABEL = "label";
    private static final String NAMESPACE = "http://www.kitodo.org/logfile";
    private static final String PROPERTIES = "properties";
    private static final String PROPERTY = "property";
    private static final String PROPERTY_IDENTIFIER = "propertyIdentifier";
    private static final String VALUE = "value";

    /**
     * This method exports the production metadata as xml to a given directory.
     *
     * @param process
     *            the process to export
     * @param destination
     *            the destination to write the file
     */

    public void startExport(Process process, String destination) throws IOException {
        try (FileOutputStream ostream = new FileOutputStream(destination)) {
            startExport(process, ostream);
        }
    }

    /**
     * Start export.
     *
     * @param process
     *            Process object
     * @param dest
     *            File
     */
    public void startExport(Process process, File dest) throws IOException {
        try (FileOutputStream ostream = new FileOutputStream(dest)) {
            startExport(process, ostream);
        }
    }

    /**
     * This method exports the production metadata as xml to a given stream.
     *
     * @param process
     *            the process to export
     * @param os
     *            the OutputStream to write the contents to
     */
    private void startExport(Process process, OutputStream os) throws IOException {
        Document doc = createDocument(process, true);

        XMLOutputter outp = new XMLOutputter();
        outp.setFormat(Format.getPrettyFormat());

        outp.output(doc, os);
    }

    /**
     * This method exports the production metadata as xml to a given stream.
     *
     * @param processList
     *            list of Process' objects
     * @param outputStream
     *            object
     * @param xslt
     *            String
     */
    public void startExport(Iterable<Process> processList, OutputStream outputStream, String xslt)
            throws IOException {
        Document answer = new Document();
        Element root = new Element("processes");
        answer.setRootElement(root);
        Namespace xmlns = Namespace.getNamespace(NAMESPACE);
        root.setNamespace(xmlns);

        addNamespaceDeclaration(root);
        for (Process p : processList) {
            Document doc = createDocument(p, false);
            Element processRoot = doc.getRootElement();
            processRoot.detach();
            root.addContent(processRoot);
        }

        XMLOutputter outp = new XMLOutputter();
        outp.output(answer, outputStream);
    }

    /**
     * This method creates a new xml document with process metadata.
     *
     * @param process
     *            the process to export
     * @return a new xml document
     */
    private Document createDocument(Process process, boolean addNamespace) {
        Element processElement = new Element("process");

        processElement.setAttribute("processID", String.valueOf(process.getId()));

        Namespace xmlns = Namespace.getNamespace(NAMESPACE);
        processElement.setNamespace(xmlns);

        // namespace declaration
        if (addNamespace) {
            addNamespaceDeclaration(processElement);
        }

        // process information
        List<Element> processElements = getProcessInformation(xmlns, process);

        processElement.setContent(processElements);

        return new Document(processElement);
    }

    private void addNamespaceDeclaration(Element element) {
        Namespace xsi = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
        element.addNamespaceDeclaration(xsi);
        Attribute attSchema = new Attribute("schemaLocation", NAMESPACE + " XML-logfile.xsd", xsi);
        element.setAttribute(attSchema);
    }

    private List<Element> getProcessInformation(Namespace xmlns, Process process) {
        List<Element> processElements = new ArrayList<>();

        Element processTitle = new Element("title", xmlns);
        processTitle.setText(process.getTitle());
        processElements.add(processTitle);

        Element project = new Element("project", xmlns);
        project.setText(process.getProject().getTitle());
        processElements.add(project);

        Element date = new Element("time", xmlns);
        date.setAttribute("type", "creation date");
        date.setText(String.valueOf(process.getCreationDate()));
        processElements.add(date);

        Element ruleset = new Element("ruleset", xmlns);
        ruleset.setText(process.getRuleset().getFile());
        processElements.add(ruleset);

        Element comment = new Element("comment", xmlns);
        comment.setText(process.getWikiField());
        processElements.add(comment);

        StringBuilder batches = new StringBuilder();
        for (Batch batch : process.getBatches()) {
            if (Objects.nonNull(batch.getType())) {
                batches.append(ServiceManager.getBatchService().getTypeTranslated(batch));
                batches.append(": ");
            }
            if (batches.length() != 0) {
                batches.append(", ");
            }
            batches.append(ServiceManager.getBatchService().getLabel(batch));
        }
        if (batches.length() != 0) {
            Element batch = new Element("batch", xmlns);
            batch.setText(batches.toString());
            processElements.add(batch);
        }

        List<Element> processProperties = prepareProperties(process.getProperties(), xmlns);

        if (!processProperties.isEmpty()) {
            Element properties = new Element(PROPERTIES, xmlns);
            properties.addContent(processProperties);
            processElements.add(properties);
        }

        // task information
        Element tasks = getTasksElement(process.getTasks(), xmlns);
        processElements.add(tasks);

        // template information
        Element templates = new Element("originals", xmlns);
        List<Element> templateElements = new ArrayList<>();

        Element template = getTemplateElement(process, xmlns);
        templateElements.add(template);
        templates.addContent(templateElements);
        processElements.add(templates);

        // digital document information
        List<Element> docElements = new ArrayList<>();

        Element dd = new Element("digitalDocument", xmlns);
        dd.setAttribute("digitalDocumentID", String.valueOf(process.getId()));

        List<Element> docProperties = prepareProperties(process.getWorkpieces(), xmlns);

        if (!docProperties.isEmpty()) {
            Element properties = new Element(PROPERTIES, xmlns);
            properties.addContent(docProperties);
            dd.addContent(properties);
        }
        docElements.add(dd);

        Element digdoc = new Element("digitalDocuments", xmlns);
        digdoc.addContent(docElements);
        processElements.add(digdoc);

        // METS information
        Element metsElement = new Element("metsInformation", xmlns);
        List<Element> metadataElements = getMetadataElements(xmlns, process);
        metsElement.addContent(metadataElements);
        processElements.add(metsElement);

        return processElements;
    }

    private Element getTasksElement(List<Task> tasks, Namespace xmlns) {
        // step information
        Element steps = new Element("steps", xmlns);
        List<Element> stepElements = new ArrayList<>();
        for (Task task : tasks) {
            Element stepElement = new Element("step", xmlns);
            stepElement.setAttribute("stepID", String.valueOf(task.getId()));

            Element stepTitle = new Element("title", xmlns);
            stepTitle.setText(task.getTitle());
            stepElement.addContent(stepTitle);

            Element state = new Element("processingstatus", xmlns);
            state.setText(String.valueOf(task.getProcessingStatus().getValue()));
            stepElement.addContent(state);

            Element begin = new Element("time", xmlns);
            begin.setAttribute("type", "start time");
            begin.setText(String.valueOf(task.getProcessingBegin()));
            stepElement.addContent(begin);

            Element end = new Element("time", xmlns);
            end.setAttribute("type", "end time");
            end.setText(String.valueOf(ServiceManager.getTaskService().getProcessingEndAsFormattedString(task)));
            stepElement.addContent(end);

            if (isNonOpenStateAndHasRegularUser(task)) {
                Element user = new Element("user", xmlns);
                user.setText(ServiceManager.getUserService().getFullName(task.getProcessingUser()));
                stepElement.addContent(user);
            }
            Element editType = new Element("edittype", xmlns);
            editType.setText(task.getEditType().getTitle());
            stepElement.addContent(editType);

            stepElements.add(stepElement);
        }
        steps.addContent(stepElements);
        return steps;
    }

    private Element getTemplateElement(Process process, Namespace xmlns) {
        Element template = new Element("original", xmlns);
        template.setAttribute("originalID", String.valueOf(process.getId()));

        List<Element> templateProperties = new ArrayList<>();
        for (Property property : process.getTemplates()) {
            Element propertyElement = preparePropertyElement(xmlns, property);

            Element label = new Element(LABEL, xmlns);
            label.setText(property.getTitle());
            propertyElement.addContent(label);
            templateProperties.add(propertyElement);

            if (property.getTitle().equals("Signatur")) {
                Element secondProperty = new Element(PROPERTY, xmlns);
                secondProperty.setAttribute(PROPERTY_IDENTIFIER, property.getTitle() + "Encoded");
                if (Objects.nonNull(property.getValue())) {
                    secondProperty.setAttribute(VALUE, "vorl:" + replacer(property.getValue()));
                    Element secondLabel = new Element(LABEL, xmlns);
                    secondLabel.setText(property.getTitle());
                    secondProperty.addContent(secondLabel);
                    templateProperties.add(secondProperty);
                }
            }
        }
        if (!templateProperties.isEmpty()) {
            Element properties = new Element(PROPERTIES, xmlns);
            properties.addContent(templateProperties);
            template.addContent(properties);
        }
        return template;
    }

    private List<Element> prepareProperties(List<Property> properties, Namespace xmlns) {
        List<Element> preparedProperties = new ArrayList<>();
        for (Property property : properties) {
            Element propertyElement = preparePropertyElement(xmlns, property);

            Element label = new Element(LABEL, xmlns);
            label.setText(property.getTitle());
            propertyElement.addContent(label);
            preparedProperties.add(propertyElement);
        }
        return preparedProperties;
    }

    private Element preparePropertyElement(Namespace xmlns, Property property) {
        Element propertyElement = new Element(PROPERTY, xmlns);
        propertyElement.setAttribute(PROPERTY_IDENTIFIER, property.getTitle());
        if (Objects.nonNull(property.getValue())) {
            propertyElement.setAttribute(VALUE, replacer(property.getValue()));
        } else {
            propertyElement.setAttribute(VALUE, "");
        }
        return propertyElement;
    }

    private void prepareMetadataElements(List<Element> metadataElements, Map<String, String> fields,
            Document document, HashMap<String, Namespace> namespaces, Namespace xmlns) throws JaxenException {
        for (Map.Entry<String, String> entry : fields.entrySet()) {
            String key = entry.getKey();
            List<Element> metsValues = getMetsValues(entry.getValue(), document, namespaces);
            for (Element element : metsValues) {
                Element ele = new Element(PROPERTY, xmlns);
                ele.setAttribute("name", key);
                ele.addContent(element.getTextTrim());
                metadataElements.add(ele);
            }
        }
    }

    private List<Element> getMetadataElements(Namespace xmlns, Process process) {
        List<Element> metadataElements = new ArrayList<>();
        try {
            URI metadataFilePath = ServiceManager.getFileService().getMetadataFilePath(process);
            Document metsDoc = new SAXBuilder()
                    .build(ServiceManager.getFileService().getFile(metadataFilePath).toString());
            Document anchorDoc = null;
            URI anchorFileName = URI.create(ServiceManager.getFileService().getMetadataFilePath(process).toString()
                    .replace("meta.xml", "meta_anchor.xml"));
            if (ServiceManager.getFileService().fileExist(anchorFileName)
                    && ServiceManager.getFileService().canRead(anchorFileName)) {
                anchorDoc = new SAXBuilder()
                        .build(ServiceManager.getFileService().getFile(metadataFilePath).toString());
            }
            HashMap<String, Namespace> namespaces = new HashMap<>();

            HashMap<String, String> names = getNamespacesFromConfig();
            for (Map.Entry<String, String> entry : names.entrySet()) {
                String key = entry.getKey();
                namespaces.put(key, Namespace.getNamespace(key, entry.getValue()));
            }

            HashMap<String, String> fields = getMetsFieldsFromConfig(false);
            prepareMetadataElements(metadataElements, fields, metsDoc, namespaces, xmlns);

            if (Objects.nonNull(anchorDoc)) {
                fields = getMetsFieldsFromConfig(true);
                prepareMetadataElements(metadataElements, fields, anchorDoc, namespaces, xmlns);
            }
        } catch (IOException | JDOMException | JaxenException e) {
            logger.error(e.getMessage(), e);
        }
        return metadataElements;
    }

    /**
     * Get METS values.
     *
     * @param expr
     *            String
     * @param element
     *            Object
     * @param namespaces
     *            HashMap
     * @return list of elements
     */
    @SuppressWarnings("unchecked")
    private List<Element> getMetsValues(String expr, Object element, Map<String, Namespace> namespaces)
            throws JaxenException {
        JDOMXPath xpath = new JDOMXPath(expr.trim().replace("\n", ""));
        // Add all namespaces
        for (Map.Entry<String, Namespace> entry : namespaces.entrySet()) {
            xpath.addNamespace(entry.getKey(), entry.getValue().getURI());
        }
        return xpath.selectNodes(element);
    }

    /**
     * This method transforms the xml log using a xslt file and opens a new window
     * with the output file.
     *
     * @param out
     *            ServletOutputStream
     * @param doc
     *            the xml document to transform
     * @param filename
     *            the filename of the xslt
     */

    private void xmlTransformation(OutputStream out, Document doc, String filename)
            throws XSLTransformException, IOException {
        Document docTrans;
        if (Objects.nonNull(filename) && filename.isEmpty()) {
            XSLTransformer transformer;
            transformer = new XSLTransformer(filename);
            docTrans = transformer.transform(doc);
        } else {
            docTrans = doc;
        }
        Format format = Format.getPrettyFormat();
        format.setEncoding(StandardCharsets.UTF_8.name());
        XMLOutputter xmlOut = new XMLOutputter(format);

        xmlOut.output(docTrans, out);
    }

    public void startTransformation(OutputStream out, Process p, String filename)
            throws XSLTransformException, IOException {
        startTransformation(p, out, filename);
    }

    private void startTransformation(Process p, OutputStream out, String filename)
            throws XSLTransformException, IOException {
        Document doc = createDocument(p, true);
        xmlTransformation(out, doc, filename);
    }

    private String replacer(String in) {
        in = in.replace("", "?");
        in = in.replace("^", "?");
        in = in.replace("|", "?");
        in = in.replace(">", "?");
        in = in.replace("<", "?");
        return in;
    }

    private HashMap<String, String> getMetsFieldsFromConfig(boolean useAnchor) {
        String xmlpath = "mets." + PROPERTY;
        if (useAnchor) {
            xmlpath = "anchor." + PROPERTY;
        }

        HashMap<String, String> fields = new HashMap<>();
        try {
            File file = new File(ConfigCore.getKitodoConfigDirectory() + "kitodo_exportXml.xml");
            if (file.exists() && file.canRead()) {
                XMLConfiguration config = new XMLConfiguration(file);
                config.setListDelimiter('&');
                config.setReloadingStrategy(new FileChangedReloadingStrategy());

                int count = config.getMaxIndex(xmlpath);
                for (int i = 0; i <= count; i++) {
                    String name = config.getString(xmlpath + "(" + i + ")[@name]");
                    String value = config.getString(xmlpath + "(" + i + ")[@value]");
                    fields.put(name, value);
                }
            }
        } catch (ConfigurationException | RuntimeException e) {
            logger.debug(e.getMessage(), e);
            fields = new HashMap<>();
        }
        return fields;
    }

    private HashMap<String, String> getNamespacesFromConfig() {
        HashMap<String, String> nss = new HashMap<>();
        try {
            File file = new File(ConfigCore.getKitodoConfigDirectory() + "kitodo_exportXml.xml");
            if (file.exists() && file.canRead()) {
                XMLConfiguration config = new XMLConfiguration(file);
                config.setListDelimiter('&');
                config.setReloadingStrategy(new FileChangedReloadingStrategy());

                int count = config.getMaxIndex("namespace");
                for (int i = 0; i <= count; i++) {
                    String name = config.getString("namespace(" + i + ")[@name]");
                    String value = config.getString("namespace(" + i + ")[@value]");
                    nss.put(name, value);
                }
            }
        } catch (ConfigurationException | RuntimeException e) {
            logger.debug(e.getMessage(), e);
            nss = new HashMap<>();
        }
        return nss;

    }

    /**
     * Check task for non-open step state and step has a regular user assigned.
     *
     * @param task
     *            task to check
     * @return boolean
     */
    private boolean isNonOpenStateAndHasRegularUser(Task task) {
        return !TaskStatus.OPEN.equals(task.getProcessingStatus()) && Objects.nonNull(task.getProcessingUser())
                && task.getProcessingUser().getId() != 0
                && Objects.nonNull(ServiceManager.getUserService().getFullName(task.getProcessingUser()));
    }

}