001    package de.deepamehta.plugins.files.provider;
002    
003    import de.deepamehta.plugins.files.UploadedFile;
004    
005    import org.apache.commons.fileupload.FileItem;
006    import org.apache.commons.fileupload.FileItemFactory;
007    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
008    import org.apache.commons.fileupload.servlet.ServletFileUpload;
009    
010    import java.io.InputStream;
011    import java.io.IOException;
012    import java.lang.annotation.Annotation;
013    import java.lang.reflect.Type;
014    import java.util.List;
015    import java.util.logging.Logger;
016    
017    import javax.servlet.http.HttpServletRequest;
018    
019    import javax.ws.rs.WebApplicationException;
020    import javax.ws.rs.core.Context;
021    import javax.ws.rs.core.MediaType;
022    import javax.ws.rs.core.MultivaluedMap;
023    import javax.ws.rs.ext.MessageBodyReader;
024    import javax.ws.rs.ext.Provider;
025    
026    
027    
028    @Provider
029    public class UploadedFileProvider implements MessageBodyReader<UploadedFile> {
030    
031        // ---------------------------------------------------------------------------------------------- Instance Variables
032    
033        @Context
034        private HttpServletRequest request;
035    
036        private Logger logger = Logger.getLogger(getClass().getName());
037    
038        // -------------------------------------------------------------------------------------------------- Public Methods
039    
040        // *** MessageBodyReader Implementation ***
041    
042        @Override
043        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
044            // Note: unlike equals() isCompatible() ignores parameters like "charset" in "application/json;charset=UTF-8"
045            return type == UploadedFile.class && mediaType.isCompatible(MediaType.MULTIPART_FORM_DATA_TYPE);
046        }
047    
048        @Override
049        public UploadedFile readFrom(Class<UploadedFile> type, Type genericType, Annotation[] annotations,
050                MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
051                                                                    throws IOException, WebApplicationException {
052            try {
053                return parseMultiPart();
054            } catch (Exception e) {
055                throw new RuntimeException("Creating UploadedFile from message body failed", e);
056            }
057        }
058    
059        // ------------------------------------------------------------------------------------------------- Private Methods
060    
061        private UploadedFile parseMultiPart() {
062            try {
063                UploadedFile file = null;
064                FileItemFactory factory = new DiskFileItemFactory();        // create a factory for disk-based file items
065                ServletFileUpload upload = new ServletFileUpload(factory);  // create a new file upload handler
066                List<FileItem> items = upload.parseRequest(request);        // parse the request
067                // FIXME: check if we can use a FileUpload low-level method to parse the request body instead of the
068                // entire request. Advantage: a) no need to inject the HttpServletRequest, b) no double work as the
069                // request is already parsed by jersey, c) no dependency on servlet-api.
070                logger.info("### Parsing multipart/form-data request (" + items.size() + " parts)");
071                for (FileItem item : items) {
072                    String fieldName = item.getFieldName();
073                    if (item.isFormField()) {
074                        String value = item.getString();
075                        logger.info("### field \"" + fieldName + "\" => \"" + value + "\"");
076                        throw new RuntimeException("\"" + fieldName + "\" is an unexpected field (value=\"" + value +
077                            "\")");
078                    } else {
079                        if (file != null) {
080                            throw new RuntimeException("Only single file uploads are supported");
081                        }
082                        file = new UploadedFile(item);
083                        logger.info("### field \"" + fieldName + "\" => " + file);
084                    }
085                }
086                if (file == null) {
087                    throw new RuntimeException("Request does not contain a file part");
088                }
089                return file;
090            } catch (Exception e) {
091                throw new RuntimeException("Parsing multipart/form-data request failed", e);
092            }
093        }
094    }