org.opentestsystem.authoring.testitembank.service.impl.ApipZipInputFileExtractorService.java Source code

Java tutorial

Introduction

Here is the source code for org.opentestsystem.authoring.testitembank.service.impl.ApipZipInputFileExtractorService.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System 
 * Copyright (c) 2014 American Institutes for Research
 *   
 * Distributed under the AIR Open Source License, Version 1.0 
 * See accompanying file AIR-License-1_0.txt or at
 * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf
 ******************************************************************************/
package org.opentestsystem.authoring.testitembank.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.utils.IOUtils;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipItemContent;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipItemMetadata;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipManifest;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipManifestDependency;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipManifestResource;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ApipZipFileContent;
import org.opentestsystem.authoring.testitembank.apipzip.domain.ResourceTypePrefix;
import org.opentestsystem.authoring.testitembank.exception.TestItemBankException;
import org.opentestsystem.authoring.testitembank.service.ZipInputFileExtractorService;
import org.opentestsystem.authoring.testitembank.service.ZipXMLService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class ApipZipInputFileExtractorService implements ZipInputFileExtractorService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApipZipInputFileExtractorService.class);

    private static final String IMS_MANIFEST_NAME = "imsmanifest.xml";
    private static final String META_DATA_FILE_PREFIX = "metadata";

    @Autowired
    private ZipXMLService zipXMLService;

    @Override
    public List<ApipItemContent> createItems(final File apipZipfile, final String gridfsId, final String tenantId,
            String itemBank) {
        final ApipZipFileContent zipContent = extractData(apipZipfile);
        final List<ApipItemContent> ret = new ArrayList<ApipItemContent>();
        final Map<String, ApipItemMetadata> itemMetadataMap = zipContent.getItemMetadata();

        for (final ApipManifestResource resource : zipContent.getManifest().getResources()) {
            // If an item, handle it
            if (isItemResource(resource.getResourceType())) {
                final ApipItemContent itemContent = zipContent.getItemContentMap().get(resource.getIdentifier());
                final ApipItemMetadata metadata = itemMetadataMap.get(resource.getIdentifier());
                if (metadata == null) {
                    itemContent.addError("item.invalid.zip.missingMetadata",
                            new String[] { resource.getIdentifier(), META_DATA_FILE_PREFIX });
                }
                itemContent.setItem(zipXMLService.buildItem(resource, metadata, gridfsId, tenantId, itemBank));
                ret.add(itemContent);
            }
        }
        return ret;
    }

    @Override
    public final ApipZipFileContent extractData(final File zippedFile) {
        final ApipZipFileContent zipContent = new ApipZipFileContent();
        try {
            final ZipFile zip = new ZipFile(zippedFile);
            final HashMap<String, ZipArchiveEntry> mappedEntries = getMappedZipFileEntries(zip);
            final ApipManifest manifest = extractManifest(zip, mappedEntries);
            zipContent.setManifest(manifest);
            zipContent.setItemMetadata(getApipItemMetadata(zip, mappedEntries, manifest));
            zipContent.setItemContentMap(getItemZipMap(manifest, zip, mappedEntries));
            zip.close();
        } catch (final IOException e) {
            throw new TestItemBankException("zip.io.error", e);
        }

        return zipContent;
    }

    private boolean isItemResource(final String type) {
        return ResourceTypePrefix.apipItem.isPrefixFor(type) || ResourceTypePrefix.stimuli.isPrefixFor(type);
    }

    private Map<String, ApipItemContent> getItemZipMap(final ApipManifest manifest, final ZipFile originalZip,
            final HashMap<String, ZipArchiveEntry> zipEntryMap) {
        final Map<String, ApipManifestResource> resourceMap = manifest.getResourceMap();
        final Map<String, ApipItemContent> itemContentMap = new HashMap<String, ApipItemContent>();
        final String apipRoot = findApipRootDirectoy(zipEntryMap);
        for (final ApipManifestResource resource : manifest.getResources()) {
            if (isItemResource(resource.getResourceType())) {
                final ApipItemContent content = new ApipItemContent();
                ZipOutputStream zipOut = null;
                ByteArrayOutputStream byteStream = null;
                byte[] bytes = null;
                try {
                    byteStream = new ByteArrayOutputStream();
                    zipOut = new ZipOutputStream(byteStream);
                    zipOut.setLevel(ZipOutputStream.STORED);
                    final ApipManifest itemManifest = new ApipManifest();
                    itemManifest.setIdentifier(manifest.getIdentifier() + "_" + resource.getIdentifier());
                    itemManifest.setSchema(manifest.getSchema());
                    itemManifest.setSchemaVersion(manifest.getSchemaVersion());

                    final List<ApipManifestResource> requiredResources = new ArrayList<ApipManifestResource>();
                    requiredResources.add(resource);

                    final ZipArchiveEntry itemEntry = zipEntryMap
                            .get(apipRoot + resource.getFileReference().getHref());
                    copyZipEntry(itemEntry, originalZip, zipOut);
                    for (final ApipManifestDependency dependency : resource.getDependencies()) {
                        final ApipManifestResource dependencyResource = resourceMap.get(dependency.getIdentifier());
                        if (dependencyResource != null) {
                            final ZipArchiveEntry dependencyEntry = zipEntryMap
                                    .get(apipRoot + dependencyResource.getFileReference().getHref());
                            if (dependencyEntry == null) {
                                if (ResourceTypePrefix.apipMetadata
                                        .isPrefixFor(dependencyResource.getResourceType())
                                        || ResourceTypePrefix.apipItem
                                                .isPrefixFor(dependencyResource.getResourceType())
                                        || ResourceTypePrefix.stimuli
                                                .isPrefixFor(dependencyResource.getResourceType())) {
                                    content.addError("item.invalid.zip.missingResource",
                                            new String[] { resource.getIdentifier(), dependency.getIdentifier() });
                                } else
                                    LOGGER.warn(String.format("Zip missing resource: %s, %s ",
                                            resource.getIdentifier(), dependency.getIdentifier()));

                            } else {
                                requiredResources.add(dependencyResource);
                                copyZipEntry(dependencyEntry, originalZip, zipOut);
                            }
                        }
                    }
                    itemManifest.setResources(requiredResources);

                    final ZipEntry entry = new ZipEntry(apipRoot + IMS_MANIFEST_NAME);
                    zipOut.putNextEntry(entry);

                    zipXMLService.writeToManifestToOutputStream(itemManifest, zipOut);
                    zipOut.closeEntry();

                    zipOut.flush();
                    zipOut.close();
                    bytes = byteStream.toByteArray();
                    content.setItemZip(bytes);
                } catch (final Exception e) {
                    LOGGER.error("creating item zip for " + resource.getIdentifier(), e);
                } finally {
                    try {
                        if (zipOut != null) {
                            zipOut.flush();
                            zipOut.close();
                        }
                        if (byteStream != null) {
                            byteStream.flush();
                            byteStream.close();
                        }
                    } catch (final IOException e) {
                        LOGGER.error("error closing zip.", e);
                    }
                }
                itemContentMap.put(resource.getIdentifier(), content);
            }
        }
        return itemContentMap;

    }

    private void copyZipEntry(final ZipArchiveEntry entryToCopy, final ZipFile originalZip,
            final ZipOutputStream zipOut) throws Exception {
        if (entryToCopy == null) {
            throw new Exception("no entry found");
        }
        final InputStream in = originalZip.getInputStream(entryToCopy);
        final ZipEntry entry = new ZipEntry(entryToCopy.getName());
        zipOut.putNextEntry(entry);
        IOUtils.copy(in, zipOut);
        zipOut.closeEntry();
        in.close();
    }

    private Map<String, ApipItemMetadata> getApipItemMetadata(final ZipFile zip,
            final HashMap<String, ZipArchiveEntry> mappedEntries, final ApipManifest manifest) {
        final Map<String, ApipItemMetadata> itemMetadataMap = new HashMap<String, ApipItemMetadata>();
        final String apipRoot = findApipRootDirectoy(mappedEntries);
        final Map<String, ApipManifestResource> resourceMap = manifest.getResourceMap();

        for (final ApipManifestResource resource : manifest.getResources()) {
            if (isItemResource(resource.getResourceType())) {
                LOGGER.warn("import metadata for." + resource.getIdentifier());
                for (final ApipManifestDependency itemDependency : resource.getDependencies()) {
                    final ApipManifestResource metadataResource = resourceMap.get(itemDependency.getIdentifier());
                    if (metadataResource != null
                            && ResourceTypePrefix.apipMetadata.isPrefixFor(metadataResource.getResourceType())) {
                        try {
                            final ZipArchiveEntry manifestZipEntry = mappedEntries
                                    .get(apipRoot + metadataResource.getFileReference().getHref());
                            if (manifestZipEntry == null) {
                                itemMetadataMap.put(resource.getIdentifier(), null);
                            } else {
                                final InputStream inputstream = zip.getInputStream(manifestZipEntry);
                                final ApipItemMetadata metadata = zipXMLService.extractMetadataFromXML(inputstream);
                                itemMetadataMap.put(resource.getIdentifier(), metadata);
                            }
                        } catch (final Exception e) {
                            // unable to get metadata insert null in map
                            itemMetadataMap.put(resource.getIdentifier(), null);
                        }
                    }
                }

            }
        }
        return itemMetadataMap;
    }

    private ApipManifest extractManifest(final ZipFile zip, final HashMap<String, ZipArchiveEntry> mappedEntries) {
        ApipManifest returnValue = null;
        try {
            final String apipRoot = findApipRootDirectoy(mappedEntries);
            final ZipArchiveEntry manifestZipEntry = mappedEntries.get(apipRoot + IMS_MANIFEST_NAME);
            final InputStream is = zip.getInputStream(manifestZipEntry);
            returnValue = zipXMLService.extractManifestFromXML(is);
        } catch (final Exception e) {
            throw new TestItemBankException("apip.zip.extractor.failure.mainfest", e);
        }

        return returnValue;
    }

    private HashMap<String, ZipArchiveEntry> getMappedZipFileEntries(final ZipFile zippedFile) {
        final HashMap<String, ZipArchiveEntry> mappedEntries = new HashMap<String, ZipArchiveEntry>();
        final Enumeration<ZipArchiveEntry> entries = zippedFile.getEntries();
        while (entries.hasMoreElements()) {
            final ZipArchiveEntry e = entries.nextElement();
            mappedEntries.put(e.getName(), e);
        }
        return mappedEntries;
    }

    private String findApipRootDirectoy(final HashMap<String, ZipArchiveEntry> zipEntries) {
        String root = "";
        for (final String key : zipEntries.keySet()) {
            if (key.endsWith(IMS_MANIFEST_NAME)) {
                root = key.replace(IMS_MANIFEST_NAME, "");
                break;
            }
        }
        return root;
    }

}