/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.usergrid.rest.applications.assets; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.jaxrs.json.annotation.JSONP; import org.apache.usergrid.persistence.EntityManager; import org.apache.usergrid.persistence.entities.Asset; import org.apache.usergrid.rest.AbstractContextResource; import org.apache.usergrid.rest.ApiResponse; import org.apache.usergrid.rest.applications.ServiceResource; import org.apache.usergrid.rest.security.annotations.CheckPermissionsForPath; import org.apache.usergrid.rest.security.annotations.RequireApplicationAccess; import org.apache.usergrid.services.assets.BinaryStoreFactory; import org.apache.usergrid.services.assets.data.*; import org.apache.usergrid.utils.StringUtils; import org.glassfish.jersey.media.multipart.FormDataParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.io.InputStream; import java.util.Date; import java.util.Map; import java.util.UUID; import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_USERGRID_BINARY_UPLOADER; /** @deprecated */ @Component("org.apache.usergrid.rest.applications.assets.AssetsResource") @Scope("prototype") @Produces(MediaType.APPLICATION_JSON) public class AssetsResource extends ServiceResource { private static final Logger logger = LoggerFactory.getLogger( AssetsResource.class ); private BinaryStore binaryStore; @Autowired private BinaryStoreFactory binaryStoreFactory; @Override @Path("{itemName}") public AbstractContextResource addNameParameter( @Context UriInfo ui, @PathParam("itemName") PathSegment itemName ) throws Exception { if (logger.isTraceEnabled()) { logger.trace("in AssetsResource.addNameParameter"); } super.addNameParameter( ui, itemName ); if (logger.isTraceEnabled()) { logger.trace("serviceParamters now has: {}", getServiceParameters()); } // HTF to work w/ the ../data endpoint when we are looking up by path? return getSubResource( AssetsResource.class ); } @Override @RequireApplicationAccess @GET @JSONP @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) public ApiResponse executeGet( @Context UriInfo ui, @QueryParam("callback") @DefaultValue("callback") String callback ) throws Exception { if (logger.isTraceEnabled()) { logger.trace("In AssetsResource.executeGet with ui: {} and callback: {}", ui, callback); } return super.executeGet( ui, callback ); } @Override @PUT @RequireApplicationAccess @Consumes(MediaType.APPLICATION_JSON) @JSONP @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) public ApiResponse executePut( @Context UriInfo ui, String body, @QueryParam("callback") @DefaultValue("callback") String callback ) throws Exception { ObjectMapper mapper = new ObjectMapper(); Map<String, Object> json = mapper.readValue( body, mapTypeReference ); return super.executePutWithMap( ui, json, callback ); } @CheckPermissionsForPath @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("{entityId: [A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}}/data") public Response uploadData( @FormDataParam("file") InputStream uploadedInputStream, // @FormDataParam("file") FormDataContentDisposition fileDetail, @PathParam("entityId") PathSegment entityId ) throws Exception { // needed for testing this.binaryStore = binaryStoreFactory.getBinaryStore( properties.getProperty(PROPERTIES_USERGRID_BINARY_UPLOADER) ); if (uploadedInputStream != null ) { UUID assetId = UUID.fromString( entityId.getPath() ); logger.info( "In AssetsResource.uploadData with id: {}", assetId ); EntityManager em = emf.getEntityManager( getApplicationId() ); Asset asset = em.get( assetId, Asset.class ); binaryStore.write( getApplicationId(), asset, uploadedInputStream ); em.update( asset ); return Response.status( 200 ).build(); } else { return Response.status(Response.Status.BAD_REQUEST).entity("File Not Found").build(); } } @CheckPermissionsForPath @PUT @Consumes(MediaType.APPLICATION_OCTET_STREAM) @Path("{entityId: [A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}}/data") public Response uploadDataStreamPut( @PathParam("entityId") PathSegment entityId, InputStream uploadedInputStream ) throws Exception { return uploadDataStream( entityId, uploadedInputStream ); } @CheckPermissionsForPath @POST @Consumes(MediaType.APPLICATION_OCTET_STREAM) @Path("{entityId: [A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}}/data") public Response uploadDataStream( @PathParam("entityId") PathSegment entityId, InputStream uploadedInputStream ) throws Exception { // needed for testing this.binaryStore = binaryStoreFactory.getBinaryStore( properties.getProperty(PROPERTIES_USERGRID_BINARY_UPLOADER) ); UUID assetId = UUID.fromString( entityId.getPath() ); if (logger.isTraceEnabled()) { logger.trace("In AssetsResource.uploadDataStream with id: {}", assetId); } EntityManager em = emf.getEntityManager( getApplicationId() ); Asset asset = em.get( assetId, Asset.class ); binaryStore.write( getApplicationId(), asset, uploadedInputStream ); if (logger.isTraceEnabled()) { logger.trace("uploadDataStream written, returning response"); } em.update( asset ); return Response.status( 200 ).build(); } @CheckPermissionsForPath @GET @Path("{entityId: [A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}}/data") public Response findAsset( @Context UriInfo ui, @QueryParam("callback") @DefaultValue("callback") String callback, @PathParam("entityId") PathSegment entityId, @HeaderParam("range") String range, @HeaderParam("if-modified-since") String modifiedSince ) throws Exception { // needed for testing this.binaryStore = binaryStoreFactory.getBinaryStore( properties.getProperty(PROPERTIES_USERGRID_BINARY_UPLOADER) ); UUID assetId = UUID.fromString( entityId.getPath() ); if (logger.isTraceEnabled()) { logger.trace("In AssetsResource.findAsset with id: {}, range: {}, modifiedSince: {}", assetId, range, modifiedSince); } EntityManager em = emf.getEntityManager( getApplicationId() ); Asset asset = em.get( assetId, Asset.class ); Map<String, Object> fileMetadata = AssetUtils.getFileMetadata( asset ); // todo: use fileMetadata // return a 302 if not modified Date moded = AssetUtils.fromIfModifiedSince( modifiedSince ); if ( moded != null ) { if ( asset.getModified() - moded.getTime() < 0 ) { return Response.status( Response.Status.NOT_MODIFIED ).build(); } } InputStream is; if ( StringUtils.isBlank( range ) ) { is = binaryStore.read( getApplicationId(), asset ); } else { // TODO range parser is = binaryStore.read( getApplicationId(), asset ); } if ( is == null ) { return Response.status( Response.Status.NOT_FOUND ).build(); } if (logger.isTraceEnabled()) { logger.trace("AssetResource.findAsset read inputStream, composing response"); } Response.ResponseBuilder responseBuilder = Response.ok( is ).type( fileMetadata.get( "content-type" ).toString() ) .lastModified( new Date( asset.getModified() ) ); if ( fileMetadata.get( AssetUtils.E_TAG ) != null ) { responseBuilder.tag( ( String ) fileMetadata.get( AssetUtils.E_TAG ) ); } if ( StringUtils.isNotBlank( range ) ) { if (logger.isTraceEnabled()) { logger.trace("Range header was not blank, sending back Content-Range"); } // TODO build content range header if needed //responseBuilder.header("Content-Range", ); } return responseBuilder.build(); } }