Java tutorial
/** * Copyright 2017 Hortonworks. * * 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 com.hortonworks.streamline.streams.service; import com.codahale.metrics.annotation.Timed; import com.hortonworks.streamline.streams.security.Permission; import com.hortonworks.streamline.streams.security.Roles; import com.hortonworks.streamline.streams.security.SecurityUtil; import com.hortonworks.streamline.streams.security.StreamlineAuthorizer; import org.apache.commons.lang3.StringUtils; import com.hortonworks.streamline.common.util.WSUtils; import com.hortonworks.streamline.streams.catalog.File; import com.hortonworks.streamline.streams.catalog.service.CatalogService; import com.hortonworks.streamline.common.exception.service.exception.request.EntityNotFoundException; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.EnumSet; import java.util.UUID; import static com.hortonworks.streamline.streams.security.Permission.EXECUTE; import static com.hortonworks.streamline.streams.security.Permission.READ; import static com.hortonworks.streamline.streams.security.Permission.WRITE; import static com.hortonworks.streamline.streams.security.Permission.DELETE; import static javax.ws.rs.core.Response.Status.CREATED; import static javax.ws.rs.core.Response.Status.OK; /** * Catalog resource for {@link File} resources. */ @Path("/v1/catalog") @Produces(MediaType.APPLICATION_JSON) public class FileCatalogResource { private static final Logger log = LoggerFactory.getLogger(FileCatalogResource.class); private final StreamlineAuthorizer authorizer; private final CatalogService catalogService; public FileCatalogResource(StreamlineAuthorizer authorizer, CatalogService catalogService) { this.authorizer = authorizer; this.catalogService = catalogService; } @GET @Path("/files") @Timed public Response listFiles(@Context UriInfo uriInfo, @Context SecurityContext securityContext) { Collection<File> files = null; MultivaluedMap<String, String> params = uriInfo.getQueryParameters(); if (params == null || params.isEmpty()) { files = catalogService.listFiles(); } else { files = catalogService.listFiles(WSUtils.buildQueryParameters(params)); } Collection<File> result = SecurityUtil.filter(authorizer, securityContext, File.NAMESPACE, files, READ); return WSUtils.respondEntities(result, OK); } /** * Adds given resource to the configured file-storage and adds an entry in entity storage. * * Below example describes how a file can be added along with metadata * <blockquote><pre> * curl -X POST -i -F file=@user-lib.jar -F "fileInfo={\"name\":\"jar-1\",\"version\":1};type=application/json" http://localhost:8080/api/v1/catalog/files * * HTTP/1.1 100 Continue * * HTTP/1.1 201 Created * Date: Fri, 15 Apr 2016 10:36:33 GMT * Content-Type: application/json * Content-Length: 239 * * {"responseCode":1000,"responseMessage":"Success","entity":{"id":1234,"name":"jar-1","className":null,"storedFileName":"/tmp/test-hdfs/jar-1-ea41fe3a-12f9-45d4-ae24-818d570b8963.jar","version":1,"timestamp":1460716593157,"auxiliaryInfo":null}} * </pre></blockquote> * * @param inputStream actual file content as {@link InputStream}. * @param contentDispositionHeader {@link FormDataContentDisposition} instance of the received file * @param file configuration of the file resource {@link File} * @return */ @Timed @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("/files") public Response addFile(@FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader, @FormDataParam("fileInfo") final File file, @Context SecurityContext securityContext) throws IOException { SecurityUtil.checkRole(authorizer, securityContext, Roles.ROLE_ADMIN); log.info("Received fileInfo: [{}]", file); File updatedFile = addFile(inputStream, file); SecurityUtil.addAcl(authorizer, securityContext, File.NAMESPACE, updatedFile.getId(), EnumSet.allOf(Permission.class)); return WSUtils.respondEntity(updatedFile, CREATED); } protected String getFileStorageName(String fileName) { return (StringUtils.isBlank(fileName) ? "file" : fileName) + "-" + UUID.randomUUID().toString(); } /** * * @param inputStream * @param contentDispositionHeader * @param file */ @Timed @PUT @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("/files/{id}") public Response updateFile(@PathParam("id") Long fileId, @FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader, @FormDataParam("fileInfo") final File file, @Context SecurityContext securityContext) throws IOException { SecurityUtil.checkPermissions(authorizer, securityContext, File.NAMESPACE, fileId, WRITE); log.info("Received fileInfo: [{}]", file); String oldFileStorageName = null; final File existingFile = catalogService.getFile(fileId); if (existingFile != null) { oldFileStorageName = existingFile.getStoredFileName(); } final File updatedFile = addOrUpdateFile(fileId, inputStream, file); if (oldFileStorageName != null) { final boolean deleted = catalogService.deleteFileFromStorage(oldFileStorageName); logDeletionMessage(oldFileStorageName, deleted); } return WSUtils.respondEntity(updatedFile, CREATED); } protected File addFile(InputStream inputStream, File file) { uploadFile(inputStream, file); return catalogService.addFile(file); } protected File addOrUpdateFile(Long fileId, InputStream inputStream, File file) { uploadFile(inputStream, file); return catalogService.addOrUpdateFile(fileId, file); } private void uploadFile(InputStream inputStream, File file) { try { final String updatedFileStorageName = getFileStorageName(file.getName()); file.setStoredFileName(updatedFileStorageName); log.info("Uploading File with fileInfo [{}]", file); final String uploadedFileStoragePath = catalogService.uploadFileToStorage(inputStream, updatedFileStorageName); log.info("Received File with fileInfo is uploaded to [{}]", uploadedFileStoragePath); file.setTimestamp(System.currentTimeMillis()); } catch (IOException ex) { log.error("Got exception while uploading file"); throw new RuntimeException("Got exception while uploading file", ex); } } @GET @Path("/files/{id}") @Timed public Response getFile(@PathParam("id") Long fileId, @Context SecurityContext securityContext) { SecurityUtil.checkPermissions(authorizer, securityContext, File.NAMESPACE, fileId, READ); File result = catalogService.getFile(fileId); if (result != null) { return WSUtils.respondEntity(result, OK); } throw EntityNotFoundException.byId(fileId.toString()); } /** * Deletes the file of given {@code fileId} * * @param fileId */ @DELETE @Path("/files/{id}") @Timed public Response removeFile(@PathParam("id") Long fileId, @Context SecurityContext securityContext) throws IOException { SecurityUtil.checkPermissions(authorizer, securityContext, File.NAMESPACE, fileId, DELETE); try { File removedFile = catalogService.removeFile(fileId); SecurityUtil.removeAcl(authorizer, securityContext, File.NAMESPACE, fileId); log.info("Removed File entry is [{}]", removedFile); if (removedFile != null) { boolean removed = catalogService.deleteFileFromStorage(removedFile.getStoredFileName()); logDeletionMessage(removedFile.getStoredFileName(), removed); return WSUtils.respondEntity(removedFile, OK); } } catch (IOException ex) { log.error("Encountered error in removing file with id [{}]", fileId, ex); throw ex; } log.info("File entry with id [{}] is not found", fileId); throw EntityNotFoundException.byId(fileId.toString()); } protected void logDeletionMessage(String removedFileName, boolean removed) { log.info("Delete action for File [{}] from storage is [{}]", removedFileName, removed ? "success" : "failure"); } /** * Downloads a given {@link File} resource for given {@code fileId} * * @param fileId */ @Timed @GET @Produces({ "application/octet-stream", "application/json" }) @Path("/files/download/{fileId}") public Response downloadFile(@PathParam("fileId") Long fileId, @Context SecurityContext securityContext) throws IOException { SecurityUtil.checkPermissions(authorizer, securityContext, File.NAMESPACE, fileId, READ, EXECUTE); File file = catalogService.getFile(fileId); if (file != null) { StreamingOutput streamOutput = WSUtils .wrapWithStreamingOutput(catalogService.downloadFileFromStorage(file.getStoredFileName())); return Response.ok(streamOutput).build(); } throw EntityNotFoundException.byId(fileId.toString()); } }