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