001/*
002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 */
019package org.nuxeo.ecm.automation.jaxrs.io.operations;
020
021import java.io.File;
022import java.io.IOException;
023import java.io.InputStream;
024import java.lang.annotation.Annotation;
025import java.lang.reflect.Type;
026import java.util.List;
027
028import javax.mail.BodyPart;
029import javax.mail.MessagingException;
030import javax.mail.internet.MimeMultipart;
031import javax.servlet.http.HttpServletRequest;
032import javax.ws.rs.Consumes;
033import javax.ws.rs.WebApplicationException;
034import javax.ws.rs.core.Context;
035import javax.ws.rs.core.MediaType;
036import javax.ws.rs.core.MultivaluedMap;
037import javax.ws.rs.ext.MessageBodyReader;
038import javax.ws.rs.ext.Provider;
039
040import org.apache.commons.io.FileUtils;
041import org.apache.commons.logging.Log;
042import org.apache.commons.logging.LogFactory;
043import org.nuxeo.ecm.automation.core.util.BlobList;
044import org.nuxeo.ecm.automation.jaxrs.io.InputStreamDataSource;
045import org.nuxeo.ecm.automation.jaxrs.io.SharedFileInputStream;
046import org.nuxeo.ecm.core.api.Blob;
047import org.nuxeo.ecm.core.api.Blobs;
048import org.nuxeo.ecm.core.api.CoreSession;
049import org.nuxeo.ecm.core.api.NuxeoException;
050import org.nuxeo.ecm.webengine.jaxrs.context.RequestContext;
051import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory;
052import org.nuxeo.runtime.api.Framework;
053
054import com.fasterxml.jackson.core.JsonFactory;
055import com.fasterxml.jackson.core.JsonParser;
056
057/**
058 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
059 * @deprecated since 9.1. Only {@link MultiPartFormRequestReader} is used.
060 */
061@Provider
062@Consumes("multipart/related")
063@Deprecated
064public class MultiPartRequestReader implements MessageBodyReader<ExecutionRequest> {
065
066    private static final Log log = LogFactory.getLog(MultiPartRequestReader.class);
067
068    @Context
069    protected HttpServletRequest request;
070
071    @Context
072    JsonFactory factory;
073
074    public CoreSession getCoreSession() {
075        return SessionFactory.getSession(request);
076    }
077
078    @Override
079    public boolean isReadable(Class<?> arg0, Type arg1, Annotation[] arg2, MediaType arg3) {
080        return ExecutionRequest.class.isAssignableFrom(arg0); // TODO check media type too
081    }
082
083    @Override
084    public ExecutionRequest readFrom(Class<ExecutionRequest> arg0, Type arg1, Annotation[] arg2, MediaType arg3,
085            MultivaluedMap<String, String> headers, InputStream in) throws IOException, WebApplicationException {
086        ExecutionRequest req = null;
087        try {
088            List<String> ctypes = headers.get("Content-Type");
089            String ctype = ctypes.get(0);
090            // we need to copy first the stream into a file otherwise it may
091            // happen that
092            // javax.mail fail to receive some parts - I am not sure why -
093            // perhaps the stream is no more available when javax.mail need it?
094            File tmp = Framework.createTempFile("nx-automation-mp-upload-", ".tmp");
095            FileUtils.copyInputStreamToFile(in, tmp);
096            in = new SharedFileInputStream(tmp); // get the input from the saved
097            // file
098            try {
099                MimeMultipart mp = new MimeMultipart(new InputStreamDataSource(in, ctype));
100                BodyPart part = mp.getBodyPart(0); // use content ids
101                InputStream pin = part.getInputStream();
102                JsonParser jp = factory.createJsonParser(pin);
103                req = JsonRequestReader.readRequest(jp, headers, getCoreSession());
104                int cnt = mp.getCount();
105                if (cnt == 2) { // a blob
106                    req.setInput(readBlob(request, mp.getBodyPart(1)));
107                } else if (cnt > 2) { // a blob list
108                    BlobList blobs = new BlobList();
109                    for (int i = 1; i < cnt; i++) {
110                        blobs.add(readBlob(request, mp.getBodyPart(i)));
111                    }
112                    req.setInput(blobs);
113                } else {
114                    log.error("Not all parts received.");
115                    for (int i = 0; i < cnt; i++) {
116                        log.error("Received parts: " + mp.getBodyPart(i).getHeader("Content-ID")[0] + " -> "
117                                + mp.getBodyPart(i).getContentType());
118                    }
119                    throw new NuxeoException(
120                            new IllegalStateException("Received only " + cnt + " part in a multipart request"));
121                }
122            } finally {
123                tmp.delete();
124            }
125        } catch (MessagingException | IOException e) {
126            throw new NuxeoException("Failed to parse multipart request", e);
127        }
128        return req;
129    }
130
131    public static Blob readBlob(HttpServletRequest request, BodyPart part) throws MessagingException, IOException {
132        String ctype = part.getContentType();
133        String fname = part.getFileName();
134        InputStream pin = part.getInputStream();
135        final File tmp = Framework.createTempFile("nx-automation-upload-", ".tmp");
136        FileUtils.copyInputStreamToFile(pin, tmp);
137        Blob blob = Blobs.createBlob(tmp, ctype, null, fname);
138        RequestContext.getActiveContext(request).addRequestCleanupHandler(req -> tmp.delete());
139        return blob;
140    }
141
142}