/*
* (C) Copyright 2014-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:
* Nuxeo - initial API and implementation
* vpasquier <vpasquier@nuxeo.com>
*
*/
package org.nuxeo.ecm.automation.server.jaxrs.batch;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.nuxeo.ecm.automation.OperationContext;
import org.nuxeo.ecm.automation.jaxrs.io.operations.ExecutionRequest;
import org.nuxeo.ecm.automation.server.jaxrs.ResponseHelper;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.webengine.WebException;
import org.nuxeo.ecm.webengine.forms.FormData;
import org.nuxeo.ecm.webengine.jaxrs.context.RequestCleanupHandler;
import org.nuxeo.ecm.webengine.jaxrs.context.RequestContext;
import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory;
import org.nuxeo.ecm.webengine.model.WebObject;
import org.nuxeo.ecm.webengine.model.impl.AbstractResource;
import org.nuxeo.ecm.webengine.model.impl.ResourceTypeImpl;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.services.config.ConfigurationService;
import org.nuxeo.runtime.transaction.TransactionHelper;
/**
* Exposes {@link Batch} as a JAX-RS resource
*
* @deprecated since 7.4, use {@link org.nuxeo.ecm.restapi.server.jaxrs.BatchUploadObject} instead.
* @author Tiry (tdelprat@nuxeo.com)
* @author Antoine Taillefer
*/
@Deprecated
@WebObject(type = "batch")
public class BatchResource extends AbstractResource<ResourceTypeImpl> {
private static final String REQUEST_BATCH_ID = "batchId";
private static final String REQUEST_FILE_IDX = "fileIdx";
protected static final Log log = LogFactory.getLog(BatchResource.class);
public CoreSession getCoreSession(HttpServletRequest request) {
return SessionFactory.getSession(request);
}
protected Response buildFromString(String message) {
return Response.ok(message, MediaType.APPLICATION_JSON).header("Content-Length", message.length()).build();
}
protected Response buildHtmlFromString(String message) {
message = "<html>" + message + "</html>";
return Response.ok(message, MediaType.TEXT_HTML_TYPE).header("Content-Length", message.length()).build();
}
protected Response buildFromMap(Map<String, String> map) throws IOException {
return buildFromMap(map, false);
}
protected Response buildFromMap(Map<String, String> map, boolean html) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ByteArrayOutputStream out = new ByteArrayOutputStream(128);
mapper.writeValue(out, map);
String result = out.toString("UTF-8");
if (html) {
// for msie with iframe transport : we need to return html !
return buildHtmlFromString(result);
} else {
return buildFromString(result);
}
}
/**
* @deprecated since 7.4, use {@link BatchUploadObject#upload(HttpServletRequest, String, String)} instead.
*/
@Deprecated
@POST
@Path("/upload")
public Object doPost(@Context HttpServletRequest request) throws IOException {
TransactionHelper.commitOrRollbackTransaction();
try {
return uploadNoTransaction(request);
} finally {
TransactionHelper.startTransaction();
}
}
protected Object uploadNoTransaction(@Context HttpServletRequest request) throws IOException {
boolean useIFrame = false;
// Parameters are passed as request header
// the request body is the stream
String fileName = request.getHeader("X-File-Name");
String fileSize = request.getHeader("X-File-Size");
String batchId = request.getHeader("X-Batch-Id");
String mimeType = request.getHeader("X-File-Type");
String idx = request.getHeader("X-File-Idx");
InputStream is = null;
// handle multipart case : mainly MSIE with jQueryFileupload
String contentType = request.getHeader("Content-Type");
if (contentType != null && contentType.contains("multipart")) {
useIFrame = true;
FormData formData = new FormData(request);
if (formData.getString("batchId") != null) {
batchId = formData.getString("batchId");
}
if (formData.getString("fileIdx") != null) {
idx = formData.getString("fileIdx");
}
if (idx == null || "".equals(idx.trim())) {
idx = "0";
}
Blob blob = formData.getFirstBlob();
if (blob != null) {
is = blob.getStream();
fileName = blob.getFilename();
mimeType = blob.getMimeType();
}
} else {
fileName = URLDecoder.decode(fileName, "UTF-8");
is = request.getInputStream();
}
log.debug("Uploading " + fileName + " (" + fileSize + "b)");
BatchManager bm = Framework.getLocalService(BatchManager.class);
if (StringUtils.isEmpty(batchId)) {
batchId = bm.initBatch();
} else if (!bm.hasBatch(batchId)) {
if (!Framework.getService(ConfigurationService.class)
.isBooleanPropertyTrue(BatchManagerComponent.CLIENT_BATCH_ID_FLAG)) {
String errorMsg = String.format(
"Cannot upload a file with a client-side generated batch id, please use new upload API or set configuration property %s to true (not recommended)",
BatchManagerComponent.CLIENT_BATCH_ID_FLAG);
return Response.status(Status.INTERNAL_SERVER_ERROR)
.entity("{\"error\" : \"" + errorMsg + "\"}")
.build();
} else {
log.warn(String.format(
"Allowing to initialize upload batch with a client-side generated id since configuration property %s is set to true but this is not recommended, please use new upload API instead",
BatchManagerComponent.CLIENT_BATCH_ID_FLAG));
}
}
bm.addStream(batchId, idx, is, fileName, mimeType);
Map<String, String> result = new HashMap<>();
result.put("batchId", batchId);
result.put("uploaded", "true");
return buildFromMap(result, useIFrame);
}
@Deprecated
@POST
@Produces("application/json")
@Path("/execute")
public Object exec(@Context HttpServletRequest request, @Context HttpServletResponse response, ExecutionRequest xreq) {
Map<String, Object> params = xreq.getParams();
String batchId = (String) params.get(REQUEST_BATCH_ID);
String fileIdx = (String) params.get(REQUEST_FILE_IDX);
String operationId = (String) params.get("operationId");
params.remove(REQUEST_BATCH_ID);
params.remove("operationId");
final BatchManager bm = Framework.getLocalService(BatchManager.class);
// register commit hook for cleanup
request.setAttribute(REQUEST_BATCH_ID, batchId);
RequestContext.getActiveContext(request).addRequestCleanupHandler(req -> {
String bid = (String) req.getAttribute(REQUEST_BATCH_ID);
bm.clean(bid);
});
try {
CoreSession session = getCoreSession(request);
OperationContext ctx = xreq.createContext(request, response, session);
Object result;
if (StringUtils.isEmpty(fileIdx)) {
result = bm.execute(batchId, operationId, session, ctx, params);
} else {
result = bm.execute(batchId, fileIdx, operationId, session, ctx, params);
}
return ResponseHelper.getResponse(result, request);
} catch (NuxeoException | MessagingException | IOException e) {
log.error("Error while executing automation batch ", e);
if (WebException.isSecurityError(e)) {
return Response.status(Status.FORBIDDEN).entity("{\"error\" : \"" + e.getMessage() + "\"}").build();
} else {
return Response.status(Status.INTERNAL_SERVER_ERROR)
.entity("{\"error\" : \"" + e.getMessage() + "\"}")
.build();
}
}
}
/**
* @deprecated since 7.4, use {@link BatchUploadObject#getBatchInfo(String)} instead.
*/
@Deprecated
@GET
@Path("/files/{batchId}")
public Object getFilesBatch(@PathParam(REQUEST_BATCH_ID) String batchId) throws IOException {
BatchManager bm = Framework.getLocalService(BatchManager.class);
List<Blob> blobs = bm.getBlobs(batchId);
List<Map<String, Object>> result = new ArrayList<>();
for (Blob blob : blobs) {
Map<String, Object> map = new HashMap<>();
map.put("name", blob.getFilename());
map.put("size", blob.getLength());
result.add(map);
}
ObjectMapper mapper = new ObjectMapper();
ByteArrayOutputStream out = new ByteArrayOutputStream(128);
mapper.writeValue(out, result);
return buildFromString(out.toString("UTF-8"));
}
/**
* @deprecated since 7.4, use {@link BatchUploadObject#dropBatch(String)} instead.
*/
@Deprecated
@GET
@Path("/drop/{batchId}")
public Object dropBatch(@PathParam(REQUEST_BATCH_ID) String batchId) throws IOException {
BatchManager bm = Framework.getLocalService(BatchManager.class);
bm.clean(batchId);
Map<String, String> result = new HashMap<>();
result.put("batchId", batchId);
result.put("dropped", "true");
return buildFromMap(result);
}
}