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