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