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