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