001/* 002 * (C) Copyright 2013 Nuxeo SA (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-2.1.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 * dmetzler 016 */ 017package org.nuxeo.ecm.restapi.server.jaxrs.blob; 018 019import static org.nuxeo.ecm.core.io.download.DownloadService.BLOBHOLDER_PREFIX; 020 021import java.io.Serializable; 022 023import javax.servlet.http.HttpServletRequest; 024import javax.ws.rs.DELETE; 025import javax.ws.rs.GET; 026import javax.ws.rs.PUT; 027import javax.ws.rs.core.Context; 028import javax.ws.rs.core.EntityTag; 029import javax.ws.rs.core.Request; 030import javax.ws.rs.core.Response; 031 032import org.apache.commons.lang.StringUtils; 033import org.nuxeo.ecm.core.api.Blob; 034import org.nuxeo.ecm.core.api.CoreSession; 035import org.nuxeo.ecm.core.api.DocumentModel; 036import org.nuxeo.ecm.core.api.PropertyException; 037import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 038import org.nuxeo.ecm.core.versioning.VersioningService; 039import org.nuxeo.ecm.platform.web.common.ServletHelper; 040import org.nuxeo.ecm.webengine.WebException; 041import org.nuxeo.ecm.webengine.forms.FormData; 042import org.nuxeo.ecm.webengine.model.WebObject; 043import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException; 044import org.nuxeo.ecm.webengine.model.impl.DefaultObject; 045 046/** 047 * @since 5.8 048 */ 049@WebObject(type = "blob") 050public class BlobObject extends DefaultObject { 051 052 private String fieldPath; 053 054 private DocumentModel doc; 055 056 private BlobHolder bh; 057 058 @Override 059 protected void initialize(Object... args) { 060 super.initialize(args); 061 if (args.length == 2) { 062 fieldPath = (String) args[0]; 063 doc = (DocumentModel) args[1]; 064 065 if (fieldPath == null) { 066 if (bh == null && doc.hasSchema("file")) { 067 fieldPath = "file:content"; 068 } else { 069 throw new IllegalArgumentException("No xpath specified and document does not have 'file' schema"); 070 } 071 } else { 072 if (fieldPath.startsWith(BLOBHOLDER_PREFIX)) { 073 bh = doc.getAdapter(BlobHolder.class); 074 if (bh != null) { 075 fieldPath = fieldPath.replace(BLOBHOLDER_PREFIX, ""); 076 } else { 077 throw new WebResourceNotFoundException("No BlobHolder found"); 078 } 079 } 080 } 081 } 082 } 083 084 @Override 085 public <A> A getAdapter(Class<A> adapter) { 086 if (adapter.isAssignableFrom(Blob.class)) { 087 return adapter.cast(getBlob()); 088 } 089 return super.getAdapter(adapter); 090 } 091 092 protected Blob getBlob() { 093 if (bh != null) { 094 if (StringUtils.isBlank(fieldPath) || fieldPath.equals("0")) { 095 return bh.getBlob(); 096 } else { 097 int index = Integer.parseInt(fieldPath); 098 return bh.getBlobs().get(index); 099 } 100 } 101 return (Blob) doc.getPropertyValue(fieldPath); 102 } 103 104 public BlobHolder getBlobHolder() { 105 return bh; 106 } 107 108 @GET 109 public Object doGet(@Context Request request) { 110 Blob blob = getBlob(); 111 if (blob == null) { 112 throw new WebResourceNotFoundException("No attached file at " + fieldPath); 113 } 114 return blob; 115 } 116 117 /** 118 * @deprecated since 7.3. Now returns directly the Blob and use default {@code BlobWriter}. 119 */ 120 @Deprecated 121 public static Response buildResponseFromBlob(Request request, HttpServletRequest httpServletRequest, Blob blob, 122 String filename) { 123 if (filename == null) { 124 filename = blob.getFilename(); 125 } 126 127 String digest = blob.getDigest(); 128 EntityTag etag = digest == null ? null : new EntityTag(digest); 129 if (etag != null) { 130 Response.ResponseBuilder builder = request.evaluatePreconditions(etag); 131 if (builder != null) { 132 return builder.build(); 133 } 134 } 135 String contentDisposition = ServletHelper.getRFC2231ContentDisposition(httpServletRequest, filename); 136 // cached resource did change or no ETag -> serve updated content 137 Response.ResponseBuilder builder = Response.ok(blob).header("Content-Disposition", contentDisposition).type( 138 blob.getMimeType()); 139 if (etag != null) { 140 builder.tag(etag); 141 } 142 return builder.build(); 143 } 144 145 @DELETE 146 public Response doDelete() { 147 if (bh != null) { 148 throw new IllegalArgumentException("Cannot modify a Blob using a BlobHolder"); 149 } 150 try { 151 doc.getProperty(fieldPath).remove(); 152 } catch (PropertyException e) { 153 throw WebException.wrap("Failed to delete attached file into property: " + fieldPath, e); 154 } 155 CoreSession session = ctx.getCoreSession(); 156 session.saveDocument(doc); 157 session.save(); 158 return Response.noContent().build(); 159 } 160 161 @PUT 162 public Response doPut() { 163 FormData form = ctx.getForm(); 164 Blob blob = form.getFirstBlob(); 165 if (blob == null) { 166 throw new IllegalArgumentException("Could not find any uploaded file"); 167 } 168 if (bh != null) { 169 throw new IllegalArgumentException("Cannot modify a Blob using a BlobHolder"); 170 } 171 try { 172 doc.setPropertyValue(fieldPath, (Serializable) blob); 173 } catch (PropertyException e) { 174 throw WebException.wrap("Failed to attach file", e); 175 } 176 // make snapshot 177 doc.putContextData(VersioningService.VERSIONING_OPTION, form.getVersioningOption()); 178 CoreSession session = ctx.getCoreSession(); 179 session.saveDocument(doc); 180 session.save(); 181 return Response.ok("blob updated").build(); 182 } 183 184}