Java tutorial
/******************************************************************************* * 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; } }