001/* 002 * (C) Copyright 2014 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * vpasquier <vpasquier@nuxeo.com> 017 * 018 */ 019package org.nuxeo.ecm.automation.server.jaxrs.batch; 020 021import java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.net.URLDecoder; 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import javax.mail.MessagingException; 031import javax.servlet.http.HttpServletRequest; 032import javax.ws.rs.GET; 033import javax.ws.rs.POST; 034import javax.ws.rs.Path; 035import javax.ws.rs.PathParam; 036import javax.ws.rs.Produces; 037import javax.ws.rs.core.Context; 038import javax.ws.rs.core.MediaType; 039import javax.ws.rs.core.Response; 040import javax.ws.rs.core.Response.Status; 041 042import org.apache.commons.lang.StringUtils; 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045import org.codehaus.jackson.map.ObjectMapper; 046import org.nuxeo.ecm.automation.OperationContext; 047import org.nuxeo.ecm.automation.jaxrs.io.operations.ExecutionRequest; 048import org.nuxeo.ecm.automation.server.jaxrs.ResponseHelper; 049import org.nuxeo.ecm.core.api.Blob; 050import org.nuxeo.ecm.core.api.CoreSession; 051import org.nuxeo.ecm.core.api.NuxeoException; 052import org.nuxeo.ecm.webengine.WebException; 053import org.nuxeo.ecm.webengine.forms.FormData; 054import org.nuxeo.ecm.webengine.jaxrs.context.RequestCleanupHandler; 055import org.nuxeo.ecm.webengine.jaxrs.context.RequestContext; 056import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory; 057import org.nuxeo.ecm.webengine.model.WebObject; 058import org.nuxeo.ecm.webengine.model.impl.AbstractResource; 059import org.nuxeo.ecm.webengine.model.impl.ResourceTypeImpl; 060import org.nuxeo.runtime.api.Framework; 061 062/** 063 * Exposes {@link Batch} as a JAX-RS resource 064 * 065 * @author Tiry (tdelprat@nuxeo.com) 066 * @author Antoine Taillefer 067 */ 068@WebObject(type = "batch") 069public class BatchResource extends AbstractResource<ResourceTypeImpl> { 070 071 private static final String REQUEST_BATCH_ID = "batchId"; 072 073 private static final String REQUEST_FILE_IDX = "fileIdx"; 074 075 protected static final Log log = LogFactory.getLog(BatchResource.class); 076 077 public CoreSession getCoreSession(HttpServletRequest request) { 078 return SessionFactory.getSession(request); 079 } 080 081 protected Response buildFromString(String message) { 082 return Response.ok(message, MediaType.APPLICATION_JSON).header("Content-Length", message.length()).build(); 083 } 084 085 protected Response buildHtmlFromString(String message) { 086 message = "<html>" + message + "</html>"; 087 return Response.ok(message, MediaType.TEXT_HTML_TYPE).header("Content-Length", message.length()).build(); 088 } 089 090 protected Response buildFromMap(Map<String, String> map) throws IOException { 091 return buildFromMap(map, false); 092 } 093 094 protected Response buildFromMap(Map<String, String> map, boolean html) throws IOException { 095 ObjectMapper mapper = new ObjectMapper(); 096 ByteArrayOutputStream out = new ByteArrayOutputStream(128); 097 mapper.writeValue(out, map); 098 String result = out.toString("UTF-8"); 099 if (html) { 100 // for msie with iframe transport : we need to return html ! 101 return buildHtmlFromString(result); 102 } else { 103 return buildFromString(result); 104 } 105 } 106 107 /** 108 * @deprecated since 5.7.2. The timeout is managed by the {@link BatchManager#execute} method. 109 */ 110 @Deprecated 111 protected int getUploadWaitTimeout() { 112 String t = Framework.getProperty("org.nuxeo.batch.upload.wait.timeout", "5"); 113 return Integer.parseInt(t); 114 } 115 116 /** 117 * @deprecated since 7.4, use {@link BatchUploadObject#upload(HttpServletRequest, String, String)} instead. 118 */ 119 @Deprecated 120 @POST 121 @Path("/upload") 122 public Object doPost(@Context HttpServletRequest request) throws IOException { 123 124 boolean useIFrame = false; 125 126 // Parameters are passed as request header 127 // the request body is the stream 128 String fileName = request.getHeader("X-File-Name"); 129 String fileSize = request.getHeader("X-File-Size"); 130 String batchId = request.getHeader("X-Batch-Id"); 131 String mimeType = request.getHeader("X-File-Type"); 132 String idx = request.getHeader("X-File-Idx"); 133 InputStream is = null; 134 135 // handle multipart case : mainly MSIE with jQueryFileupload 136 String contentType = request.getHeader("Content-Type"); 137 if (contentType != null && contentType.contains("multipart")) { 138 useIFrame = true; 139 FormData formData = new FormData(request); 140 if (formData.getString("batchId") != null) { 141 batchId = formData.getString("batchId"); 142 } 143 if (formData.getString("fileIdx") != null) { 144 idx = formData.getString("fileIdx"); 145 } 146 if (idx == null || "".equals(idx.trim())) { 147 idx = "0"; 148 } 149 Blob blob = formData.getFirstBlob(); 150 if (blob != null) { 151 is = blob.getStream(); 152 fileName = blob.getFilename(); 153 mimeType = blob.getMimeType(); 154 } 155 } else { 156 fileName = URLDecoder.decode(fileName, "UTF-8"); 157 is = request.getInputStream(); 158 } 159 160 log.debug("Uploading " + fileName + " (" + fileSize + "b)"); 161 BatchManager bm = Framework.getLocalService(BatchManager.class); 162 // TODO https://jira.nuxeo.com/browse/NXP-16953 163 // Use flag to allow or not providing a client-side generated batch id 164 bm.addStream(batchId, idx, is, fileName, mimeType); 165 166 Map<String, String> result = new HashMap<String, String>(); 167 result.put("batchId", batchId); 168 result.put("uploaded", "true"); 169 return buildFromMap(result, useIFrame); 170 } 171 172 @POST 173 @Produces("application/json") 174 @Path("/execute") 175 public Object exec(@Context HttpServletRequest request, ExecutionRequest xreq) { 176 177 Map<String, Object> params = xreq.getParams(); 178 String batchId = (String) params.get(REQUEST_BATCH_ID); 179 String fileIdx = (String) params.get(REQUEST_FILE_IDX); 180 String operationId = (String) params.get("operationId"); 181 params.remove(REQUEST_BATCH_ID); 182 params.remove("operationId"); 183 184 final BatchManager bm = Framework.getLocalService(BatchManager.class); 185 // register commit hook for cleanup 186 request.setAttribute(REQUEST_BATCH_ID, batchId); 187 RequestContext.getActiveContext(request).addRequestCleanupHandler(new RequestCleanupHandler() { 188 @Override 189 public void cleanup(HttpServletRequest req) { 190 String bid = (String) req.getAttribute(REQUEST_BATCH_ID); 191 bm.clean(bid); 192 } 193 194 }); 195 196 try { 197 OperationContext ctx = xreq.createContext(request, getCoreSession(request)); 198 199 Object result; 200 if (StringUtils.isEmpty(fileIdx)) { 201 result = bm.execute(batchId, operationId, getCoreSession(request), ctx, params); 202 } else { 203 result = bm.execute(batchId, fileIdx, operationId, getCoreSession(request), ctx, params); 204 } 205 return ResponseHelper.getResponse(result, request); 206 } catch (NuxeoException | MessagingException | IOException e) { 207 log.error("Error while executing automation batch ", e); 208 if (WebException.isSecurityError(e)) { 209 return Response.status(Status.FORBIDDEN).entity("{\"error\" : \"" + e.getMessage() + "\"}").build(); 210 } else { 211 return Response.status(Status.INTERNAL_SERVER_ERROR).entity("{\"error\" : \"" + e.getMessage() + "\"}").build(); 212 } 213 } 214 } 215 216 /** 217 * @deprecated since 7.4, use {@link BatchUploadObject#getBatchInfo(String)} instead. 218 */ 219 @Deprecated 220 @GET 221 @Path("/files/{batchId}") 222 public Object getFilesBatch(@PathParam(REQUEST_BATCH_ID) String batchId) throws IOException { 223 BatchManager bm = Framework.getLocalService(BatchManager.class); 224 List<Blob> blobs = bm.getBlobs(batchId); 225 226 List<Map<String, Object>> result = new ArrayList<>(); 227 for (Blob blob : blobs) { 228 Map<String, Object> map = new HashMap<>(); 229 map.put("name", blob.getFilename()); 230 map.put("size", blob.getLength()); 231 result.add(map); 232 } 233 234 ObjectMapper mapper = new ObjectMapper(); 235 ByteArrayOutputStream out = new ByteArrayOutputStream(128); 236 mapper.writeValue(out, result); 237 return buildFromString(out.toString("UTF-8")); 238 } 239 240 /** 241 * @deprecated since 7.4, use {@link BatchUploadObject#dropBatch(String)} instead. 242 */ 243 @Deprecated 244 @GET 245 @Path("/drop/{batchId}") 246 public Object dropBatch(@PathParam(REQUEST_BATCH_ID) String batchId) throws IOException { 247 BatchManager bm = Framework.getLocalService(BatchManager.class); 248 bm.clean(batchId); 249 250 Map<String, String> result = new HashMap<String, String>(); 251 result.put("batchId", batchId); 252 result.put("dropped", "true"); 253 return buildFromMap(result); 254 } 255 256}