001/* 002 * (C) Copyright 2017 Nuxeo (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 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.webengine.model.io; 020 021import java.io.IOException; 022import java.io.OutputStream; 023import java.lang.annotation.Annotation; 024import java.lang.reflect.Type; 025 026import javax.servlet.http.HttpServletRequest; 027import javax.servlet.http.HttpServletResponse; 028import javax.ws.rs.Produces; 029import javax.ws.rs.core.Context; 030import javax.ws.rs.core.MediaType; 031import javax.ws.rs.core.MultivaluedMap; 032import javax.ws.rs.ext.MessageBodyWriter; 033import javax.ws.rs.ext.Provider; 034 035import org.nuxeo.ecm.core.api.Blob; 036import org.nuxeo.ecm.core.api.DocumentModel; 037import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; 038import org.nuxeo.ecm.core.io.download.BufferingServletOutputStream; 039import org.nuxeo.ecm.core.io.download.DownloadHelper; 040import org.nuxeo.ecm.core.io.download.DownloadService; 041import org.nuxeo.runtime.api.Framework; 042import org.nuxeo.runtime.transaction.TransactionHelper; 043 044/** 045 * Writer for a {@link DocumentBlobHolder}, keeping the context of the current document, which allows for later 046 * filtering by the {@link DownloadService}. 047 * 048 * @since 9.3 049 */ 050@Provider 051@Produces({ "*/*", "text/plain" }) 052public class DocumentBlobHolderWriter implements MessageBodyWriter<DocumentBlobHolder> { 053 054 @Context 055 protected HttpServletRequest request; 056 057 @Context 058 protected HttpServletResponse response; 059 060 @Override 061 public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { 062 return DocumentBlobHolder.class.isAssignableFrom(type); 063 } 064 065 @Override 066 public long getSize(DocumentBlobHolder blobHolder, Class<?> type, Type genericType, Annotation[] annotations, 067 MediaType mediaType) { 068 long n = blobHolder.getBlob().getLength(); 069 return n < 0 ? -1 : n; 070 } 071 072 @Override 073 public void writeTo(DocumentBlobHolder blobHolder, Class<?> type, Type genericType, Annotation[] annotations, 074 MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) 075 throws IOException { 076 // ensure transaction is committed before writing blob to response 077 commitAndReopenTransaction(); 078 Blob blob = blobHolder.getBlob(); 079 // we don't want JAX-RS default headers (like Content-Type: text/plain) 080 // to be written, we control everything from the DownloadService 081 httpHeaders.clear(); 082 if (Framework.isTestModeSet()) { 083 // TODO remove this test-specific code 084 String filename = blob.getFilename(); 085 if (filename != null) { 086 String contentDisposition = DownloadHelper.getRFC2231ContentDisposition(request, filename); 087 response.setHeader("Content-Disposition", contentDisposition); 088 } 089 response.setContentType(blob.getMimeType()); 090 if (blob.getEncoding() != null) { 091 try { 092 response.setCharacterEncoding(blob.getEncoding()); 093 } catch (IllegalArgumentException e) { 094 // ignore invalid encoding 095 } 096 } 097 transferBlob(blob, entityStream); 098 return; 099 } 100 DocumentModel doc = blobHolder.getDocument(); 101 String xpath = blobHolder.getXpath(); 102 DownloadService downloadService = Framework.getService(DownloadService.class); 103 downloadService.downloadBlob(request, response, doc, xpath, blob, blob.getFilename(), "download"); 104 } 105 106 protected void commitAndReopenTransaction() { 107 if (TransactionHelper.isTransactionActiveOrMarkedRollback()) { 108 TransactionHelper.commitOrRollbackTransaction(); 109 TransactionHelper.startTransaction(); 110 } 111 } 112 113 protected void transferBlob(Blob blob, OutputStream entityStream) throws IOException { 114 if (entityStream instanceof BufferingServletOutputStream) { 115 ((BufferingServletOutputStream) entityStream).stopBuffering(); 116 } 117 blob.transferTo(entityStream); 118 entityStream.flush(); 119 } 120 121}