info.magnolia.ui.framework.command.ImportZipCommand.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.ui.framework.command.ImportZipCommand.java

Source

/**
 * This file Copyright (c) 2013-2015 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.ui.framework.command;

import info.magnolia.cms.beans.config.MIMEMapping;
import info.magnolia.cms.core.Path;
import info.magnolia.commands.impl.BaseRepositoryCommand;
import info.magnolia.context.Context;
import info.magnolia.i18nsystem.SimpleTranslator;
import info.magnolia.jcr.util.NodeTypes;
import info.magnolia.jcr.util.NodeUtil;
import info.magnolia.ui.form.field.upload.UploadReceiver;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.RepositoryException;

import org.apache.commons.collections.EnumerationUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.vaadin.easyuploads.UploadField;

/**
 * Imports ZIP archive contents into JCR repository.
 */
public class ImportZipCommand extends BaseRepositoryCommand {

    public static final String STREAM_PROPERTY = "inputStream";

    public static final String ENCODING_PROPERTY = "encoding";

    public static final String ZIP_TMP_FILE_PREFIX = "zipupload";

    public static final String ZIP_TMP_FILE_SUFFIX = ".zip";

    public static final String DEFAULT_MIME_TYPE = "application/octet-stream";

    public static final String BACKSLASH_DUMMY = "________backslash________";

    private SimpleTranslator translator;

    private InputStream inputStream;

    protected Context context;

    private String encoding;

    @Inject
    public ImportZipCommand(SimpleTranslator translator) {
        this.translator = translator;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        this.context = context;
        File tmpFile = null;
        FileOutputStream tmpStream = null;
        try {
            tmpFile = File.createTempFile(ZIP_TMP_FILE_PREFIX, ZIP_TMP_FILE_SUFFIX);
            tmpStream = new FileOutputStream(tmpFile);
            IOUtils.copy(inputStream, tmpStream);
        } catch (IOException e) {
            log.error("Failed to dump zip file to temp file: ", e);
            throw e;
        } finally {
            IOUtils.closeQuietly(tmpStream);
            IOUtils.closeQuietly(inputStream);
        }

        if (isValid(tmpFile)) {
            ZipFile zip = new ZipFile(tmpFile, getEncoding());
            // We use the ant-1.6.5 zip package to workaround encoding issues of the sun implementation (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4244499)
            // For some reason, entries are not in the opposite order as how they appear in most tools - reversing here.
            // Note that java.util.zip does not show this behaviour, and ant-1.7.1 seems to enumerate entries in alphabetical or random order.
            // Another alternative might be http://truezip.dev.java.net
            final List zipEntries = EnumerationUtils.toList(zip.getEntries());
            Collections.sort(zipEntries, new Comparator() {
                @Override
                public int compare(Object first, Object second) {
                    ZipArchiveEntry firstEntry = ((ZipArchiveEntry) first);
                    ZipArchiveEntry secondEntry = ((ZipArchiveEntry) second);
                    if (firstEntry.isDirectory() != secondEntry.isDirectory()) {
                        // order folders first
                        return Boolean.compare(secondEntry.isDirectory(), firstEntry.isDirectory());
                    }
                    // order alphabetically
                    return firstEntry.getName().compareTo(secondEntry.getName());
                }
            });

            final Iterator it = zipEntries.iterator();
            while (it.hasNext()) {
                ZipArchiveEntry entry = (ZipArchiveEntry) it.next();
                processEntry(zip, entry);
            }
            context.getJCRSession(getRepository()).save();
        }
        return false;
    }

    private void processEntry(ZipFile zip, ZipArchiveEntry entry) throws IOException, RepositoryException {
        if (entry.getName().startsWith("__MACOSX")) {
            return;
        } else if (entry.getName().endsWith(".DS_Store")) {
            return;
        }

        if (entry.isDirectory()) {
            ensureFolder(entry);
        } else {
            handleFileEntry(zip, entry);
        }

    }

    protected void handleFileEntry(ZipFile zip, ZipArchiveEntry entry) throws IOException, RepositoryException {
        String fileName = entry.getName();
        if (StringUtils.contains(fileName, "/")) {
            fileName = StringUtils.substringAfterLast(fileName, "/");
        }

        String extension = StringUtils.substringAfterLast(fileName, ".");
        InputStream stream = zip.getInputStream(entry);
        FileOutputStream os = null;
        try {
            UploadReceiver receiver = createReceiver();

            String folderPath = extractEntryPath(entry);
            if (folderPath.startsWith("/")) {
                folderPath = folderPath.substring(1);
            }
            Node folder = getJCRNode(context);
            if (StringUtils.isNotBlank(folderPath)) {
                if (folder.hasNode(folderPath)) {
                    folder = folder.getNode(folderPath);
                } else {
                    folder = NodeUtil.createPath(folder, folderPath, NodeTypes.Folder.NAME, true);
                }
            }
            receiver.setFieldType(UploadField.FieldType.BYTE_ARRAY);
            receiver.receiveUpload(fileName,
                    StringUtils.defaultIfEmpty(MIMEMapping.getMIMEType(extension), DEFAULT_MIME_TYPE));
            receiver.setValue(IOUtils.toByteArray(stream));

            doHandleEntryFromReceiver(folder, receiver);
        } catch (IOException e) {
            throw e;
        } finally {
            IOUtils.closeQuietly(stream);
            IOUtils.closeQuietly(os);
        }
    }

    protected UploadReceiver createReceiver() {
        return new UploadReceiver(Path.getTempDirectory(), translator);
    }

    /**
     * Actually produce an asset or a node based on the entry data aggregated into an
     * {@link UploadReceiver} object.
     * @param folder parent folder node for a new entry.
     * @param receiver wraps the entry file and its basic meta-information.
     * @throws RepositoryException
     */
    protected void doHandleEntryFromReceiver(Node folder, UploadReceiver receiver) throws RepositoryException {
    }

    private void ensureFolder(ZipArchiveEntry entry) throws RepositoryException {
        String path = extractEntryPath(entry);
        NodeUtil.createPath(getJCRNode(context), path, NodeTypes.Folder.NAME, true);
    }

    private String extractEntryPath(ZipArchiveEntry entry) {
        String entryName = entry.getName();
        String path = (StringUtils.contains(entryName, "/")) ? StringUtils.substringBeforeLast(entryName, "/")
                : "/";

        if (!path.startsWith("/")) {
            path = "/" + path;
        }

        // make proper name, the path was already created
        path = StringUtils.replace(path, "/", BACKSLASH_DUMMY);
        path = Path.getValidatedLabel(path);
        path = StringUtils.replace(path, BACKSLASH_DUMMY, "/");
        return path;
    }

    private boolean isValid(File tmpFile) {
        return tmpFile != null;
    }

    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }
}