001/* 002 * (C) Copyright 2014-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 * Nuxeo - initial API and implementation 018 * vpasquier <vpasquier@nuxeo.com> 019 * 020 */ 021package org.nuxeo.ecm.automation.server.jaxrs.batch; 022 023import java.io.ByteArrayOutputStream; 024import java.io.IOException; 025import java.io.InputStream; 026import java.net.URLDecoder; 027import java.util.ArrayList; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031 032import javax.mail.MessagingException; 033import javax.servlet.http.HttpServletRequest; 034import javax.servlet.http.HttpServletResponse; 035import javax.ws.rs.GET; 036import javax.ws.rs.POST; 037import javax.ws.rs.Path; 038import javax.ws.rs.PathParam; 039import javax.ws.rs.Produces; 040import javax.ws.rs.core.Context; 041import javax.ws.rs.core.MediaType; 042import javax.ws.rs.core.Response; 043import javax.ws.rs.core.Response.Status; 044 045import org.apache.commons.lang.StringUtils; 046import org.apache.commons.logging.Log; 047import org.apache.commons.logging.LogFactory; 048import org.codehaus.jackson.map.ObjectMapper; 049import org.nuxeo.ecm.automation.OperationContext; 050import org.nuxeo.ecm.automation.jaxrs.io.operations.ExecutionRequest; 051import org.nuxeo.ecm.automation.server.jaxrs.ResponseHelper; 052import org.nuxeo.ecm.core.api.Blob; 053import org.nuxeo.ecm.core.api.CoreSession; 054import org.nuxeo.ecm.core.api.NuxeoException; 055import org.nuxeo.ecm.webengine.WebException; 056import org.nuxeo.ecm.webengine.forms.FormData; 057import org.nuxeo.ecm.webengine.jaxrs.context.RequestCleanupHandler; 058import org.nuxeo.ecm.webengine.jaxrs.context.RequestContext; 059import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory; 060import org.nuxeo.ecm.webengine.model.WebObject; 061import org.nuxeo.ecm.webengine.model.impl.AbstractResource; 062import org.nuxeo.ecm.webengine.model.impl.ResourceTypeImpl; 063import org.nuxeo.runtime.api.Framework; 064import org.nuxeo.runtime.services.config.ConfigurationService; 065import org.nuxeo.runtime.transaction.TransactionHelper; 066 067/** 068 * Exposes {@link Batch} as a JAX-RS resource 069 * 070 * @deprecated since 7.4, use {@link org.nuxeo.ecm.restapi.server.jaxrs.BatchUploadObject} instead. 071 * @author Tiry (tdelprat@nuxeo.com) 072 * @author Antoine Taillefer 073 */ 074@Deprecated 075@WebObject(type = "batch") 076public class BatchResource extends AbstractResource<ResourceTypeImpl> { 077 078 private static final String REQUEST_BATCH_ID = "batchId"; 079 080 private static final String REQUEST_FILE_IDX = "fileIdx"; 081 082 protected static final Log log = LogFactory.getLog(BatchResource.class); 083 084 public CoreSession getCoreSession(HttpServletRequest request) { 085 return SessionFactory.getSession(request); 086 } 087 088 protected Response buildFromString(String message) { 089 return Response.ok(message, MediaType.APPLICATION_JSON).header("Content-Length", message.length()).build(); 090 } 091 092 protected Response buildHtmlFromString(String message) { 093 message = "<html>" + message + "</html>"; 094 return Response.ok(message, MediaType.TEXT_HTML_TYPE).header("Content-Length", message.length()).build(); 095 } 096 097 protected Response buildFromMap(Map<String, String> map) throws IOException { 098 return buildFromMap(map, false); 099 } 100 101 protected Response buildFromMap(Map<String, String> map, boolean html) throws IOException { 102 ObjectMapper mapper = new ObjectMapper(); 103 ByteArrayOutputStream out = new ByteArrayOutputStream(128); 104 mapper.writeValue(out, map); 105 String result = out.toString("UTF-8"); 106 if (html) { 107 // for msie with iframe transport : we need to return html ! 108 return buildHtmlFromString(result); 109 } else { 110 return buildFromString(result); 111 } 112 } 113 114 /** 115 * @deprecated since 7.4, use {@link BatchUploadObject#upload(HttpServletRequest, String, String)} instead. 116 */ 117 @Deprecated 118 @POST 119 @Path("/upload") 120 public Object doPost(@Context HttpServletRequest request) throws IOException { 121 TransactionHelper.commitOrRollbackTransaction(); 122 try { 123 return uploadNoTransaction(request); 124 } finally { 125 TransactionHelper.startTransaction(); 126 } 127 } 128 129 protected Object uploadNoTransaction(@Context HttpServletRequest request) throws IOException { 130 boolean useIFrame = false; 131 132 // Parameters are passed as request header 133 // the request body is the stream 134 String fileName = request.getHeader("X-File-Name"); 135 String fileSize = request.getHeader("X-File-Size"); 136 String batchId = request.getHeader("X-Batch-Id"); 137 String mimeType = request.getHeader("X-File-Type"); 138 String idx = request.getHeader("X-File-Idx"); 139 InputStream is = null; 140 141 // handle multipart case : mainly MSIE with jQueryFileupload 142 String contentType = request.getHeader("Content-Type"); 143 if (contentType != null && contentType.contains("multipart")) { 144 useIFrame = true; 145 FormData formData = new FormData(request); 146 if (formData.getString("batchId") != null) { 147 batchId = formData.getString("batchId"); 148 } 149 if (formData.getString("fileIdx") != null) { 150 idx = formData.getString("fileIdx"); 151 } 152 if (idx == null || "".equals(idx.trim())) { 153 idx = "0"; 154 } 155 Blob blob = formData.getFirstBlob(); 156 if (blob != null) { 157 is = blob.getStream(); 158 fileName = blob.getFilename(); 159 mimeType = blob.getMimeType(); 160 } 161 } else { 162 fileName = URLDecoder.decode(fileName, "UTF-8"); 163 is = request.getInputStream(); 164 } 165 166 log.debug("Uploading " + fileName + " (" + fileSize + "b)"); 167 BatchManager bm = Framework.getLocalService(BatchManager.class); 168 if (StringUtils.isEmpty(batchId)) { 169 batchId = bm.initBatch(); 170 } else if (!bm.hasBatch(batchId)) { 171 if (!Framework.getService(ConfigurationService.class) 172 .isBooleanPropertyTrue(BatchManagerComponent.CLIENT_BATCH_ID_FLAG)) { 173 String errorMsg = String.format( 174 "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)", 175 BatchManagerComponent.CLIENT_BATCH_ID_FLAG); 176 return Response.status(Status.INTERNAL_SERVER_ERROR) 177 .entity("{\"error\" : \"" + errorMsg + "\"}") 178 .build(); 179 } else { 180 log.warn(String.format( 181 "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", 182 BatchManagerComponent.CLIENT_BATCH_ID_FLAG)); 183 } 184 } 185 bm.addStream(batchId, idx, is, fileName, mimeType); 186 187 Map<String, String> result = new HashMap<>(); 188 result.put("batchId", batchId); 189 result.put("uploaded", "true"); 190 return buildFromMap(result, useIFrame); 191 } 192 193 @Deprecated 194 @POST 195 @Produces("application/json") 196 @Path("/execute") 197 public Object exec(@Context HttpServletRequest request, @Context HttpServletResponse response, ExecutionRequest xreq) { 198 Map<String, Object> params = xreq.getParams(); 199 String batchId = (String) params.get(REQUEST_BATCH_ID); 200 String fileIdx = (String) params.get(REQUEST_FILE_IDX); 201 String operationId = (String) params.get("operationId"); 202 params.remove(REQUEST_BATCH_ID); 203 params.remove("operationId"); 204 205 final BatchManager bm = Framework.getLocalService(BatchManager.class); 206 // register commit hook for cleanup 207 request.setAttribute(REQUEST_BATCH_ID, batchId); 208 RequestContext.getActiveContext(request).addRequestCleanupHandler(req -> { 209 String bid = (String) req.getAttribute(REQUEST_BATCH_ID); 210 bm.clean(bid); 211 }); 212 213 try { 214 CoreSession session = getCoreSession(request); 215 OperationContext ctx = xreq.createContext(request, response, session); 216 217 Object result; 218 if (StringUtils.isEmpty(fileIdx)) { 219 result = bm.execute(batchId, operationId, session, ctx, params); 220 } else { 221 result = bm.execute(batchId, fileIdx, operationId, session, ctx, params); 222 } 223 return ResponseHelper.getResponse(result, request); 224 } catch (NuxeoException | MessagingException | IOException e) { 225 log.error("Error while executing automation batch ", e); 226 if (WebException.isSecurityError(e)) { 227 return Response.status(Status.FORBIDDEN).entity("{\"error\" : \"" + e.getMessage() + "\"}").build(); 228 } else { 229 return Response.status(Status.INTERNAL_SERVER_ERROR) 230 .entity("{\"error\" : \"" + e.getMessage() + "\"}") 231 .build(); 232 } 233 } 234 } 235 236 /** 237 * @deprecated since 7.4, use {@link BatchUploadObject#getBatchInfo(String)} instead. 238 */ 239 @Deprecated 240 @GET 241 @Path("/files/{batchId}") 242 public Object getFilesBatch(@PathParam(REQUEST_BATCH_ID) String batchId) throws IOException { 243 BatchManager bm = Framework.getLocalService(BatchManager.class); 244 List<Blob> blobs = bm.getBlobs(batchId); 245 246 List<Map<String, Object>> result = new ArrayList<>(); 247 for (Blob blob : blobs) { 248 Map<String, Object> map = new HashMap<>(); 249 map.put("name", blob.getFilename()); 250 map.put("size", blob.getLength()); 251 result.add(map); 252 } 253 254 ObjectMapper mapper = new ObjectMapper(); 255 ByteArrayOutputStream out = new ByteArrayOutputStream(128); 256 mapper.writeValue(out, result); 257 return buildFromString(out.toString("UTF-8")); 258 } 259 260 /** 261 * @deprecated since 7.4, use {@link BatchUploadObject#dropBatch(String)} instead. 262 */ 263 @Deprecated 264 @GET 265 @Path("/drop/{batchId}") 266 public Object dropBatch(@PathParam(REQUEST_BATCH_ID) String batchId) throws IOException { 267 BatchManager bm = Framework.getLocalService(BatchManager.class); 268 bm.clean(batchId); 269 270 Map<String, String> result = new HashMap<>(); 271 result.put("batchId", batchId); 272 result.put("dropped", "true"); 273 return buildFromMap(result); 274 } 275 276}