mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2)
123868 jkaabimofrad: RA-856: Added support for REST APIs “/content” endpoints to optionally set their own cache. (Currently, only the Renditions API is setting its own cache) - Removed the http attachment header setting from the AbstractResourceWebScript, as the ContentStreamer already has this feature - Reformatted ContentStreamer according to Alfresco code standards git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@126543 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2016 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.alfresco.repo.web.scripts.content;
|
||||
|
||||
import java.io.File;
|
||||
@@ -13,18 +32,12 @@ import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.events.types.ContentEvent;
|
||||
import org.alfresco.events.types.ContentEventImpl;
|
||||
import org.alfresco.events.types.Event;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.Client;
|
||||
import org.alfresco.repo.Client.ClientType;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
import org.alfresco.repo.events.EventPreparator;
|
||||
import org.alfresco.repo.events.EventPublisher;
|
||||
import org.alfresco.repo.web.util.HttpRangeProcessor;
|
||||
import org.alfresco.repo.webdav.WebDAVHelper;
|
||||
import org.alfresco.rest.framework.resource.content.CacheDirective;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
@@ -38,6 +51,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.extensions.surf.util.URLEncoder;
|
||||
import org.springframework.extensions.webscripts.Cache;
|
||||
import org.springframework.extensions.webscripts.WebScriptException;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
@@ -55,74 +69,80 @@ import org.springframework.util.FileCopyUtils;
|
||||
|
||||
public class ContentStreamer implements ResourceLoaderAware
|
||||
{
|
||||
// Logger
|
||||
private static final Log logger = LogFactory.getLog(ContentStreamer.class);
|
||||
// Logger
|
||||
private static final Log logger = LogFactory.getLog(ContentStreamer.class);
|
||||
|
||||
/**
|
||||
* format definied by RFC 822, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
|
||||
*/
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);
|
||||
|
||||
private static final String HEADER_CONTENT_RANGE = "Content-Range";
|
||||
private static final String HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
private static final String HEADER_ACCEPT_RANGES = "Accept-Ranges";
|
||||
private static final String HEADER_RANGE = "Range";
|
||||
private static final String HEADER_USER_AGENT = "User-Agent";
|
||||
|
||||
/** Services */
|
||||
// protected PermissionService permissionService;
|
||||
protected NodeService nodeService;
|
||||
protected ContentService contentService;
|
||||
protected MimetypeService mimetypeService;
|
||||
protected ResourceLoader resourceLoader;
|
||||
protected EventPublisher eventPublisher;
|
||||
protected SiteService siteService;
|
||||
/**
|
||||
* @param mimetypeService MimetypeService
|
||||
*/
|
||||
public void setMimetypeService(MimetypeService mimetypeService)
|
||||
{
|
||||
this.mimetypeService = mimetypeService;
|
||||
}
|
||||
public static final String KEY_ALLOW_BROWSER_TO_CACHE = "allowBrowserToCache";
|
||||
public static final String KEY_CACHE_DIRECTIVE = "cacheDirective";
|
||||
|
||||
/**
|
||||
* @param nodeService NodeService
|
||||
*/
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param eventPublisher EventPublisher
|
||||
*/
|
||||
public void setEventPublisher(EventPublisher eventPublisher)
|
||||
{
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param siteService SiteService
|
||||
*/
|
||||
public void setSiteService(SiteService siteService)
|
||||
{
|
||||
this.siteService = siteService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader)
|
||||
{
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentService ContentService
|
||||
*/
|
||||
public void setContentService(ContentService contentService)
|
||||
|
||||
{
|
||||
this.contentService = contentService;
|
||||
}
|
||||
/**
|
||||
* format definied by RFC 822, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
|
||||
*/
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);
|
||||
|
||||
private static final String HEADER_CONTENT_RANGE = "Content-Range";
|
||||
private static final String HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
private static final String HEADER_ACCEPT_RANGES = "Accept-Ranges";
|
||||
private static final String HEADER_RANGE = "Range";
|
||||
private static final String HEADER_USER_AGENT = "User-Agent";
|
||||
|
||||
/**
|
||||
* Services
|
||||
*/
|
||||
// protected PermissionService permissionService;
|
||||
protected NodeService nodeService;
|
||||
protected ContentService contentService;
|
||||
protected MimetypeService mimetypeService;
|
||||
protected ResourceLoader resourceLoader;
|
||||
protected EventPublisher eventPublisher;
|
||||
protected SiteService siteService;
|
||||
|
||||
/**
|
||||
* @param mimetypeService MimetypeService
|
||||
*/
|
||||
public void setMimetypeService(MimetypeService mimetypeService)
|
||||
{
|
||||
this.mimetypeService = mimetypeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeService NodeService
|
||||
*/
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param eventPublisher EventPublisher
|
||||
*/
|
||||
public void setEventPublisher(EventPublisher eventPublisher)
|
||||
{
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param siteService SiteService
|
||||
*/
|
||||
public void setSiteService(SiteService siteService)
|
||||
{
|
||||
this.siteService = siteService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader)
|
||||
{
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentService ContentService
|
||||
*/
|
||||
public void setContentService(ContentService contentService)
|
||||
|
||||
{
|
||||
this.contentService = contentService;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -169,79 +189,79 @@ public class ContentStreamer implements ResourceLoaderAware
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams the content on a given node's content property to the response of the web script.
|
||||
*
|
||||
* @param req Request
|
||||
* @param res Response
|
||||
* @param nodeRef The node reference
|
||||
* @param propertyQName The content property name
|
||||
* @param attach Indicates whether the content should be streamed as an attachment or not
|
||||
* @param attachFileName Optional file name to use when attach is <code>true</code>
|
||||
* @throws IOException
|
||||
*/
|
||||
public void streamContent(WebScriptRequest req,
|
||||
WebScriptResponse res,
|
||||
NodeRef nodeRef,
|
||||
QName propertyQName,
|
||||
boolean attach,
|
||||
String attachFileName,
|
||||
Map<String, Object> model) throws IOException
|
||||
* Streams the content on a given node's content property to the response of the web script.
|
||||
*
|
||||
* @param req Request
|
||||
* @param res Response
|
||||
* @param nodeRef The node reference
|
||||
* @param propertyQName The content property name
|
||||
* @param attach Indicates whether the content should be streamed as an attachment or not
|
||||
* @param attachFileName Optional file name to use when attach is <code>true</code>
|
||||
* @throws IOException
|
||||
*/
|
||||
public void streamContent(WebScriptRequest req,
|
||||
WebScriptResponse res,
|
||||
NodeRef nodeRef,
|
||||
QName propertyQName,
|
||||
boolean attach,
|
||||
String attachFileName,
|
||||
Map<String, Object> model) throws IOException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Retrieving content from node ref " + nodeRef.toString() + " (property: " + propertyQName.toString() + ") (attach: " + attach + ")");
|
||||
|
||||
// TODO
|
||||
// This was commented out to accomadate records management permissions. We need to review how we cope with this
|
||||
// hard coded permission checked.
|
||||
|
||||
// check that the user has at least READ_CONTENT access - else redirect to the login page
|
||||
// if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
|
||||
// {
|
||||
// throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
|
||||
// }
|
||||
|
||||
// check If-Modified-Since header and set Last-Modified header as appropriate
|
||||
Date modified = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
|
||||
if (modified != null)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Retrieving content from node ref " + nodeRef.toString() + " (property: " + propertyQName.toString() + ") (attach: " + attach + ")");
|
||||
|
||||
// TODO
|
||||
// This was commented out to accomadate records management permissions. We need to review how we cope with this
|
||||
// hard coded permission checked.
|
||||
|
||||
// check that the user has at least READ_CONTENT access - else redirect to the login page
|
||||
// if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
|
||||
// {
|
||||
// throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
|
||||
// }
|
||||
|
||||
// check If-Modified-Since header and set Last-Modified header as appropriate
|
||||
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
|
||||
if (modified != null)
|
||||
long modifiedSince = -1;
|
||||
String modifiedSinceStr = req.getHeader("If-Modified-Since");
|
||||
if (modifiedSinceStr != null)
|
||||
{
|
||||
long modifiedSince = -1;
|
||||
String modifiedSinceStr = req.getHeader("If-Modified-Since");
|
||||
if (modifiedSinceStr != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
modifiedSince = dateFormat.parse(modifiedSinceStr).getTime();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Browser sent badly-formatted If-Modified-Since header: " + modifiedSinceStr);
|
||||
}
|
||||
|
||||
if (modifiedSince > 0L)
|
||||
{
|
||||
// round the date to the ignore millisecond value which is not supplied by header
|
||||
long modDate = (modified.getTime() / 1000L) * 1000L;
|
||||
if (modDate <= modifiedSince)
|
||||
{
|
||||
modifiedSince = dateFormat.parse(modifiedSinceStr).getTime();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Browser sent badly-formatted If-Modified-Since header: " + modifiedSinceStr);
|
||||
}
|
||||
|
||||
if (modifiedSince > 0L)
|
||||
{
|
||||
// round the date to the ignore millisecond value which is not supplied by header
|
||||
long modDate = (modified.getTime() / 1000L) * 1000L;
|
||||
if (modDate <= modifiedSince)
|
||||
{
|
||||
res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the content reader
|
||||
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
|
||||
if (reader == null || !reader.exists())
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to locate content for node ref " + nodeRef + " (property: " + propertyQName.toString() + ")");
|
||||
}
|
||||
|
||||
// Stream the content
|
||||
streamContentImpl(req, res, reader, nodeRef, propertyQName, attach, modified, modified == null ? null : String.valueOf(modified.getTime()), attachFileName, model);
|
||||
}
|
||||
|
||||
// get the content reader
|
||||
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
|
||||
if (reader == null || !reader.exists())
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to locate content for node ref " + nodeRef + " (property: " + propertyQName.toString() + ")");
|
||||
}
|
||||
|
||||
// Stream the content
|
||||
streamContentImpl(req, res, reader, nodeRef, propertyQName, attach, modified, modified == null ? null : Long.toString(modified.getTime()), attachFileName, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams content back to client from a given resource path.
|
||||
*
|
||||
@@ -330,7 +350,7 @@ public class ContentStreamer implements ResourceLoaderAware
|
||||
final String attachFileName,
|
||||
Map<String, Object> model) throws IOException
|
||||
{
|
||||
setAttachment(null, res, attach, attachFileName);
|
||||
setAttachment(req, res, attach, attachFileName);
|
||||
|
||||
// establish mimetype
|
||||
String mimetype = reader.getMimetype();
|
||||
@@ -450,7 +470,7 @@ public class ContentStreamer implements ResourceLoaderAware
|
||||
|
||||
if (req == null)
|
||||
{
|
||||
headerValue += "; filename*=UTF-8''" + WebDAVHelper.encodeURL(attachFileName)
|
||||
headerValue += "; filename*=UTF-8''" + URLEncoder.encode(attachFileName)
|
||||
+ "; filename=\"" + attachFileName + "\"";
|
||||
}
|
||||
else
|
||||
@@ -459,12 +479,12 @@ public class ContentStreamer implements ResourceLoaderAware
|
||||
boolean isLegacy = (null != userAgent) && (userAgent.contains("MSIE 8") || userAgent.contains("MSIE 7"));
|
||||
if (isLegacy)
|
||||
{
|
||||
headerValue += "; filename=\"" + WebDAVHelper.encodeURL(attachFileName);
|
||||
headerValue += "; filename=\"" + URLEncoder.encode(attachFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
headerValue += "; filename=\"" + attachFileName + "\"; filename*=UTF-8''"
|
||||
+ WebDAVHelper.encodeURL(attachFileName);
|
||||
+ URLEncoder.encode(attachFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,8 +506,21 @@ public class ContentStreamer implements ResourceLoaderAware
|
||||
protected void setResponseCache(WebScriptResponse res, Date modified, String eTag, Map<String, Object> model)
|
||||
{
|
||||
Cache cache = new Cache();
|
||||
if (model == null || model.get("allowBrowserToCache") == null || ((String)model.get("allowBrowserToCache")).equals("false"))
|
||||
|
||||
Object obj;
|
||||
if (model != null && (obj = model.get(KEY_CACHE_DIRECTIVE)) instanceof CacheDirective)
|
||||
{
|
||||
CacheDirective cacheDirective = (CacheDirective) obj;
|
||||
cache.setNeverCache(cacheDirective.isNeverCache());
|
||||
cache.setMustRevalidate(cacheDirective.isMustRevalidate());
|
||||
cache.setMaxAge(cacheDirective.getMaxAge());
|
||||
cache.setLastModified(cacheDirective.getLastModified());
|
||||
cache.setETag(cacheDirective.getETag());
|
||||
cache.setIsPublic(cacheDirective.isPublic());
|
||||
}
|
||||
else if (model == null || !getBooleanValue(model.get(KEY_ALLOW_BROWSER_TO_CACHE)))
|
||||
{
|
||||
// if 'allowBrowserToCache' is null or false
|
||||
cache.setNeverCache(false);
|
||||
cache.setMustRevalidate(true);
|
||||
cache.setMaxAge(0L);
|
||||
@@ -498,12 +531,20 @@ public class ContentStreamer implements ResourceLoaderAware
|
||||
{
|
||||
cache.setNeverCache(false);
|
||||
cache.setMustRevalidate(false);
|
||||
cache.setMaxAge(new Long(31536000));
|
||||
cache.setMaxAge(Long.valueOf(31536000));// one year
|
||||
cache.setLastModified(modified);
|
||||
cache.setETag(eTag);
|
||||
res.setCache(cache);
|
||||
}
|
||||
|
||||
res.setCache(cache);
|
||||
}
|
||||
|
||||
private boolean getBooleanValue(Object obj)
|
||||
{
|
||||
if (obj instanceof String)
|
||||
{
|
||||
return Boolean.valueOf((String) obj);
|
||||
}
|
||||
return Boolean.TRUE.equals(obj);
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
|
||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
|
||||
import org.alfresco.rest.framework.resource.content.BinaryResource;
|
||||
import org.alfresco.rest.framework.resource.content.CacheDirective;
|
||||
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
|
||||
import org.alfresco.rest.framework.resource.content.FileBinaryResource;
|
||||
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
|
||||
@@ -70,6 +71,7 @@ import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -348,14 +350,23 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
|
||||
|
||||
Map<QName, Serializable> nodeProps = nodeService.getProperties(renditionNodeRef);
|
||||
ContentData contentData = (ContentData) nodeProps.get(ContentModel.PROP_CONTENT);
|
||||
Date modified = (Date) nodeProps.get(ContentModel.PROP_MODIFIED);
|
||||
|
||||
org.alfresco.rest.framework.resource.content.ContentInfo contentInfo = null;
|
||||
if (contentData != null)
|
||||
{
|
||||
contentInfo = new ContentInfoImpl(contentData.getMimetype(), contentData.getEncoding(), contentData.getSize(), contentData.getLocale());
|
||||
}
|
||||
// add cache settings
|
||||
CacheDirective cacheDirective = new CacheDirective.Builder()
|
||||
.setNeverCache(false)
|
||||
.setMustRevalidate(false)
|
||||
.setLastModified(modified)
|
||||
.setETag(modified != null ? Long.toString(modified.getTime()) : null)
|
||||
.setMaxAge(Long.valueOf(31536000))// one year (in seconds)
|
||||
.build();
|
||||
|
||||
return new NodeBinaryResource(renditionNodeRef, ContentModel.PROP_CONTENT, contentInfo, attachFileName);
|
||||
return new NodeBinaryResource(renditionNodeRef, ContentModel.PROP_CONTENT, contentInfo, attachFileName, cacheDirective);
|
||||
}
|
||||
|
||||
protected NodeRef getRenditionByName(NodeRef nodeRef, String renditionId, Parameters parameters)
|
||||
|
@@ -27,14 +27,21 @@ package org.alfresco.rest.framework.resource.content;
|
||||
public class AbstractBinaryResource implements BinaryResource
|
||||
{
|
||||
final String attachFileName;
|
||||
final CacheDirective cacheDirective;
|
||||
|
||||
public AbstractBinaryResource(String attachFileName)
|
||||
public AbstractBinaryResource(String attachFileName, CacheDirective cacheDirective)
|
||||
{
|
||||
this.attachFileName = attachFileName;
|
||||
this.cacheDirective = cacheDirective;
|
||||
}
|
||||
|
||||
public String getAttachFileName()
|
||||
{
|
||||
return attachFileName;
|
||||
}
|
||||
|
||||
public CacheDirective getCacheDirective()
|
||||
{
|
||||
return cacheDirective;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2016 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.alfresco.rest.framework.resource.content;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* An immutable builder for setting the HTTP cache.
|
||||
*
|
||||
* @author Jamal Kaabi-Mofrad
|
||||
*/
|
||||
public class CacheDirective
|
||||
{
|
||||
private final boolean neverCache;
|
||||
private final boolean isPublic;
|
||||
private final boolean mustRevalidate;
|
||||
private final Date lastModified;
|
||||
private final String eTag;
|
||||
private final Long maxAge;
|
||||
|
||||
private CacheDirective(Builder builder)
|
||||
{
|
||||
this.neverCache = builder.neverCache;
|
||||
this.isPublic = builder.isPublic;
|
||||
this.mustRevalidate = builder.mustRevalidate;
|
||||
this.lastModified = builder.lastModified == null ? null : new Date(builder.lastModified.getTime());
|
||||
this.eTag = builder.eTag;
|
||||
this.maxAge = builder.maxAge;
|
||||
}
|
||||
|
||||
public boolean isNeverCache()
|
||||
{
|
||||
return neverCache;
|
||||
}
|
||||
|
||||
public boolean isPublic()
|
||||
{
|
||||
return isPublic;
|
||||
}
|
||||
|
||||
public boolean isMustRevalidate()
|
||||
{
|
||||
return mustRevalidate;
|
||||
}
|
||||
|
||||
public Date getLastModified()
|
||||
{
|
||||
if (lastModified != null)
|
||||
{
|
||||
return new Date(lastModified.getTime());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getETag()
|
||||
{
|
||||
return eTag;
|
||||
}
|
||||
|
||||
public Long getMaxAge()
|
||||
{
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
// The default values are the same as the org.springframework.extensions.webscripts.Cache
|
||||
private boolean neverCache = true;
|
||||
private boolean isPublic = false;
|
||||
private boolean mustRevalidate = true;
|
||||
private Date lastModified = null;
|
||||
private String eTag = null;
|
||||
private Long maxAge = null;
|
||||
|
||||
public Builder setNeverCache(boolean neverCache)
|
||||
{
|
||||
this.neverCache = neverCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPublic(boolean aPublic)
|
||||
{
|
||||
isPublic = aPublic;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMustRevalidate(boolean mustRevalidate)
|
||||
{
|
||||
this.mustRevalidate = mustRevalidate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setLastModified(Date lastModified)
|
||||
{
|
||||
this.lastModified = lastModified;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setETag(String eTag)
|
||||
{
|
||||
this.eTag = eTag;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxAge(Long maxAge)
|
||||
{
|
||||
this.maxAge = maxAge;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CacheDirective build()
|
||||
{
|
||||
return new CacheDirective(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -37,7 +37,7 @@ public class FileBinaryResource extends AbstractBinaryResource
|
||||
|
||||
public FileBinaryResource(File file, String attachFileName)
|
||||
{
|
||||
super(attachFileName);
|
||||
super(attachFileName, null);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,12 @@ public class NodeBinaryResource extends AbstractBinaryResource
|
||||
|
||||
public NodeBinaryResource(NodeRef nodeRef, QName propertyQName, ContentInfo contentInfo, String attachFileName)
|
||||
{
|
||||
super(attachFileName);
|
||||
this(nodeRef, propertyQName, contentInfo, attachFileName, null);
|
||||
}
|
||||
|
||||
public NodeBinaryResource(NodeRef nodeRef, QName propertyQName, ContentInfo contentInfo, String attachFileName, CacheDirective cacheDirective)
|
||||
{
|
||||
super(attachFileName, cacheDirective);
|
||||
this.nodeRef = nodeRef;
|
||||
this.propertyQName = propertyQName;
|
||||
this.contentInfo = contentInfo;
|
||||
|
@@ -19,14 +19,12 @@
|
||||
package org.alfresco.rest.framework.webscripts;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.node.integrity.IntegrityException;
|
||||
import org.alfresco.repo.tenant.TenantUtil;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.web.scripts.content.ContentStreamer;
|
||||
@@ -42,6 +40,7 @@ import org.alfresco.rest.framework.resource.actions.ActionExecutor;
|
||||
import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
|
||||
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
|
||||
import org.alfresco.rest.framework.resource.content.BinaryResource;
|
||||
import org.alfresco.rest.framework.resource.content.CacheDirective;
|
||||
import org.alfresco.rest.framework.resource.content.ContentInfo;
|
||||
import org.alfresco.rest.framework.resource.content.FileBinaryResource;
|
||||
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
|
||||
@@ -53,7 +52,6 @@ import org.codehaus.jackson.JsonGenerationException;
|
||||
import org.codehaus.jackson.JsonGenerator;
|
||||
import org.codehaus.jackson.map.JsonMappingException;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.springframework.extensions.surf.util.URLEncoder;
|
||||
import org.springframework.extensions.webscripts.Cache;
|
||||
import org.springframework.extensions.webscripts.WebScriptException;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
@@ -84,8 +82,6 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
|
||||
private ContentStreamer streamer;
|
||||
protected ResourceWebScriptHelper helper;
|
||||
|
||||
public final static String HDR_NAME_CONTENT_DISPOSITION = "Content-Disposition";
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void execute(final Api api, final WebScriptRequest req, final WebScriptResponse res) throws IOException
|
||||
@@ -187,27 +183,30 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
|
||||
{
|
||||
FileBinaryResource fileResource = (FileBinaryResource) resource;
|
||||
// if requested, set attachment
|
||||
setAttachment(res, fileResource.getAttachFileName());
|
||||
streamer.streamContent(req, res, fileResource.getFile(), null, false, null, null);
|
||||
boolean attach = StringUtils.isNotEmpty(fileResource.getAttachFileName());
|
||||
Map<String, Object> model = getModelForCacheDirective(fileResource.getCacheDirective());
|
||||
streamer.streamContent(req, res, fileResource.getFile(), null, attach, fileResource.getAttachFileName(), model);
|
||||
}
|
||||
else if (resource instanceof NodeBinaryResource)
|
||||
{
|
||||
NodeBinaryResource nodeResource = (NodeBinaryResource) resource;
|
||||
ContentInfo contentInfo = nodeResource.getContentInfo();
|
||||
setContentInfoOnResponse(res,contentInfo);
|
||||
setContentInfoOnResponse(res, contentInfo);
|
||||
// if requested, set attachment
|
||||
setAttachment(res, nodeResource.getAttachFileName());
|
||||
streamer.streamContent(req, res, nodeResource.getNodeRef(), nodeResource.getPropertyQName(), false, null, null);
|
||||
boolean attach = StringUtils.isNotEmpty(nodeResource.getAttachFileName());
|
||||
Map<String, Object> model = getModelForCacheDirective(nodeResource.getCacheDirective());
|
||||
streamer.streamContent(req, res, nodeResource.getNodeRef(), nodeResource.getPropertyQName(), attach, nodeResource.getAttachFileName(), model);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setAttachment(final WebScriptResponse res, final String attachFileName)
|
||||
private static Map<String, Object> getModelForCacheDirective(CacheDirective cacheDirective)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(attachFileName))
|
||||
if (cacheDirective != null)
|
||||
{
|
||||
String headerValue = "attachment; filename=\"" + attachFileName + "\"; filename*=UTF-8''" + URLEncoder.encode(attachFileName);
|
||||
res.setHeader(HDR_NAME_CONTENT_DISPOSITION, headerValue);
|
||||
return Collections.singletonMap(ContentStreamer.KEY_CACHE_DIRECTIVE, (Object) cacheDirective);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user