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