/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* bstefanescu
*/
package org.nuxeo.ecm.automation.client.jaxrs.spi;
import static org.nuxeo.ecm.automation.client.Constants.CTYPE_AUTOMATION;
import static org.nuxeo.ecm.automation.client.Constants.CTYPE_ENTITY;
import static org.nuxeo.ecm.automation.client.Constants.CTYPE_MULTIPART_EMPTY;
import static org.nuxeo.ecm.automation.client.Constants.CTYPE_MULTIPART_MIXED;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMultipart;
import javax.ws.rs.core.Response;
import org.nuxeo.ecm.automation.client.RemoteException;
import org.nuxeo.ecm.automation.client.jaxrs.spi.marshallers.ExceptionMarshaller;
import org.nuxeo.ecm.automation.client.jaxrs.util.IOUtils;
import org.nuxeo.ecm.automation.client.jaxrs.util.InputStreamDataSource;
import org.nuxeo.ecm.automation.client.model.Blob;
import org.nuxeo.ecm.automation.client.model.Blobs;
import org.nuxeo.ecm.automation.client.model.FileBlob;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class Request extends HashMap<String, String> {
public static final int GET = 0;
public static final int POST = 1;
private static final long serialVersionUID = 1L;
protected static Pattern RFC2231_ATTR_PATTERN = Pattern.compile(
";?\\s*filename\\s*\\\\*.*\\*=([^']*)'([^']*)'\\s*([^;]+)\\s*", Pattern.CASE_INSENSITIVE);
protected static Pattern ATTR_PATTERN = Pattern.compile(";?\\s*filename\\s*=\\s*([^;]+)\\s*",
Pattern.CASE_INSENSITIVE);
protected final int method;
protected final String url;
protected final boolean isMultiPart;
protected Object entity;
public Request(int method, String url) {
this.method = method;
this.url = url;
isMultiPart = false;
}
public Request(int method, String url, MimeMultipart entity) {
this.method = method;
this.url = url;
this.entity = entity;
isMultiPart = true;
}
public Request(int method, String url, String entity) {
this.method = method;
this.url = url;
this.entity = entity;
isMultiPart = false;
}
public int getMethod() {
return method;
}
public String getUrl() {
return url;
}
public Object getEntity() {
return entity;
}
public final boolean isMultiPart() {
return isMultiPart;
}
public MimeMultipart asMultiPartEntity() {
return isMultiPart ? (MimeMultipart) entity : null;
}
public String asStringEntity() {
return isMultiPart ? null : (String) entity;
}
/**
* Must read the object from the server response and return it or throw a {@link RemoteException} if server sent an
* error.
*/
public Object handleResult(int status, String ctype, String disp, InputStream stream)
throws RemoteException, IOException {
// Specific http status handling
if (status >= Response.Status.BAD_REQUEST.getStatusCode()) {
handleException(status, ctype, stream);
} else if (status == Response.Status.NO_CONTENT.getStatusCode() || stream == null) {
if (ctype != null && ctype.toLowerCase().startsWith(CTYPE_MULTIPART_EMPTY)) {
// empty entity and content type of nuxeo empty list
return new Blobs();
}
// no content
return null;
}
// Check content type
if (ctype == null) {
if (status != Response.Status.OK.getStatusCode()) {
// this may happen when login failed
throw new RemoteException(status, "ServerError", "Server Error", "");
}
// cannot handle responses with no content type
return null;
}
// Handle result
String lctype = ctype.toLowerCase();
if (lctype.startsWith(CTYPE_ENTITY)) {
return JsonMarshalling.readEntity(IOUtils.read(stream));
} else if (lctype.startsWith(CTYPE_AUTOMATION)) {
return JsonMarshalling.readRegistry(IOUtils.read(stream));
} else if (lctype.startsWith(CTYPE_MULTIPART_MIXED)) { // list of
// blobs
return readBlobs(ctype, stream);
} else { // a blob?
String fname = null;
if (disp != null) {
fname = getFileName(disp);
}
return readBlob(ctype, fname, stream);
}
}
protected static Blobs readBlobs(String ctype, InputStream in) throws IOException {
Blobs files = new Blobs();
// save the stream to a temporary file
File file = IOUtils.copyToTempFile(in);
try (FileInputStream fin = new FileInputStream(file)) {
MimeMultipart mp = new MimeMultipart(new InputStreamDataSource(fin, ctype));
int size = mp.getCount();
for (int i = 0; i < size; i++) {
BodyPart part = mp.getBodyPart(i);
String fname = part.getFileName();
files.add(readBlob(part.getContentType(), fname, part.getInputStream()));
}
} catch (MessagingException e) {
throw new IOException(e);
} finally {
file.delete();
}
return files;
}
protected static Blob readBlob(String ctype, String fileName, InputStream in) throws IOException {
File file = IOUtils.copyToTempFile(in);
FileBlob blob = new FileBlob(file);
blob.setMimeType(ctype);
if (fileName != null) {
blob.setFileName(fileName);
}
return blob;
}
protected static String getFileName(String ctype) {
Matcher m = RFC2231_ATTR_PATTERN.matcher(ctype);
if (m.find()) {
try {
return URLDecoder.decode(m.group(3), m.group(1));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
m = ATTR_PATTERN.matcher(ctype);
if (m.find()) {
return m.group(1);
}
return null;
}
protected void handleException(int status, String ctype, InputStream stream) throws RemoteException, IOException {
if (stream == null) {
throw new RemoteException(status, "ServerError", "Server Error", "");
} else if (CTYPE_ENTITY.equalsIgnoreCase(ctype)) {
String content = IOUtils.read(stream);
RemoteException e;
try {
e = ExceptionMarshaller.readException(content);
} catch (IOException t) {
throw new RemoteException(status, "ServerError", "Server Error", content);
}
throw e;
} else {
throw new RemoteException(status, "ServerError", "Server Error", IOUtils.read(stream));
}
}
}