org.digidoc4j.impl.bdoc.asic.AsicContainerParser.java Source code

Java tutorial

Introduction

Here is the source code for org.digidoc4j.impl.bdoc.asic.AsicContainerParser.java

Source

/* DigiDoc4J library
*
* This software is released under either the GNU Library General Public
* License (see LICENSE.LGPL).
*
* Note that the only valid version of the LGPL license as far as this
* project is concerned is the original GNU Library General Public License
* Version 2.1, February 1999
*/

package org.digidoc4j.impl.bdoc.asic;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.lang.StringUtils;
import org.digidoc4j.Configuration;
import org.digidoc4j.DataFile;
import org.digidoc4j.exceptions.DuplicateDataFileException;
import org.digidoc4j.exceptions.TechnicalException;
import org.digidoc4j.exceptions.UnsupportedFormatException;
import org.digidoc4j.impl.StreamDocument;
import org.digidoc4j.impl.bdoc.manifest.ManifestEntry;
import org.digidoc4j.impl.bdoc.manifest.ManifestParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.InMemoryDocument;
import eu.europa.esig.dss.MimeType;

public abstract class AsicContainerParser {

    private final static Logger logger = LoggerFactory.getLogger(AsicContainerParser.class);
    //Matches META-INF/*signatures*.xml where the last * is a number
    private static final String SIGNATURES_FILE_REGEX = "META-INF/(.*)signatures(.*).xml";
    private static final Pattern SIGNATURE_FILE_ENDING_PATTERN = Pattern.compile("(\\d+).xml");
    public static final String MANIFEST = "META-INF/manifest.xml";
    private AsicParseResult parseResult = new AsicParseResult();
    private List<DSSDocument> signatures = new ArrayList<>();
    private LinkedHashMap<String, DataFile> dataFiles = new LinkedHashMap<>();
    private List<DSSDocument> detachedContents = new ArrayList<>();
    private Integer currentSignatureFileIndex;
    private String mimeType;
    private String zipFileComment;
    private List<AsicEntry> asicEntries = new ArrayList<>();
    private Map<String, ManifestEntry> manifestFileItems = Collections.emptyMap();
    private ManifestParser manifestParser;
    private boolean storeDataFilesOnlyInMemory;
    private long maxDataFileCachedInBytes;

    protected AsicContainerParser(Configuration configuration) {
        storeDataFilesOnlyInMemory = configuration.storeDataFilesOnlyInMemory();
        maxDataFileCachedInBytes = configuration.getMaxDataFileCachedInBytes();
    }

    public AsicParseResult read() {
        parseContainer();
        validateParseResult();
        populateParseResult();
        return parseResult;
    }

    protected abstract void parseContainer();

    protected abstract void extractManifest(ZipEntry entry);

    protected abstract InputStream getZipEntryInputStream(ZipEntry entry);

    protected void parseManifestEntry(DSSDocument manifestFile) {
        logger.debug("Parsing manifest");
        manifestParser = new ManifestParser(manifestFile);
        manifestFileItems = manifestParser.getManifestFileItems();
    }

    protected void parseEntry(ZipEntry entry) {
        String entryName = entry.getName();
        logger.debug("Paring zip entry " + entryName + " with comment: " + entry.getComment());
        if (isMimeType(entryName)) {
            extractMimeType(entry);
        } else if (isManifest(entryName)) {
            extractManifest(entry);
        } else if (isSignaturesFile(entryName)) {
            determineCurrentSignatureFileIndex(entryName);
            extractSignature(entry);
        } else if (isDataFile(entryName)) {
            extractDataFile(entry);
        } else {
            extractAsicEntry(entry);
        }
    }

    private void extractMimeType(ZipEntry entry) {
        try {
            InputStream zipFileInputStream = getZipEntryInputStream(entry);
            BOMInputStream bomInputStream = new BOMInputStream(zipFileInputStream);
            DSSDocument document = new InMemoryDocument(bomInputStream);
            mimeType = StringUtils.trim(IOUtils.toString(getDocumentBytes(document), "UTF-8"));
            extractAsicEntry(entry, document);
        } catch (IOException e) {
            logger.error("Error parsing container mime type: " + e.getMessage());
            throw new TechnicalException("Error parsing container mime type: " + e.getMessage(), e);
        }
    }

    private void extractSignature(ZipEntry entry) {
        logger.debug("Extracting signature");
        InputStream zipFileInputStream = getZipEntryInputStream(entry);
        String fileName = entry.getName();
        InMemoryDocument document = new InMemoryDocument(zipFileInputStream, fileName);
        signatures.add(document);
        extractSignatureAsicEntry(entry, document);
    }

    private void extractDataFile(ZipEntry entry) {
        logger.debug("Extracting data file");
        String fileName = entry.getName();
        validateDataFile(fileName);
        DSSDocument document = extractStreamDocument(entry);
        DataFile dataFile = new AsicDataFile(document);
        dataFiles.put(fileName, dataFile);
        detachedContents.add(document);
        extractAsicEntry(entry, document);
    }

    private DSSDocument extractStreamDocument(ZipEntry entry) {
        logger.debug("Zip entry size is " + entry.getSize() + " bytes");
        InputStream zipFileInputStream = getZipEntryInputStream(entry);
        String fileName = entry.getName();
        String mimeType = getDataFileMimeType(fileName);
        MimeType mimeTypeCode = MimeType.fromMimeTypeString(mimeType);
        DSSDocument document;
        if (storeDataFilesOnlyInMemory || entry.getSize() <= maxDataFileCachedInBytes) {
            document = new InMemoryDocument(zipFileInputStream, fileName, mimeTypeCode);
        } else {
            document = new StreamDocument(zipFileInputStream, fileName, mimeTypeCode);
        }
        return document;
    }

    protected AsicEntry extractAsicEntry(ZipEntry entry) {
        logger.debug("Extracting asic entry");
        DSSDocument document = extractStreamDocument(entry);
        return extractAsicEntry(entry, document);
    }

    private AsicEntry extractAsicEntry(ZipEntry zipEntry, DSSDocument document) {
        AsicEntry asicEntry = new AsicEntry(zipEntry);
        asicEntry.setContent(document);
        asicEntries.add(asicEntry);
        return asicEntry;
    }

    private void extractSignatureAsicEntry(ZipEntry entry, DSSDocument document) {
        AsicEntry asicEntry = extractAsicEntry(entry, document);
        asicEntry.setSignature(true);
    }

    protected String getDataFileMimeType(String fileName) {
        if (manifestFileItems.containsKey(fileName)) {
            ManifestEntry manifestEntry = manifestFileItems.get(fileName);
            return manifestEntry.getMimeType();
        } else {
            MimeType mimeType = MimeType.fromFileName(fileName);
            return mimeType.getMimeTypeString();
        }
    }

    private void validateParseResult() {
        if (!StringUtils.equalsIgnoreCase(MimeType.ASICE.getMimeTypeString(), mimeType)) {
            logger.error(
                    "Container mime type is not " + MimeType.ASICE.getMimeTypeString() + " but is " + mimeType);
            throw new UnsupportedFormatException(
                    "Container mime type is not " + MimeType.ASICE.getMimeTypeString() + " but is " + mimeType);
        }
    }

    private void validateDataFile(String fileName) {
        if (dataFiles.containsKey(fileName)) {
            logger.error("Container contains duplicate data file: " + fileName);
            throw new DuplicateDataFileException("Container contains duplicate data file: " + fileName);
        }
    }

    private void populateParseResult() {
        Collection<DataFile> files = dataFiles.values();
        parseResult.setDataFiles(new ArrayList<>(files));
        parseResult.setSignatures(signatures);
        parseResult.setCurrentUsedSignatureFileIndex(currentSignatureFileIndex);
        parseResult.setDetachedContents(detachedContents);
        parseResult.setManifestParser(manifestParser);
        parseResult.setZipFileComment(zipFileComment);
        parseResult.setAsicEntries(asicEntries);
    }

    private boolean isMimeType(String entryName) {
        return StringUtils.equalsIgnoreCase("mimetype", entryName);
    }

    private boolean isDataFile(String entryName) {
        return !entryName.startsWith("META-INF/") && !isMimeType(entryName);
    }

    private boolean isManifest(String entryName) {
        return StringUtils.equalsIgnoreCase(MANIFEST, entryName);
    }

    private boolean isSignaturesFile(String entryName) {
        return entryName.matches(SIGNATURES_FILE_REGEX);
    }

    private void determineCurrentSignatureFileIndex(String entryName) {
        Matcher fileEndingMatcher = SIGNATURE_FILE_ENDING_PATTERN.matcher(entryName);
        boolean fileEndingFound = fileEndingMatcher.find();
        if (fileEndingFound) {
            String fileEnding = fileEndingMatcher.group();
            String indexNumber = fileEnding.replace(".xml", "");
            int fileIndex = Integer.parseInt(indexNumber);
            if (currentSignatureFileIndex == null || currentSignatureFileIndex <= fileIndex) {
                currentSignatureFileIndex = fileIndex;
            }
        }
    }

    private byte[] getDocumentBytes(DSSDocument document) {
        try {
            return IOUtils.toByteArray(document.openStream());
        } catch (IOException e) {
            logger.error("Error getting document content: " + e.getMessage());
            throw new TechnicalException("Error getting document content: " + e.getMessage(), e);
        }
    }

    void setZipFileComment(String zipFileComment) {
        this.zipFileComment = zipFileComment;
    }

    LinkedHashMap<String, DataFile> getDataFiles() {
        return dataFiles;
    }
}