org.structr.websocket.command.UnarchiveCommand.java Source code

Java tutorial

Introduction

Here is the source code for org.structr.websocket.command.UnarchiveCommand.java

Source

/**
 * Copyright (C) 2010-2016 Structr GmbH
 *
 * This file is part of Structr <http://structr.org>.
 *
 * Structr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Structr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Structr.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.structr.websocket.command;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.lang3.StringUtils;
import org.structr.common.PathHelper;
import org.structr.common.SecurityContext;
import org.structr.common.error.FrameworkException;
import org.structr.core.app.App;
import org.structr.core.app.StructrApp;
import org.structr.core.graph.Tx;
import org.structr.dynamic.File;
import org.structr.web.common.FileHelper;
import org.structr.web.common.ImageHelper;
import org.structr.web.entity.AbstractFile;
import org.structr.web.entity.Folder;
import org.structr.web.entity.Image;
import org.structr.websocket.StructrWebSocket;
import org.structr.websocket.message.MessageBuilder;
import org.structr.websocket.message.WebSocketMessage;

//~--- classes ----------------------------------------------------------------
/**
 * Websocket command for un-archiving archive files.
 *
 *
 */
public class UnarchiveCommand extends AbstractCommand {

    private static final Logger logger = Logger.getLogger(UnarchiveCommand.class.getName());

    static {

        StructrWebSocket.addCommand(UnarchiveCommand.class);

    }

    //~--- methods --------------------------------------------------------
    @Override
    public void processMessage(WebSocketMessage webSocketData) {

        final Set<String> supportedByArchiveStreamFactory = new HashSet<>(
                Arrays.asList(new String[] { ArchiveStreamFactory.AR, ArchiveStreamFactory.ARJ,
                        ArchiveStreamFactory.CPIO, ArchiveStreamFactory.DUMP, ArchiveStreamFactory.JAR,
                        ArchiveStreamFactory.TAR, ArchiveStreamFactory.ZIP }));

        final SecurityContext securityContext = getWebSocket().getSecurityContext();
        final App app = StructrApp.getInstance(securityContext);

        try {

            final String id = (String) webSocketData.getId();
            final File file;

            try (final Tx tx = app.tx()) {

                file = app.get(File.class, id);

                if (file == null) {
                    getWebSocket().send(
                            MessageBuilder.status().code(400).message("File not found: ".concat(id)).build(), true);
                    return;
                }

                final String fileExtension = StringUtils.substringAfterLast(file.getName(), ".");
                if (!supportedByArchiveStreamFactory.contains(fileExtension)) {

                    getWebSocket().send(MessageBuilder.status().code(400)
                            .message("Unsupported archive format: ".concat(fileExtension)).build(), true);
                    return;
                }

                tx.success();
            }

            // no transaction here since this is a bulk command
            unarchive(securityContext, file);

        } catch (Throwable t) {

            t.printStackTrace();

            String msg = t.toString();

            try (final Tx tx = app.tx()) {

                // return error message
                getWebSocket().send(
                        MessageBuilder.status().code(400)
                                .message("Could not unarchive file: ".concat((msg != null) ? msg : "")).build(),
                        true);

                tx.success();

            } catch (FrameworkException ignore) {
            }

        }
    }

    @Override
    public boolean requiresEnclosingTransaction() {
        return false;
    }

    /**
     * Return the folder node which corresponds with the parent path.
     *
     * @param path
     */
    private Folder createOrGetParentFolder(final SecurityContext securityContext, final String path)
            throws FrameworkException {

        final String[] parts = PathHelper.getParts(path);

        // string is empty or a single file in root dir
        if (parts == null || parts.length == 1) {
            return null;
        }

        // Find root folder
        final App app = StructrApp.getInstance(securityContext);
        Folder folder = null;
        for (final Folder possibleRootFolder : app.nodeQuery(Folder.class).andName(parts[0])) {

            if (possibleRootFolder.getProperty(AbstractFile.parent) == null) {
                folder = possibleRootFolder;
                break;
            }

        }

        if (folder == null) {

            // Root folder doesn't exist, so create it and all child folders
            folder = app.create(Folder.class, parts[0]);
            logger.log(Level.INFO, "Created root folder {0}", new Object[] { parts[0] });

            for (int i = 1; i < parts.length - 1; i++) {
                Folder childFolder = app.create(Folder.class, parts[i]);
                childFolder.setProperty(AbstractFile.parent, folder);
                logger.log(Level.INFO, "Created {0} {1} with path {2}",
                        new Object[] { childFolder.getType(), childFolder, FileHelper.getFolderPath(childFolder) });
                folder = childFolder;
            }

            return folder;

        }

        // Root folder exists, so walk over children and search for next path part
        for (int i = 1; i < parts.length - 1; i++) {

            Folder subFolder = null;

            for (AbstractFile child : folder.getProperty(Folder.children)) {

                if (child instanceof Folder && child.getName().equals(parts[i])) {
                    subFolder = (Folder) child;
                    break;
                }

            }

            if (subFolder == null) {

                // sub folder doesn't exist, so create it and all child folders
                subFolder = app.create(Folder.class, parts[i]);
                subFolder.setProperty(AbstractFile.parent, folder);
                logger.log(Level.INFO, "Created {0} {1} with path {2}",
                        new Object[] { subFolder.getType(), subFolder, FileHelper.getFolderPath(subFolder) });

            }

            folder = subFolder;
        }

        return folder;

    }

    private void unarchive(final SecurityContext securityContext, final File file)
            throws ArchiveException, IOException, FrameworkException {

        final App app = StructrApp.getInstance(securityContext);
        final InputStream is;

        try (final Tx tx = app.tx()) {

            final String fileName = file.getName();

            logger.log(Level.INFO, "Unarchiving file {0}", fileName);

            is = file.getInputStream();
            tx.success();

            if (is == null) {

                getWebSocket().send(MessageBuilder.status().code(400)
                        .message("Could not get input stream from file ".concat(fileName)).build(), true);
                return;
            }
        }

        final ArchiveInputStream in = new ArchiveStreamFactory()
                .createArchiveInputStream(new BufferedInputStream(is));
        ArchiveEntry entry = in.getNextEntry();
        int overallCount = 0;

        while (entry != null) {

            try (final Tx tx = app.tx()) {

                int count = 0;

                while (entry != null && count++ < 50) {

                    final String entryPath = "/" + PathHelper.clean(entry.getName());
                    logger.log(Level.INFO, "Entry path: {0}", entryPath);

                    final AbstractFile f = FileHelper.getFileByAbsolutePath(securityContext, entryPath);
                    if (f == null) {

                        final Folder parentFolder = createOrGetParentFolder(securityContext, entryPath);
                        final String name = PathHelper.getName(entry.getName());

                        if (StringUtils.isNotEmpty(name) && (parentFolder == null
                                || !(FileHelper.getFolderPath(parentFolder).equals(entryPath)))) {

                            AbstractFile fileOrFolder = null;

                            if (entry.isDirectory()) {

                                fileOrFolder = app.create(Folder.class, name);

                            } else {

                                fileOrFolder = ImageHelper.isImageType(name)
                                        ? ImageHelper.createImage(securityContext, in, null, Image.class, name,
                                                false)
                                        : FileHelper.createFile(securityContext, in, null, File.class, name);
                            }

                            if (parentFolder != null) {
                                fileOrFolder.setProperty(AbstractFile.parent, parentFolder);
                            }

                            logger.log(Level.INFO, "Created {0} {1} with path {2}", new Object[] {
                                    fileOrFolder.getType(), fileOrFolder, FileHelper.getFolderPath(fileOrFolder) });

                            // create thumbnails while importing data
                            if (fileOrFolder instanceof Image) {
                                fileOrFolder.getProperty(Image.tnMid);
                                fileOrFolder.getProperty(Image.tnSmall);
                            }

                        }
                    }

                    entry = in.getNextEntry();

                    overallCount++;
                }

                logger.log(Level.INFO, "Committing transaction after {0} files..", overallCount);

                tx.success();
            }

        }

        in.close();

    }

    //~--- get methods ----------------------------------------------------
    @Override
    public String getCommand() {
        return "UNARCHIVE";
    }
}