org.xwiki.filemanager.internal.job.PackJob.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.filemanager.internal.job.PackJob.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.filemanager.internal.job;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.IOUtils;
import org.xwiki.component.annotation.Component;
import org.xwiki.environment.Environment;
import org.xwiki.filemanager.FileSystem;
import org.xwiki.filemanager.Folder;
import org.xwiki.filemanager.Path;
import org.xwiki.filemanager.job.PackJobStatus;
import org.xwiki.filemanager.job.PackRequest;
import org.xwiki.job.internal.AbstractJob;
import org.xwiki.model.reference.AttachmentReference;
import org.xwiki.model.reference.DocumentReference;

/**
 * Packs multiple files and folders (including the child files and sub-folders) in a single ZIP archive.
 * 
 * @version $Id$
 * @since 2.0M2
 */
@Component
@Named(PackJob.JOB_TYPE)
public class PackJob extends AbstractJob<PackRequest, PackJobStatus> {
    /**
     * The id of the job.
     */
    public static final String JOB_TYPE = "pack";

    /**
     * The default URL encoding.
     */
    private static final String UTF8 = "UTF-8";

    /**
     * The module name used when creating temporary files.
     */
    private static final String MODULE_NAME = "filemanager";

    /**
     * The pseudo file system.
     */
    @Inject
    private FileSystem fileSystem;

    /**
     * Used to access the temporary directory.
     */
    @Inject
    private Environment environment;

    @Override
    public String getType() {
        return JOB_TYPE;
    }

    @Override
    protected PackJobStatus createNewStatus(PackRequest request) {
        return new PackJobStatus(request, this.observationManager, this.loggerManager);
    }

    @Override
    protected void runInternal() throws Exception {
        Collection<Path> paths = getRequest().getPaths();
        if (paths == null) {
            return;
        }

        File outputFile = getTemporaryFile(getRequest().getOutputFileReference());
        // TODO: Use java.util.zip.ZipOutputStream when moving to Java 7.
        // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4244499
        ZipArchiveOutputStream zip = new ZipArchiveOutputStream(outputFile);
        String pathPrefix = "";

        notifyPushLevelProgress(paths.size());

        try {
            for (Path path : paths) {
                pack(path, zip, pathPrefix);
                notifyStepPropress();
            }
        } finally {
            IOUtils.closeQuietly(zip);
            getStatus().setOutputFileSize(outputFile.length());
            notifyPopLevelProgress();
        }
    }

    /**
     * Creates a temporary file that can be accessed through the 'temp' action, e.g.:
     * {@code /xwiki/temp/Space/Page/filemanager/file.zip} .
     * 
     * @param fileReference the reference to the temporary file to create
     * @return the temporary file
     * @throws Exception if it fails to create the temporary file
     */
    private File getTemporaryFile(AttachmentReference fileReference) throws Exception {
        // Encode to avoid illegal characters in file paths.
        DocumentReference accessDocRef = fileReference.getDocumentReference();
        String encodedWiki = URLEncoder.encode(accessDocRef.getWikiReference().getName(), UTF8);
        String encodedSpace = URLEncoder.encode(accessDocRef.getLastSpaceReference().getName(), UTF8);
        String encodedPage = URLEncoder.encode(accessDocRef.getName(), UTF8);
        String encodedFileName = URLEncoder.encode(fileReference.getName(), UTF8);

        // Create a temporary directory to hold the file.
        String path = String.format("temp/%s/%s/%s/%s/", MODULE_NAME, encodedWiki, encodedSpace, encodedPage);
        File tempDir = new File(this.environment.getTemporaryDirectory(), path);
        if (!((tempDir.exists() || tempDir.mkdirs()) && tempDir.isDirectory() && tempDir.canWrite())) {
            String message = "Failed to create temporary directory [%s].";
            throw new Exception(String.format(message, path));
        }

        File file = new File(tempDir, encodedFileName);
        file.deleteOnExit();
        return file;
    }

    /**
     * Packs a file or a folder.
     * 
     * @param path the file or folder to add to the ZIP archive
     * @param zip the ZIP archive to add the file or folder to
     * @param pathPrefix the current path prefix, used to ensure the folder hierarchy is preserved in the ZIP file
     */
    private void pack(Path path, ZipArchiveOutputStream zip, String pathPrefix) {
        if (path.getFileReference() != null) {
            packFile(path.getFileReference(), zip, pathPrefix);
        } else if (path.getFolderReference() != null) {
            packFolder(path.getFolderReference(), zip, pathPrefix);
        }
    }

    /**
     * Packs a file.
     * 
     * @param fileReference the file to add to the ZIP archive
     * @param zip the ZIP archive to add the file to
     * @param pathPrefix the file path
     */
    private void packFile(DocumentReference fileReference, ZipArchiveOutputStream zip, String pathPrefix) {
        org.xwiki.filemanager.File file = fileSystem.getFile(fileReference);
        if (file != null && fileSystem.canView(fileReference)) {
            try {
                String path = pathPrefix + file.getName();
                this.logger.info("Packing file [{}]", path);
                zip.putArchiveEntry(new ZipArchiveEntry(path));
                IOUtils.copy(file.getContent(), zip);
                zip.closeArchiveEntry();
                getStatus().setBytesWritten(zip.getBytesWritten());
            } catch (IOException e) {
                this.logger.warn("Failed to pack file [{}].", fileReference, e);
            }
        }
    }

    /**
     * Packs a folder.
     * 
     * @param folderReference the folder to add to the ZIP archive
     * @param zip the ZIP archive to add the folder to
     * @param pathPrefix the folder path
     */
    private void packFolder(DocumentReference folderReference, ZipArchiveOutputStream zip, String pathPrefix) {
        Folder folder = fileSystem.getFolder(folderReference);
        if (folder != null && fileSystem.canView(folderReference)) {
            List<DocumentReference> childFolderReferences = folder.getChildFolderReferences();
            List<DocumentReference> childFileReferences = folder.getChildFileReferences();
            notifyPushLevelProgress(childFolderReferences.size() + childFileReferences.size() + 1);

            try {
                String path = pathPrefix + folder.getName() + '/';
                this.logger.info("Packing folder [{}]", path);
                zip.putArchiveEntry(new ZipArchiveEntry(path));
                zip.closeArchiveEntry();
                notifyStepPropress();

                for (DocumentReference childFolderReference : childFolderReferences) {
                    packFolder(childFolderReference, zip, path);
                    notifyStepPropress();
                }

                for (DocumentReference childFileReference : childFileReferences) {
                    packFile(childFileReference, zip, path);
                    notifyStepPropress();
                }
            } catch (IOException e) {
                this.logger.warn("Failed to pack folder [{}].", folderReference, e);
            } finally {
                notifyPopLevelProgress();
            }
        }
    }
}