org.geoserver.restupload.ResumableUploadResourceManager.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.restupload.ResumableUploadResourceManager.java

Source

/* (c) 2015 Open Source Geospatial Foundation - all rights reserved
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */

package org.geoserver.restupload;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.Resources;
import org.geoserver.rest.util.IOUtils;
import org.geoserver.rest.util.RESTUtils;
import org.geotools.util.logging.Logging;
import org.restlet.resource.Representation;

/**
 * Manages resumable upload resource
 *
 * @author Nicola Lagomarsini
 */

public class ResumableUploadResourceManager {
    /** LOGGER class */
    private static final Logger LOGGER = Logging.getLogger(ResumableUploadResourceManager.class);

    /** Resource folder for the temporary uploads */
    private static Resource tmpUploadFolder;

    public ResumableUploadResourceManager(String tmpFolder) {
        GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class);
        tmpUploadFolder = loader.get(tmpFolder);
    }

    public String createUploadResource(String filePath) throws IllegalStateException, IOException {
        String uploadId = getUploadId();
        ResumableUploadResource uploadResource = getResource(uploadId);
        if (uploadResource != null) {
            throw new IllegalStateException("The uploadId was already set!");
        } else {
            createUploadResource(filePath, uploadId);
        }
        return uploadId;
    }

    public Boolean hasAnyResource() {
        Collection<File> files = FileUtils.listFiles(tmpUploadFolder.dir(), new WildcardFileFilter("*.*"),
                TrueFileFilter.INSTANCE);
        return (files.size() != 0);
    }

    public Boolean resourceExists(String uploadId) {
        return getResource(uploadId) != null;
    }

    public Long handleUpload(String uploadId, Representation entity, Long startPosition) {
        ResumableUploadResource resource = getResource(uploadId);
        Long writtenBytes = 0L;
        try {
            final ReadableByteChannel source = entity.getChannel();
            RandomAccessFile raf = null;
            FileChannel outputChannel = null;
            try {
                raf = new RandomAccessFile(resource.getFile(), "rw");
                outputChannel = raf.getChannel();
                writtenBytes = IOUtils.copyToFileChannel(256 * 1024, source, outputChannel, startPosition);
            } finally {
                try {
                    if (raf != null) {
                        raf.close();
                    }
                } finally {
                    IOUtils.closeQuietly(source);
                    IOUtils.closeQuietly(outputChannel);
                }
            }
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
        } finally {

        }

        return resource.getFile().length();
    }

    /**
     * Executes validations on resume parameters to check
     * if successive start position index matches actual partial file length
     */
    public Boolean validateUpload(String uploadId, Long totalByteToUpload, Long startPosition, Long endPosition,
            Long totalFileSize) {
        Boolean validated = false;
        ResumableUploadResource uploadResource = getResource(uploadId);
        if (uploadResource != null && uploadResource.getFile().exists()) {
            if (uploadResource.getFile().length() == startPosition) {
                validated = true;
            }
        }
        return validated;
    }

    /**
     * Create new temporary file for this uploadId
     */
    public void clearUpload(String uploadId) {
        ResumableUploadResource resource = getResource(uploadId);
        if (resource != null) {
            resource.clear();
        }
    }

    /**
     * Deletes all the file from temporary folder which aren't modified from more than expirationThreshold
     */
    public void cleanExpiredResources(long expirationThreshold) {
        Collection<File> files = FileUtils.listFiles(tmpUploadFolder.dir(), new WildcardFileFilter("*.*"),
                TrueFileFilter.INSTANCE);
        for (Iterator<File> i = files.iterator(); i.hasNext();) {
            File file = i.next();
            if (file.lastModified() < expirationThreshold) {
                file.delete();
            }
        }
    }

    public Long getWrittenBytes(String uploadId) {
        return getResource(uploadId).getFile().length();
    }

    /**
     * Executes the mapping to move uploaded file from temporary folder to REST upload root
     * Creates the sidecar file
     */
    public String uploadDone(String uploadId) throws IOException {
        ResumableUploadResource resource = getResource(uploadId);
        Map<String, String> storeParams = new HashMap<String, String>();
        String destinationPath = getDestinationPath(uploadId);
        StringBuilder remappingPath = new StringBuilder(destinationPath);
        String tempFile = resource.getFile().getCanonicalPath();
        RESTUtils.remapping(null, FilenameUtils.getBaseName(destinationPath), remappingPath, tempFile, storeParams);
        //Move file to remapped path
        Resource destinationFile = Resources.fromPath(remappingPath.toString());
        // Fill file
        IOUtils.copyStream(new FileInputStream(resource.getFile()), destinationFile.out(), true, true);
        resource.delete();
        // Add temporary sidecar file to mark upload completion, it will be cleared after expirationThreshold
        getSideCarFile(uploadId).createNewFile();
        return destinationPath.toString();
    }

    /**
     * Checks if upload with uploadId is terminated:</br>
     * <ul>
     * <li>if resource exists in temp folder the upload is not terminate
     * <li>if sidecar file exists in temp folder the upload is terminated
     * <li>if no resource or sidecar is found the resource is unknown
     * </ul>
     */
    public Boolean isUploadDone(String uploadId) throws IOException, IllegalStateException {
        ResumableUploadResource resource = getResource(uploadId);
        if (resource != null) {
            return false;
        } else {
            if (getSideCarFile(uploadId).exists()) {
                return true;
            } else {
                throw new IllegalStateException("Resource uploaded not found");
            }
        }
    }

    //Return relative path of uploaded file
    private String getDestinationPath(String uploadId) throws IOException {
        ResumableUploadResource resource = getResource(uploadId);
        String fileName = resource.getFile().getCanonicalPath().replaceAll(tmpUploadFolder.dir().getCanonicalPath(),
                "");
        fileName = fileName.replaceAll("_" + uploadId, "");
        fileName = fileName.replaceAll("^/", "");
        return fileName;
    }

    // Create a sidecar file associated to uploadId
    private File getSideCarFile(String uploadId) throws IOException {
        String sidecarPath = FilenameUtils.concat(tmpUploadFolder.dir().getCanonicalPath(), uploadId + ".sidecar");
        return new File(sidecarPath);
    }

    // Find resource with specific uploadId into temp folder
    private ResumableUploadResource getResource(String uploadId) throws IllegalStateException {
        Collection<File> files = FileUtils.listFiles(tmpUploadFolder.dir(),
                new WildcardFileFilter("*_" + uploadId + ".*"), TrueFileFilter.INSTANCE);
        if (files.size() == 1) {
            return new ResumableUploadResource(uploadId, files.iterator().next());
        }
        if (files.size() > 1) {
            throw new IllegalStateException("Found multiple files with same uploadId");
        }
        return null;
    }

    // Create a file by append uploadId to filePath with "_" separator
    private void createUploadResource(String filePath, String uploadId) throws IOException {
        String tempPath = FilenameUtils.removeExtension(filePath) + "_" + uploadId + "."
                + FilenameUtils.getExtension(filePath);
        tempPath = tempPath.replaceAll("^/", "");
        tempPath = FilenameUtils.concat(tmpUploadFolder.dir().getCanonicalPath(), tempPath);
        try {
            new File(tempPath).getParentFile().mkdirs();
            new File(tempPath).createNewFile();
        } catch (IOException e) {
            throw new IllegalStateException("Unable to create upload resource");
        }
    }

    // Generate random uploadId
    private String getUploadId() {
        String id = UUID.randomUUID().toString();
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.log(Level.FINE, "Associating resource with upload id: " + id);
        }
        return id;
    }

    private static final class ResumableUploadResource {
        private String id;
        private File file;

        public ResumableUploadResource(String id, File file) {
            this.id = id;
            this.file = file;
        }

        public File getFile() {
            return file;
        }

        public void delete() {
            if (this.file.exists()) {
                this.file.delete();
            }
        }

        public void clear() {
            if (this.file.exists()) {
                this.file.delete();
            }
            try {
                this.file.createNewFile();
            } catch (IOException e) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
            }
        }

    }
}