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.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                fileName = "Unknown";
088            }
089
090            String digest = blob.getDigest();
091            EntityTag etag = digest == null ? null : new EntityTag(digest);
092
093            if (etag != null) {
094                ResponseBuilder builder = request.evaluatePreconditions(etag);
095                if (builder != null) {
096                    return builder.build();
097                }
098            }
099
100            // TODO probably not needed as DownloadService already does it
101            String contentDisposition = ServletHelper.getRFC2231ContentDisposition(ctx.getRequest(), fileName);
102
103            // cached resource did change or no ETag -> serve updated content
104            ResponseBuilder builder = Response.ok(blob).header("Content-Disposition", contentDisposition).type(
105                    blob.getMimeType());
106            if (etag != null) {
107                builder.tag(etag);
108            }
109            return builder.build();
110
111        } catch (NuxeoException e) {
112            e.addInfo("Failed to get the attached file");
113            throw 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            e.addInfo("Failed to attach file");
160            throw e;
161        }
162    }
163
164    @GET
165    @Path("delete")
166    public Response remove() {
167        return doDelete();
168    }
169
170    @DELETE
171    public Response doDelete() {
172        DocumentModel doc = getTarget().getAdapter(DocumentModel.class);
173        FormData form = ctx.getForm();
174        String xpath = form.getString(FormData.PROPERTY);
175        if (xpath == null) {
176            if (doc.hasSchema("file")) {
177                xpath = "file:content";
178            } else {
179                throw new IllegalArgumentException("Missing request parameter named 'property' that specifies "
180                        + "the blob property xpath to fetch");
181            }
182        }
183        try {
184            doc.getProperty(xpath).remove();
185            CoreSession session = ctx.getCoreSession();
186            session.saveDocument(doc);
187            session.save();
188        } catch (NuxeoException e) {
189            e.addInfo("Failed to delete attached file");
190            throw e;
191        }
192        return redirect(getTarget().getPath());
193    }
194
195}