001/* 002 * (C) Copyright 2006-2008 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 * bstefanescu 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.core.rest; 021 022import java.io.Serializable; 023import java.util.HashMap; 024import java.util.Map; 025 026import javax.ws.rs.DELETE; 027import javax.ws.rs.GET; 028import javax.ws.rs.POST; 029import javax.ws.rs.Path; 030import javax.ws.rs.core.Context; 031import javax.ws.rs.core.EntityTag; 032import javax.ws.rs.core.Request; 033import javax.ws.rs.core.Response; 034import javax.ws.rs.core.Response.ResponseBuilder; 035 036import org.nuxeo.ecm.core.api.Blob; 037import org.nuxeo.ecm.core.api.CoreSession; 038import org.nuxeo.ecm.core.api.DocumentModel; 039import org.nuxeo.ecm.core.api.NuxeoException; 040import org.nuxeo.ecm.core.api.PropertyException; 041import org.nuxeo.ecm.core.api.model.Property; 042import org.nuxeo.ecm.core.versioning.VersioningService; 043import org.nuxeo.ecm.platform.web.common.ServletHelper; 044import org.nuxeo.ecm.webengine.WebException; 045import org.nuxeo.ecm.webengine.forms.FormData; 046import org.nuxeo.ecm.webengine.model.WebAdapter; 047import org.nuxeo.ecm.webengine.model.exceptions.IllegalParameterException; 048import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException; 049import org.nuxeo.ecm.webengine.model.impl.DefaultAdapter; 050 051/** 052 * File Service - manages attachments to a document. 053 * <p> 054 * Accepts the following methods: 055 * <ul> 056 * <li>GET - get the attached file 057 * <li>POST - create an attachment 058 * </ul> 059 * 060 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 061 */ 062@WebAdapter(name = "file", type = "FileService", targetType = "Document") 063public class FileService extends DefaultAdapter { 064 065 @GET 066 public Response doGet(@Context Request request) { 067 DocumentModel doc = getTarget().getAdapter(DocumentModel.class); 068 FormData form = ctx.getForm(); 069 String xpath = form.getString(FormData.PROPERTY); 070 if (xpath == null) { 071 if (doc.hasSchema("file")) { 072 xpath = "file:content"; 073 } else { 074 throw new IllegalParameterException( 075 "Missing request parameter named 'property' that specify the blob property xpath to fetch"); 076 } 077 } 078 try { 079 Property p = doc.getProperty(xpath); 080 Blob blob = (Blob) p.getValue(); 081 if (blob == null) { 082 throw new WebResourceNotFoundException("No attached file at " + xpath); 083 } 084 085 String fileName = blob.getFilename(); 086 if (fileName == null) { 087 p = p.getParent(); 088 if (p.isComplex()) { // special handling for file and files 089 // schema 090 try { 091 fileName = (String) p.getValue("filename"); 092 } catch (PropertyException e) { 093 fileName = "Unknown"; 094 } 095 } 096 } 097 098 String digest = blob.getDigest(); 099 EntityTag etag = digest == null ? null : new EntityTag(digest); 100 101 if (etag != null) { 102 ResponseBuilder builder = request.evaluatePreconditions(etag); 103 if (builder != null) { 104 return builder.build(); 105 } 106 } 107 108 // TODO probably not needed as DownloadService already does it 109 String contentDisposition = ServletHelper.getRFC2231ContentDisposition(ctx.getRequest(), fileName); 110 111 // cached resource did change or no ETag -> serve updated content 112 ResponseBuilder builder = Response.ok(blob).header("Content-Disposition", contentDisposition).type( 113 blob.getMimeType()); 114 if (etag != null) { 115 builder.tag(etag); 116 } 117 return builder.build(); 118 119 } catch (NuxeoException e) { 120 throw WebException.wrap("Failed to get the attached file", e); 121 } 122 } 123 124 @POST 125 public Response doPost() { 126 DocumentModel doc = getTarget().getAdapter(DocumentModel.class); 127 FormData form = ctx.getForm(); 128 form.fillDocument(doc); 129 String xpath = ctx.getForm().getString(FormData.PROPERTY); 130 if (xpath == null) { 131 if (doc.hasSchema("file")) { 132 xpath = "file:content"; 133 } else if (doc.hasSchema("files")) { 134 xpath = "files:files"; 135 } else { 136 throw new IllegalArgumentException("Missing request parameter named 'property' that specifies " 137 + "the blob property xpath to fetch"); 138 } 139 } 140 Blob blob = form.getFirstBlob(); 141 if (blob == null) { 142 throw new IllegalArgumentException("Could not find any uploaded file"); 143 } 144 try { 145 Property p = doc.getProperty(xpath); 146 if (p.isList()) { // add the file to the list 147 if ("files".equals(p.getSchema().getName())) { // treat the 148 // files schema 149 // separately 150 Map<String, Serializable> map = new HashMap<String, Serializable>(); 151 map.put("filename", blob.getFilename()); 152 map.put("file", (Serializable) blob); 153 p.addValue(map); 154 } else { 155 p.addValue(blob); 156 } 157 } else { 158 if ("file".equals(p.getSchema().getName())) { // for 159 // compatibility 160 // with deprecated 161 // filename 162 p.getParent().get("filename").setValue(blob.getFilename()); 163 } 164 p.setValue(blob); 165 } 166 // make snapshot 167 doc.putContextData(VersioningService.VERSIONING_OPTION, form.getVersioningOption()); 168 CoreSession session = ctx.getCoreSession(); 169 session.saveDocument(doc); 170 session.save(); 171 return redirect(getTarget().getPath()); 172 } catch (NuxeoException e) { 173 throw WebException.wrap("Failed to attach file", e); 174 } 175 } 176 177 @GET 178 @Path("delete") 179 public Response remove() { 180 return doDelete(); 181 } 182 183 @DELETE 184 public Response doDelete() { 185 DocumentModel doc = getTarget().getAdapter(DocumentModel.class); 186 FormData form = ctx.getForm(); 187 String xpath = form.getString(FormData.PROPERTY); 188 if (xpath == null) { 189 if (doc.hasSchema("file")) { 190 xpath = "file:content"; 191 } else { 192 throw new IllegalArgumentException("Missing request parameter named 'property' that specifies " 193 + "the blob property xpath to fetch"); 194 } 195 } 196 try { 197 doc.getProperty(xpath).remove(); 198 CoreSession session = ctx.getCoreSession(); 199 session.saveDocument(doc); 200 session.save(); 201 } catch (NuxeoException e) { 202 throw WebException.wrap("Failed to delete attached file", e); 203 } 204 return redirect(getTarget().getPath()); 205 } 206 207}