ca.ualberta.physics.cssdp.vfs.resource.FileSystemResource.java Source code

Java tutorial

Introduction

Here is the source code for ca.ualberta.physics.cssdp.vfs.resource.FileSystemResource.java

Source

/* ============================================================
 * FileSystemResource.java
 * ============================================================
 * Copyright 2013 University of Alberta
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ 
 */
package ca.ualberta.physics.cssdp.vfs.resource;

import java.io.File;
import java.io.InputStream;
import java.util.Date;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import org.joda.time.LocalDateTime;

import ca.ualberta.physics.cssdp.auth.service.AuthClient;
import ca.ualberta.physics.cssdp.configuration.InjectorHolder;
import ca.ualberta.physics.cssdp.domain.vfs.VfsListing;
import ca.ualberta.physics.cssdp.domain.vfs.VfsListingEntry;
import ca.ualberta.physics.cssdp.jaxb.Link;
import ca.ualberta.physics.cssdp.service.ServiceResponse;
import ca.ualberta.physics.cssdp.vfs.service.FileSystemService;

import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses;

/**
 * A virtual file system accessible via REST commands. Delete is a special case
 * because we use a POST method instead of a DELETE method only because DELETE
 * does not accept request parameters.
 * 
 * @author rpotter
 * 
 */
@Path("/vfs/filesystem")
@Api(value = "/vfs/filesystem", description = "Operations about the files on a users VFS instance")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class FileSystemResource {

    @Inject
    private FileSystemService fileSystemService;

    @Inject
    private AuthClient authClient;

    public FileSystemResource() {
        InjectorHolder.inject(this);
    }

    @POST
    @Path("/{owner}/write")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @ApiOperation(value = "Write a file to this filesystem at the given path.  "
            + "If the file already exists, a '(n)' is suffixed to the filename. "
            + "Non-existing paths will automatically be created.")
    @ApiResponses(value = { @ApiResponse(code = 400, message = "No owner specified"),
            @ApiResponse(code = 400, message = "No path specified"),
            @ApiResponse(code = 400, message = "No file data specified"),
            @ApiResponse(code = 404, message = "No session found"),
            @ApiResponse(code = 500, message = "Unable to complete request, see response body for error details") })
    public Response write(
            @ApiParam(value = "The owner of this file system", required = true) @PathParam("owner") Long owner,
            @ApiParam(value = "The path to write the data to", required = true) @FormDataParam("path") String path,
            @ApiParam(value = "The file data to write", required = true) @FormDataParam("file") InputStream inputStream,
            @FormDataParam("file") FormDataContentDisposition disposition,
            @ApiParam(value = "The authenticated session token", required = true) @HeaderParam("CICSTART.session") String sessionToken,
            @Context UriInfo uriInfo) {

        if (Strings.isNullOrEmpty(path)) {
            return Response.status(400).build();
        }
        if (owner == null) {
            return Response.status(400).build();
        }

        validateSessionAndOwner(owner, sessionToken);

        path = path.endsWith("/") ? path : path + "/";

        String fileName = disposition.getFileName();
        if (Strings.isNullOrEmpty(fileName)) {
            return Response.status(400).entity("filename not found in content disposition").build();
        }
        ServiceResponse<Void> sr = fileSystemService.write(owner, path, fileName, inputStream);

        if (sr.isRequestOk()) {

            return Response
                    .status(201).location(UriBuilder.fromUri(uriInfo.getBaseUri()).path(getClass())
                            .path(owner.toString()).path("read").queryParam("path", path + fileName).build())
                    .build();

        } else {
            return Response.status(500).entity(sr.getMessagesAsStrings()).build();
        }

    }

    private boolean validateSessionAndOwner(Long owner, String sessionToken) {
        if (authClient.whois(sessionToken).getId().equals(owner)) {
            return true;
        }
        throw new WebApplicationException(404);
    }

    @GET
    @Path("/{owner}/read")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    @ApiOperation(value = "Read a file from this filesystem")
    @ApiResponses(value = { @ApiResponse(code = 400, message = "No owner specified"),
            @ApiResponse(code = 400, message = "No path specified"),
            @ApiResponse(code = 404, message = "File not found"),
            @ApiResponse(code = 404, message = "No session found"),
            @ApiResponse(code = 500, message = "Unable to complete request, see response body for error details") })
    public Response read(
            @ApiParam(value = "The owner of this filesystem", required = true) @PathParam("owner") Long owner,
            @ApiParam(value = "The path of the file to read", required = true) @QueryParam("path") String path,
            @ApiParam(value = "The authenticated session token", required = true) @HeaderParam("CICSTART.session") String sessionToken) {

        if (Strings.isNullOrEmpty(path)) {
            return Response.status(400).build();
        }
        if (owner == null) {
            return Response.status(400).build();
        }
        validateSessionAndOwner(owner, sessionToken);

        File file = fileSystemService.read(owner, path).getPayload();

        if (file != null) {

            ResponseBuilder response = Response.ok((Object) file);
            response.type(MediaType.APPLICATION_OCTET_STREAM);
            response.header("Content-Disposition", "attachment; " + "filename=\"" + file.getName() + "\"");
            return response.build();

        } else {
            return Response.status(404).build();
        }
    }

    @GET
    @Path("/{owner}/ls")
    @ApiOperation(value = "List the contents of a directory in this filesystem", response = ca.ualberta.physics.cssdp.domain.vfs.VfsListing.class)
    @ApiResponses(value = { @ApiResponse(code = 400, message = "No owner specified"),
            @ApiResponse(code = 400, message = "No path specified"),
            @ApiResponse(code = 404, message = "Path not found"),
            @ApiResponse(code = 404, message = "No session found"),
            @ApiResponse(code = 500, message = "Unable to complete request, see response body for error details") })
    public Response ls(
            @ApiParam(value = "The owner of this filesystem", required = true) @PathParam("owner") Long owner,
            @ApiParam(value = "The path of the file to read", required = true) @QueryParam("path") String path,
            @ApiParam(value = "The authenticated session token", required = true) @HeaderParam("CICSTART.session") String sessionToken,
            @Context UriInfo uriInfo) {

        if (Strings.isNullOrEmpty(path)) {
            return Response.status(400).build();
        }
        if (owner == null) {
            return Response.status(400).build();
        }
        validateSessionAndOwner(owner, sessionToken);

        path = path.endsWith("/") ? path : path + "/";

        ServiceResponse<File[]> sr = fileSystemService.ls(owner, path);
        if (sr.isRequestOk()) {

            File[] files = sr.getPayload();
            if (files != null) {

                VfsListing listing = new VfsListing();

                listing.setPath(new Link(path, UriBuilder.fromUri(uriInfo.getBaseUri()).path(getClass())
                        .path(owner.toString()).path("ls").queryParam("path", path).build()));

                for (File file : files) {

                    VfsListingEntry entry = new VfsListingEntry();
                    entry.setDir(file.isDirectory());

                    entry.setPath(new Link(file.getName(),
                            UriBuilder.fromUri(uriInfo.getBaseUri()).path(getClass()).path(owner.toString())
                                    .path("read").queryParam("path", path + file.getName()).build()));
                    entry.setSize(file.length());
                    entry.setLastModified(new LocalDateTime(new Date(file.lastModified())));
                    listing.getEntries().add(entry);
                }

                return Response.ok(listing).build();
            } else {
                return Response.status(204).build();
            }
        } else {
            return Response.status(500).entity(sr.getMessagesAsStrings()).build();
        }
    }

    /**
     * Not "RESTful", but DELETE doesn't take parameters and we need the params
     * to locate the file or directory to delete
     * 
     * @param owner
     * @param path
     * @return
     */
    @POST
    @Path("/{owner}/rm")
    @ApiOperation(value = "Remove a file or files in a directory in this filesystem")
    @ApiResponses(value = { @ApiResponse(code = 400, message = "No owner specified"),
            @ApiResponse(code = 400, message = "No path specified"),
            @ApiResponse(code = 404, message = "File or path not found"),
            @ApiResponse(code = 404, message = "No session found"),
            @ApiResponse(code = 500, message = "Unable to complete request, see response body for error details") })
    public Response rm(
            @ApiParam(value = "The owner of this filesystem", required = true) @PathParam("owner") Long owner,
            @ApiParam(value = "The path of the file to read", required = true) @QueryParam("path") String path,
            @ApiParam(value = "The authenticated session token", required = true) @HeaderParam("CICSTART.session") String sessionToken) {

        if (Strings.isNullOrEmpty(path)) {
            return Response.status(400).build();
        }
        if (owner == null) {
            return Response.status(400).build();
        }

        validateSessionAndOwner(owner, sessionToken);

        ServiceResponse<Void> sr = fileSystemService.rm(owner, path);

        if (sr.isRequestOk()) {
            return Response.ok().build();
        } else {
            return Response.status(500).entity(sr.getMessagesAsStrings()).build();
        }
    }

}