From 4ba65b99502eb97c5a53dc7f607f6cc4a74bdd07 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Mon, 20 Aug 2007 15:46:59 +0000 Subject: [PATCH] Merged V2.1 to V2.0 6435: AR-1644 Web Scripts do not provide any control over caching 6469: Replaced EUPL licence with standard license header 6526: AR-1685 Error creating workflow with no document associated 6565: Fix for issue with file Upload in main web-client portlet for JBoss/Liferay portal integration. 6578: AR-1620: Upgraded One-Jar to 0.96-RC4 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6581 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../web/app/portlet/AlfrescoFacesPortlet.java | 15 +- .../bean/VersionedDocumentDetailsBean.java | 31 +++- .../bean/workflow/StartWorkflowWizard.java | 23 +-- .../web/scripts/AbstractWebScript.java | 3 +- .../web/scripts/DeclarativeWebScript.java | 30 +++- .../scripts/DeclarativeWebScriptRegistry.java | 30 +++- .../alfresco/web/scripts/WebScriptCache.java | 162 ++++++++++++++++++ .../web/scripts/WebScriptDescription.java | 34 ++++ .../web/scripts/WebScriptDescriptionImpl.java | 19 ++ .../web/scripts/WebScriptResponse.java | 10 +- .../web/scripts/WebScriptRuntime.java | 3 + .../web/scripts/WebScriptServlet.java | 3 - .../web/scripts/WebScriptServletResponse.java | 93 +++++++++- .../alfresco/web/scripts/bean/ContentGet.java | 14 +- .../web/scripts/jsf/WebScriptJSFResponse.java | 9 + .../portlet/WebScriptPortletResponse.java | 11 +- 16 files changed, 449 insertions(+), 41 deletions(-) create mode 100644 source/java/org/alfresco/web/scripts/WebScriptCache.java diff --git a/source/java/org/alfresco/web/app/portlet/AlfrescoFacesPortlet.java b/source/java/org/alfresco/web/app/portlet/AlfrescoFacesPortlet.java index 2d10a7ffcf..87a6c7c124 100644 --- a/source/java/org/alfresco/web/app/portlet/AlfrescoFacesPortlet.java +++ b/source/java/org/alfresco/web/app/portlet/AlfrescoFacesPortlet.java @@ -74,6 +74,7 @@ import org.springframework.web.context.WebApplicationContext; public class AlfrescoFacesPortlet extends MyFacesGenericPortlet { private static final String PREF_ALF_USERNAME = "_alfUserName"; + private static final String SESSION_LAST_VIEW_ID = "_alfLastViewId"; private static final String ERROR_PAGE_PARAM = "error-page"; private static final String ERROR_OCCURRED = "error-occurred"; @@ -151,11 +152,13 @@ public class AlfrescoFacesPortlet extends MyFacesGenericPortlet } } - // it doesn't matter what the value is we just need the VIEW_ID parameter - // to tell the faces portlet bridge to treat the request as a JSF request, - // this will send us back to the same page we came from, which is fine for - // most scenarios. - response.setRenderParameter(VIEW_ID, "a-jsf-page"); + // Set the VIEW_ID parameter to tell the faces portlet bridge to treat the request + // as a JSF request, this will send us back to the previous page we came from. + String lastViewId = (String)request.getPortletSession().getAttribute(SESSION_LAST_VIEW_ID); + if (lastViewId != null) + { + response.setRenderParameter(VIEW_ID, lastViewId); + } } else { @@ -262,6 +265,8 @@ public class AlfrescoFacesPortlet extends MyFacesGenericPortlet // use the viewId to check that we are not already on the login page PortletSession session = request.getPortletSession(); String viewId = request.getParameter(VIEW_ID); + // keep track of last view id so we can use it as return page from multi-part requests + request.getPortletSession().setAttribute(SESSION_LAST_VIEW_ID, viewId); User user = (User)request.getPortletSession().getAttribute(AuthenticationHelper.AUTHENTICATION_USER); if (user == null && (viewId == null || viewId.equals(getLoginPage()) == false)) { diff --git a/source/java/org/alfresco/web/bean/VersionedDocumentDetailsBean.java b/source/java/org/alfresco/web/bean/VersionedDocumentDetailsBean.java index b4b5d55aa7..cb4d98b513 100644 --- a/source/java/org/alfresco/web/bean/VersionedDocumentDetailsBean.java +++ b/source/java/org/alfresco/web/bean/VersionedDocumentDetailsBean.java @@ -1,9 +1,27 @@ -/*--+ - | Copyright European Community 2006 - Licensed under the EUPL V.1.0 - | - | http://ec.europa.eu/idabc/en/document/6523 - | - +--*/ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ package org.alfresco.web.bean; import java.io.Serializable; @@ -25,7 +43,6 @@ import org.alfresco.repo.version.common.VersionLabelComparator; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.ml.EditionService; import org.alfresco.service.cmr.ml.MultilingualContentService; -import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; diff --git a/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java b/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java index 08ed2eab46..0918ff7a30 100644 --- a/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java +++ b/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java @@ -153,21 +153,16 @@ public class StartWorkflowWizard extends BaseWizardBean logger.debug("Starting workflow with parameters: " + params); // create a workflow package for the attached items and add them - if (this.packageItemsToAdd.size() > 0) + NodeRef workflowPackage = this.workflowService.createPackage(null); + params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage); + + for (String addedItem : this.packageItemsToAdd) { - NodeRef workflowPackage = this.workflowService.createPackage(null); - - for (String addedItem : this.packageItemsToAdd) - { - NodeRef addedNodeRef = new NodeRef(addedItem); - this.nodeService.addChild(workflowPackage, addedNodeRef, - ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - QName.createValidLocalName((String)this.nodeService.getProperty( - addedNodeRef, ContentModel.PROP_NAME)))); - } - - // add the workflow package to the parameter map - params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage); + NodeRef addedNodeRef = new NodeRef(addedItem); + this.nodeService.addChild(workflowPackage, addedNodeRef, + ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + QName.createValidLocalName((String)this.nodeService.getProperty( + addedNodeRef, ContentModel.PROP_NAME)))); } // setup the context for the workflow (this is the space the workflow was launched from) diff --git a/source/java/org/alfresco/web/scripts/AbstractWebScript.java b/source/java/org/alfresco/web/scripts/AbstractWebScript.java index 91c5605ee0..7ca67434a0 100644 --- a/source/java/org/alfresco/web/scripts/AbstractWebScript.java +++ b/source/java/org/alfresco/web/scripts/AbstractWebScript.java @@ -356,7 +356,7 @@ public abstract class AbstractWebScript implements WebScript * @param model model * @throws IOException */ - final protected void sendStatus(WebScriptRequest req, WebScriptResponse res, WebScriptStatus status, String format, Map model) + final protected void sendStatus(WebScriptRequest req, WebScriptResponse res, WebScriptStatus status, WebScriptCache cache, String format, Map model) throws IOException { // locate status template @@ -389,6 +389,7 @@ public abstract class AbstractWebScript implements WebScript } res.reset(); + res.setCache(cache); res.setStatus(req.forceSuccessStatus() ? HttpServletResponse.SC_OK : statusCode); res.setContentType(mimetype + ";charset=UTF-8"); renderTemplate(template.path, model, res.getWriter()); diff --git a/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java b/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java index da74e23b3a..4af1501d65 100644 --- a/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java +++ b/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java @@ -93,14 +93,16 @@ public class DeclarativeWebScript extends AbstractWebScript throw new WebScriptException("Web Script format '" + format + "' is not registered"); } - // construct data model for template + // construct model for script / template WebScriptStatus status = new WebScriptStatus(); - Map model = executeImpl(req, status); + WebScriptCache cache = new WebScriptCache(getDescription().getRequiredCache()); + Map model = executeImpl(req, status, cache); if (model == null) { model = new HashMap(7, 1.0f); } model.put("status", status); + model.put("cache", cache); // execute script if it exists if (executeScript != null) @@ -122,7 +124,7 @@ public class DeclarativeWebScript extends AbstractWebScript // is a redirect to a status specific template required? if (status.getRedirect()) { - sendStatus(req, res, status, format, templateModel); + sendStatus(req, res, status, cache, format, templateModel); } else { @@ -135,6 +137,9 @@ public class DeclarativeWebScript extends AbstractWebScript res.setStatus(statusCode); } + // apply cache + res.setCache(cache); + String callback = req.getJSONCallback(); if (format.equals(WebScriptResponse.JSON_FORMAT) && callback != null) { @@ -178,10 +183,12 @@ public class DeclarativeWebScript extends AbstractWebScript status.setCode(statusCode); status.setMessage(e.getMessage()); status.setException(e); + WebScriptCache cache = new WebScriptCache(); + cache.setNeverCache(true); Map customModel = new HashMap(); customModel.put("status", status); Map templateModel = createTemplateModel(req, res, customModel); - sendStatus(req, res, status, format, templateModel); + sendStatus(req, res, status, cache, format, templateModel); } } @@ -207,6 +214,7 @@ public class DeclarativeWebScript extends AbstractWebScript * Execute custom Java logic * * @param req Web Script request + * @param status Web Script status * @return custom service model */ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status) @@ -214,6 +222,20 @@ public class DeclarativeWebScript extends AbstractWebScript return null; } + /** + * Execute custom Java logic + * + * @param req Web Script request + * @param status Web Script status + * @param cache Web Script cache + * @return custom service model + */ + protected Map executeImpl(WebScriptRequest req, WebScriptStatus status, WebScriptCache cache) + { + // NOTE: Redirect to those web scripts implemented before cache support + return executeImpl(req, status); + } + /** * Render a template (of given format) to the Web Script Response * diff --git a/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java b/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java index f00cd37b26..9904d97c3b 100644 --- a/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java +++ b/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java @@ -34,7 +34,6 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import javax.faces.context.FacesContext; import javax.servlet.ServletContext; import org.alfresco.service.cmr.repository.FileTypeImageSize; @@ -531,6 +530,34 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean } } + // retrieve caching + WebScriptCache cache = new WebScriptCache(); + Element cacheElement = rootElement.element("cache"); + if (cacheElement != null) + { + Element neverElement = cacheElement.element("never"); + if (neverElement != null) + { + String neverStr = neverElement.getTextTrim(); + boolean neverBool = (neverStr == null || neverStr.length() == 0) ? true : Boolean.valueOf(neverStr); + cache.setNeverCache(neverBool); + } + Element publicElement = cacheElement.element("public"); + if (publicElement != null) + { + String publicStr = publicElement.getTextTrim(); + boolean publicBool = (publicStr == null || publicStr.length() == 0) ? true : Boolean.valueOf(publicStr); + cache.setIsPublic(publicBool); + } + Element revalidateElement = cacheElement.element("mustrevalidate"); + if (revalidateElement != null) + { + String revalidateStr = revalidateElement.getTextTrim(); + boolean revalidateBool = (revalidateStr == null || revalidateStr.length() == 0) ? true : Boolean.valueOf(revalidateStr); + cache.setMustRevalidate(revalidateBool); + } + } + // construct service description WebScriptDescriptionImpl serviceDesc = new WebScriptDescriptionImpl(); serviceDesc.setStore(store); @@ -541,6 +568,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean serviceDesc.setDescription(description); serviceDesc.setRequiredAuthentication(reqAuth); serviceDesc.setRequiredTransaction(reqTrx); + serviceDesc.setRequiredCache(cache); serviceDesc.setMethod(method); serviceDesc.setUris(uris.toArray(new String[uris.size()])); serviceDesc.setDefaultFormat(defaultFormat); diff --git a/source/java/org/alfresco/web/scripts/WebScriptCache.java b/source/java/org/alfresco/web/scripts/WebScriptCache.java new file mode 100644 index 0000000000..506e063e22 --- /dev/null +++ b/source/java/org/alfresco/web/scripts/WebScriptCache.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.web.scripts; + +import java.util.Date; + + +/** + * Web Script Cache + * + * Records the desired cache requirements for the Web Script + * + * @author davidc + */ +public class WebScriptCache implements WebScriptDescription.RequiredCache +{ + private boolean neverCache = true; + private boolean isPublic = false; + private boolean mustRevalidate = true; + private Date lastModified = null; + private String eTag = null; + private Long maxAge = null; + + + /** + * Construct + */ + public WebScriptCache() + { + } + + /** + * Construct + * + * @param requiredCache + */ + public WebScriptCache(WebScriptDescription.RequiredCache requiredCache) + { + neverCache = requiredCache.getNeverCache(); + isPublic = requiredCache.getIsPublic(); + mustRevalidate = requiredCache.getMustRevalidate(); + } + + /* (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptDescription.RequiredCache#getNeverCache() + */ + public boolean getNeverCache() + { + return neverCache; + } + + /** + * @param neverCache + */ + public void setNeverCache(boolean neverCache) + { + this.neverCache = neverCache; + } + + /* (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptDescription.RequiredCache#getIsPublic() + */ + public boolean getIsPublic() + { + return isPublic; + } + + /** + * @param isPublic + */ + public void setIsPublic(boolean isPublic) + { + this.isPublic = isPublic; + } + + /** + * @return last modified + */ + public Date getLastModified() + { + return lastModified; + } + + /** + * @param lastModified + */ + public void setLastModified(Date lastModified) + { + this.lastModified = lastModified; + } + + /** + * @return ETag + */ + public String getETag() + { + return eTag; + } + + /** + * @param tag ETag + */ + public void setETag(String tag) + { + eTag = tag; + } + + /** + * @return Max Age (seconds) + */ + public Long getMaxAge() + { + return maxAge; + } + + /** + * @param maxAge Max Age (seconds) + */ + public void setMaxAge(Long maxAge) + { + this.maxAge = maxAge; + } + + /* (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptDescription.RequiredCache#getMustRevalidate() + */ + public boolean getMustRevalidate() + { + return mustRevalidate; + } + + /** + * @param mustRevalidate + */ + public void setMustRevalidate(boolean mustRevalidate) + { + this.mustRevalidate = mustRevalidate; + } + +} diff --git a/source/java/org/alfresco/web/scripts/WebScriptDescription.java b/source/java/org/alfresco/web/scripts/WebScriptDescription.java index 4c43e33ebc..39fdfdadc2 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptDescription.java +++ b/source/java/org/alfresco/web/scripts/WebScriptDescription.java @@ -56,6 +56,33 @@ public interface WebScriptDescription requiresnew } + /** + * Caching requirements + */ + public interface RequiredCache + { + /** + * Determine if Web Script should ever be cached + * + * @return true => do not cache, false => caching may or not occur + */ + public boolean getNeverCache(); + + /** + * Determine if Web Script content is for public caching + * + * @return true => content is public, so allow cache + */ + public boolean getIsPublic(); + + /** + * Must cache re-validate to ensure content is fresh + * + * @return true => must re-validate + */ + public boolean getMustRevalidate(); + } + /** * Enumeration of ways to specify format */ @@ -66,6 +93,7 @@ public interface WebScriptDescription argument // /a/b/c?format=x } + /** * Gets the root path of the store of this web script * @@ -128,6 +156,12 @@ public interface WebScriptDescription */ public RequiredTransaction getRequiredTransaction(); + /** + * Gets the required level of caching + * @return + */ + public RequiredCache getRequiredCache(); + /** * Gets the HTTP method this service is bound to * diff --git a/source/java/org/alfresco/web/scripts/WebScriptDescriptionImpl.java b/source/java/org/alfresco/web/scripts/WebScriptDescriptionImpl.java index 447c5d3ffe..c6ed8452c8 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptDescriptionImpl.java +++ b/source/java/org/alfresco/web/scripts/WebScriptDescriptionImpl.java @@ -43,6 +43,7 @@ public class WebScriptDescriptionImpl implements WebScriptDescription private String description; private RequiredAuthentication requiredAuthentication; private RequiredTransaction requiredTransaction; + private RequiredCache requiredCache; private FormatStyle formatStyle; private String httpMethod; private String[] uris; @@ -202,6 +203,24 @@ public class WebScriptDescriptionImpl implements WebScriptDescription return this.requiredTransaction; } + /** + * Sets the required cache + * + * @param requiredCache + */ + public void setRequiredCache(RequiredCache requiredCache) + { + this.requiredCache = requiredCache; + } + + /* (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptDescription#getRequiredCache() + */ + public RequiredCache getRequiredCache() + { + return this.requiredCache; + } + /** * Sets the format style * diff --git a/source/java/org/alfresco/web/scripts/WebScriptResponse.java b/source/java/org/alfresco/web/scripts/WebScriptResponse.java index dffe39aea0..acc764ff83 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptResponse.java +++ b/source/java/org/alfresco/web/scripts/WebScriptResponse.java @@ -58,6 +58,13 @@ public interface WebScriptResponse */ public void setContentType(String contentType); + /** + * Sets the Cache control + * + * @param cache cache control + */ + public void setCache(WebScriptCache cache); + /** * Gets the Writer * @@ -78,7 +85,7 @@ public interface WebScriptResponse * Clears response buffer */ public void reset(); - + /** * Encode a script URL * @@ -97,4 +104,5 @@ public interface WebScriptResponse * @return javascript function definition */ public String getEncodeScriptUrlFunction(String name); + } diff --git a/source/java/org/alfresco/web/scripts/WebScriptRuntime.java b/source/java/org/alfresco/web/scripts/WebScriptRuntime.java index be15f36761..6d96391e3f 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptRuntime.java +++ b/source/java/org/alfresco/web/scripts/WebScriptRuntime.java @@ -235,6 +235,9 @@ public abstract class WebScriptRuntime } res.reset(); + WebScriptCache cache = new WebScriptCache(); + cache.setNeverCache(true); + res.setCache(cache); res.setStatus(req.forceSuccessStatus() ? HttpServletResponse.SC_OK : statusCode); res.setContentType(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8"); try diff --git a/source/java/org/alfresco/web/scripts/WebScriptServlet.java b/source/java/org/alfresco/web/scripts/WebScriptServlet.java index dbd1a0d4c0..56694e9b07 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptServlet.java +++ b/source/java/org/alfresco/web/scripts/WebScriptServlet.java @@ -102,9 +102,6 @@ public class WebScriptServlet extends HttpServlet if (logger.isDebugEnabled()) logger.debug("Processing request (" + req.getMethod() + ") " + req.getRequestURL() + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); - res.setHeader("Cache-Control", "no-cache"); - res.setHeader("Pragma", "no-cache"); - WebScriptRuntime runtime = new WebScriptServletRuntime(registry, serviceRegistry, authenticator, req, res, serverConfig); runtime.executeScript(); } diff --git a/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java b/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java index 70067a257e..34874f9919 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java +++ b/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java @@ -27,10 +27,15 @@ package org.alfresco.web.scripts; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.TimeZone; import javax.servlet.http.HttpServletResponse; +import org.alfresco.util.CachingDateFormat; import org.alfresco.web.ui.common.Utils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * HTTP Servlet Web Script Response @@ -39,7 +44,12 @@ import org.alfresco.web.ui.common.Utils; */ public class WebScriptServletResponse implements WebScriptResponse { + // Logger + private static final Log logger = LogFactory.getLog(WebScriptServletResponse.class); + + // Servlet Response private HttpServletResponse res; + /** * Construct @@ -77,6 +87,66 @@ public class WebScriptServletResponse implements WebScriptResponse res.setContentType(contentType); } + /* (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#setCache(org.alfresco.web.scripts.WebScriptCache) + */ + public void setCache(WebScriptCache cache) + { + // set Cache-Control + String cacheControl = ""; + String pragma = ""; + if (cache.getIsPublic()) + { + cacheControl += "public"; + } + if (cache.getNeverCache()) + { + cacheControl += (cacheControl.length() > 0 ? ", " : "") + "no-cache"; + pragma += (pragma.length() > 0) ? ", " : "" + "no-cache"; + } + if (cache.getMaxAge() != null && cache.getNeverCache() == false) + { + cacheControl += (cacheControl.length() > 0 ? ", " : "") + " max-age=" + cache.getMaxAge(); + } + if (cache.getMustRevalidate() && cache.getNeverCache() == false) + { + cacheControl += (cacheControl.length() > 0 ? ", " : "") + " must-revalidate"; + } + if (cacheControl.length() > 0) + { + res.setHeader("Cache-Control", cacheControl); + if (logger.isDebugEnabled()) + logger.debug("Cache - set response header Cache-Control: " + cacheControl); + } + if (pragma.length() > 0) + { + res.setHeader("Pragma", pragma); + if (logger.isDebugEnabled()) + logger.debug("Cache - set response header Pragma: " + pragma); + } + + // set ETag + if (cache.getETag() != null) + { + String eTag = "\"" + cache.getETag() + "\""; + res.setHeader("ETag", eTag); + if (logger.isDebugEnabled()) + logger.debug("Cache - set response header ETag: " + eTag); + } + + // set Last Modified + if (cache.getLastModified() != null) + { + res.setDateHeader("Last-Modified", cache.getLastModified().getTime()); + if (logger.isDebugEnabled()) + { + SimpleDateFormat formatter = getHTTPDateFormat(); + String lastModified = formatter.format(cache.getLastModified()); + logger.debug("Cache - set response header Last-Modified: " + lastModified); + } + } + } + /* (non-Javadoc) * @see org.alfresco.web.scripts.WebScriptResponse#reset() */ @@ -90,7 +160,7 @@ public class WebScriptServletResponse implements WebScriptResponse { } } - + /* (non-Javadoc) * @see org.alfresco.web.scripts.WebScriptResponse#getWriter() */ @@ -124,4 +194,25 @@ public class WebScriptServletResponse implements WebScriptResponse } private static final String ENCODE_FUNCTION = "{ $name$: function(url) { return url; } }"; + + /** + * Helper to return a HTTP Date Formatter + * + * @return HTTP Date Formatter + */ + private static SimpleDateFormat getHTTPDateFormat() + { + if (s_dateFormat.get() != null) + { + return s_dateFormat.get(); + } + + SimpleDateFormat formatter = CachingDateFormat.getDateFormat("EEE, dd MMM yyyy kk:mm:ss zzz", false); + formatter.setTimeZone(TimeZone.getTimeZone("GMT")); + s_dateFormat.set(formatter); + return s_dateFormat.get(); + } + + private static ThreadLocal s_dateFormat = new ThreadLocal(); + } diff --git a/source/java/org/alfresco/web/scripts/bean/ContentGet.java b/source/java/org/alfresco/web/scripts/bean/ContentGet.java index e1bab8987d..0e3dfc36fc 100644 --- a/source/java/org/alfresco/web/scripts/bean/ContentGet.java +++ b/source/java/org/alfresco/web/scripts/bean/ContentGet.java @@ -45,6 +45,7 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.web.scripts.AbstractWebScript; +import org.alfresco.web.scripts.WebScriptCache; import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptResponse; @@ -138,7 +139,6 @@ public class ContentGet extends AbstractWebScript return; } } - httpRes.setDateHeader("Last-Modified", modified.getTime()); // handle attachment if (attach == true) @@ -174,10 +174,18 @@ public class ContentGet extends AbstractWebScript } } - // set mimetype for the content and the character encoding for the stream + // set mimetype for the content and the character encoding + length for the stream httpRes.setContentType(mimetype); httpRes.setCharacterEncoding(reader.getEncoding()); - + httpRes.setHeader("Content-Length", Long.toString(reader.getSize())); + + // set caching + WebScriptCache cache = new WebScriptCache(); + cache.setNeverCache(false); + cache.setMustRevalidate(true); + cache.setLastModified(modified); + res.setCache(cache); + // get the content and stream directly to the response output stream // assuming the repository is capable of streaming in chunks, this should allow large files // to be streamed directly to the browser response stream. diff --git a/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java b/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java index dd52a1b998..44087d9308 100644 --- a/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java +++ b/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java @@ -34,6 +34,7 @@ import javax.faces.component.UIForm; import javax.faces.context.FacesContext; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.web.scripts.WebScriptCache; import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.ui.common.Utils; import org.apache.myfaces.shared_impl.renderkit.html.HtmlFormRendererBase; @@ -137,6 +138,14 @@ public class WebScriptJSFResponse implements WebScriptResponse // makes no sense in the JSF env } + /** + * @see org.alfresco.web.scripts.WebScriptResponse#setCache() + */ + public void setCache(WebScriptCache cache) + { + // NOTE: not applicable + } + /** * @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String) */ diff --git a/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java b/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java index 3af33a5f12..900b9fee39 100644 --- a/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java +++ b/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java @@ -31,6 +31,7 @@ import java.io.Writer; import javax.portlet.PortletURL; import javax.portlet.RenderResponse; +import org.alfresco.web.scripts.WebScriptCache; import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.ui.common.Utils; @@ -82,6 +83,14 @@ public class WebScriptPortletResponse implements WebScriptResponse res.setContentType(contentType); } + /* (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#getCache() + */ + public void setCache(WebScriptCache cache) + { + // NOTE: Not applicable + } + /* (non-Javadoc) * @see org.alfresco.web.scripts.WebScriptResponse#reset() */ @@ -111,7 +120,7 @@ public class WebScriptPortletResponse implements WebScriptResponse { return res.getPortletOutputStream(); } - + /* (non-Javadoc) * @see org.alfresco.web.scripts.WebScriptResponse#encodeScriptUrl(java.lang.String) */