001/*
002 * (C) Copyright 2006-2011 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.client.jaxrs.spi;
020
021import static org.nuxeo.ecm.automation.client.Constants.CTYPE_AUTOMATION;
022import static org.nuxeo.ecm.automation.client.Constants.CTYPE_ENTITY;
023import static org.nuxeo.ecm.automation.client.Constants.CTYPE_MULTIPART_MIXED;
024
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.io.UnsupportedEncodingException;
030import java.net.URLDecoder;
031import java.util.HashMap;
032import java.util.regex.Matcher;
033import java.util.regex.Pattern;
034
035import javax.mail.BodyPart;
036import javax.mail.MessagingException;
037import javax.mail.internet.MimeMultipart;
038
039import org.nuxeo.ecm.automation.client.RemoteException;
040import org.nuxeo.ecm.automation.client.jaxrs.spi.marshallers.ExceptionMarshaller;
041import org.nuxeo.ecm.automation.client.jaxrs.util.IOUtils;
042import org.nuxeo.ecm.automation.client.jaxrs.util.InputStreamDataSource;
043import org.nuxeo.ecm.automation.client.model.Blob;
044import org.nuxeo.ecm.automation.client.model.Blobs;
045import org.nuxeo.ecm.automation.client.model.FileBlob;
046
047/**
048 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
049 */
050public class Request extends HashMap<String, String> {
051
052    public static final int GET = 0;
053
054    public static final int POST = 1;
055
056    private static final long serialVersionUID = 1L;
057
058    protected static Pattern RFC2231_ATTR_PATTERN = Pattern.compile(";?\\s*filename\\s*\\\\*.*\\*=([^']*)'([^']*)'\\s*([^;]+)\\s*", Pattern.CASE_INSENSITIVE);
059
060    protected static Pattern ATTR_PATTERN = Pattern.compile(";?\\s*filename\\s*=\\s*([^;]+)\\s*",
061            Pattern.CASE_INSENSITIVE);
062
063    protected final int method;
064
065    protected final String url;
066
067    protected final boolean isMultiPart;
068
069    protected Object entity;
070
071    public Request(int method, String url) {
072        this.method = method;
073        this.url = url;
074        isMultiPart = false;
075    }
076
077    public Request(int method, String url, MimeMultipart entity) {
078        this.method = method;
079        this.url = url;
080        this.entity = entity;
081        isMultiPart = true;
082    }
083
084    public Request(int method, String url, String entity) {
085        this.method = method;
086        this.url = url;
087        this.entity = entity;
088        isMultiPart = false;
089    }
090
091    public int getMethod() {
092        return method;
093    }
094
095    public String getUrl() {
096        return url;
097    }
098
099    public Object getEntity() {
100        return entity;
101    }
102
103    public final boolean isMultiPart() {
104        return isMultiPart;
105    }
106
107    public MimeMultipart asMultiPartEntity() {
108        return isMultiPart ? (MimeMultipart) entity : null;
109    }
110
111    public String asStringEntity() {
112        return isMultiPart ? null : (String) entity;
113    }
114
115    /**
116     * Must read the object from the server response and return it or throw a {@link RemoteException} if server sent an
117     * error.
118     */
119    public Object handleResult(int status, String ctype, String disp, InputStream stream) throws RemoteException,
120            IOException {
121        if (status == 204) { // no content
122            return null;
123        } else if (status >= 400) {
124            handleException(status, ctype, stream);
125        }
126        String lctype = ctype.toLowerCase();
127        if (lctype.startsWith(CTYPE_ENTITY)) {
128            return JsonMarshalling.readEntity(IOUtils.read(stream));
129        } else if (lctype.startsWith(CTYPE_AUTOMATION)) {
130            return JsonMarshalling.readRegistry(IOUtils.read(stream));
131        } else if (lctype.startsWith(CTYPE_MULTIPART_MIXED)) { // list of
132                                                               // blobs
133            return readBlobs(ctype, stream);
134        } else { // a blob?
135            String fname = null;
136            if (disp != null) {
137                fname = getFileName(disp);
138            }
139            return readBlob(ctype, fname, stream);
140        }
141    }
142
143    protected static Blobs readBlobs(String ctype, InputStream in) throws IOException {
144        Blobs files = new Blobs();
145        // save the stream to a temporary file
146        File file = IOUtils.copyToTempFile(in);
147        FileInputStream fin = new FileInputStream(file);
148        try {
149            MimeMultipart mp = new MimeMultipart(new InputStreamDataSource(fin, ctype));
150            int size = mp.getCount();
151            for (int i = 0; i < size; i++) {
152                BodyPart part = mp.getBodyPart(i);
153                String fname = part.getFileName();
154                files.add(readBlob(part.getContentType(), fname, part.getInputStream()));
155            }
156        } catch (MessagingException e) {
157            throw new IOException(e);
158        } finally {
159            try {
160                fin.close();
161            } catch (IOException e) {
162            }
163            file.delete();
164        }
165        return files;
166    }
167
168    protected static Blob readBlob(String ctype, String fileName, InputStream in) throws IOException {
169        File file = IOUtils.copyToTempFile(in);
170        FileBlob blob = new FileBlob(file);
171        blob.setMimeType(ctype);
172        if (fileName != null) {
173            blob.setFileName(fileName);
174        }
175        return blob;
176    }
177
178    protected static String getFileName(String ctype) {
179        Matcher m = RFC2231_ATTR_PATTERN.matcher(ctype);
180        if (m.find()) {
181            try {
182                return URLDecoder.decode(m.group(3), m.group(1));
183            } catch (UnsupportedEncodingException e) {
184                throw new RuntimeException(e);
185            }
186        }
187        m = ATTR_PATTERN.matcher(ctype);
188        if (m.find()) {
189            return m.group(1);
190        }
191        return null;
192    }
193
194    protected void handleException(int status, String ctype, InputStream stream) throws RemoteException, IOException {
195        if (CTYPE_ENTITY.equalsIgnoreCase(ctype)) {
196            String content = IOUtils.read(stream);
197            RemoteException e = null;
198            try {
199                e = ExceptionMarshaller.readException(content);
200            } catch (IOException t) {
201                throw new RemoteException(status, "ServerError", "Server Error", content);
202            }
203            throw e;
204        } else {
205            throw new RemoteException(status, "ServerError", "Server Error", IOUtils.read(stream));
206        }
207    }
208
209}