From: <ap...@vh...> - 2005-09-22 05:06:37
|
Author: apevec Date: 2005-09-22 06:57:12 +0200 (Thu, 22 Sep 2005) New Revision: 900 Modified: trunk/ccm-cms/src/com/arsdigita/cms/ui/portlet/ContentItemPortletRenderer.java trunk/ccm-core/src/com/arsdigita/bebop/portal/AbstractPortletRenderer.java trunk/ccm-core/src/com/arsdigita/bebop/portal/Portlet.java trunk/ccm-core/src/com/arsdigita/bebop/portal/PortletRenderer.java trunk/ccm-core/src/com/arsdigita/portal/apportlet/TimestampCachingPortletRenderer.java Log: SF patch [ 1178475 ] Allow cached portlet XML to be invalidated across hosts Modified: trunk/ccm-cms/src/com/arsdigita/cms/ui/portlet/ContentItemPortletRenderer.java =================================================================== --- trunk/ccm-cms/src/com/arsdigita/cms/ui/portlet/ContentItemPortletRenderer.java 2005-09-22 04:15:27 UTC (rev 899) +++ trunk/ccm-cms/src/com/arsdigita/cms/ui/portlet/ContentItemPortletRenderer.java 2005-09-22 04:57:12 UTC (rev 900) @@ -69,10 +69,10 @@ SimpleXMLGenerator.ADAPTER_CONTEXT ); } - public Object getCacheKey() { + public String getCacheKey(PageState state) { ContentItem item = m_portlet.getContentItem(); if( null == item ) return null; - if( item.isLive() ) return item.getPublicVersion().getOID(); + if( item.isLive() ) return item.getPublicVersion().getOID().toString(); // Don't cache it if it's not live return null; @@ -81,7 +81,7 @@ // For a given cache key a contnet item is *never* dirty, // since upon republishing of a live item, the item_id // changes! - public boolean isDirty() { + public boolean isDirty(PageState state) { return false; } Modified: trunk/ccm-core/src/com/arsdigita/bebop/portal/AbstractPortletRenderer.java =================================================================== --- trunk/ccm-core/src/com/arsdigita/bebop/portal/AbstractPortletRenderer.java 2005-09-22 04:15:27 UTC (rev 899) +++ trunk/ccm-core/src/com/arsdigita/bebop/portal/AbstractPortletRenderer.java 2005-09-22 04:57:12 UTC (rev 900) @@ -19,6 +19,7 @@ package com.arsdigita.bebop.portal; import com.arsdigita.bebop.PageState; +import com.arsdigita.caching.CacheTable; import com.arsdigita.xml.Element; import java.util.HashMap; import java.util.Collections; @@ -48,12 +49,17 @@ */ public abstract class AbstractPortletRenderer extends Portlet { // PortletRenderer renderer => Element root - private static Map s_cachedXMLMap = - Collections.synchronizedMap(new HashMap()); + // use a cachetable to enable cached versions to be invalidated across nodes + private static CacheTable s_cachedXMLMap = new CacheTable("portletXML"); // Portlet portlet => Date dateCached. Note that we choose not // to use a synchronized map, since a race here wouldn't matter // much. + // + // cg - no need to use a CacheTable here - if xml for a given key has been invalidated in s_cachedXMLMap + // then makeXML will be invoked on all nodes individually when they + // are next asked to render the portlet and the local date entries overwritten at that time + // private static Map s_dateCachedMap = Collections.synchronizedMap(new HashMap()); @@ -77,7 +83,7 @@ private Element makeXML(PageState state) { Element holder = new Element("holder"); - setDateCached(new Date()); + setDateCached(new Date(), state); generateXMLBody(state, holder); generateChildrenXML(state, holder); @@ -85,10 +91,10 @@ } public void generateXML(PageState state, Element parent) { - Object key = getCacheKey(); + String key = getCacheKey(state); Element xml; - if (isDirty()) { + if (isDirty(state)) { xml = makeXML(state); s_cachedXMLMap.put(key, xml); } @@ -111,28 +117,40 @@ } /** - * Developer's may use this method to implement cacheing of a - * portlet's content. Portlet's default implementation always - * returns true. + * Invalidate cached version of XML output for this portlet + * across all nodes. + * + * This can be invoked when a portlet is edited on one node eg. + * + * <pre> + * protected void processWidgets(PageState state, Portlet portlet) + * throws FormProcessException { + * + * + * ExamplePortlet myportlet = (ExamplePortlet) portlet; + * + * update portlet data + * + * myportlet.getPortletRenderer().invalidateCachedVersion(state); + * + * } + * + * </pre> * - * @return whether or not the portlet's content is dirty and must - * be regenerated. + * @param state */ - public boolean isDirty() { - return true; + public void invalidateCachedVersion(PageState state) { + s_cachedXMLMap.remove(getCacheKey(state)); } - public Object getCacheKey() { - return null; - } - // Can return null. - public Date getDateCached() { - return (Date) s_dateCachedMap.get(getCacheKey()); + public Date getDateCached(PageState state) { + return (Date) s_dateCachedMap.get(getCacheKey(state)); } // Can take null. - private void setDateCached(Date dateCached) { - s_dateCachedMap.put(getCacheKey(), dateCached); + private void setDateCached(Date dateCached, PageState state) { + s_dateCachedMap.put(getCacheKey(state), dateCached); } + } Modified: trunk/ccm-core/src/com/arsdigita/bebop/portal/Portlet.java =================================================================== --- trunk/ccm-core/src/com/arsdigita/bebop/portal/Portlet.java 2005-09-22 04:15:27 UTC (rev 899) +++ trunk/ccm-core/src/com/arsdigita/bebop/portal/Portlet.java 2005-09-22 04:57:12 UTC (rev 900) @@ -209,11 +209,20 @@ // For PortletRenderer interface. - public boolean isDirty() { + public boolean isDirty(PageState state) { return true; } - public Object getCacheKey() { - return null; + public String getCacheKey(PageState state) { + // cannot return null, as CacheTable doesn't like it + return ""; } + + /* + * @see com.arsdigita.bebop.portal.PortletRenderer#invalidateCachedVersion(com.arsdigita.bebop.PageState) + */ + public void invalidateCachedVersion(PageState state) { + // to be implemented in the class that implements the cache + } + } Modified: trunk/ccm-core/src/com/arsdigita/bebop/portal/PortletRenderer.java =================================================================== --- trunk/ccm-core/src/com/arsdigita/bebop/portal/PortletRenderer.java 2005-09-22 04:15:27 UTC (rev 899) +++ trunk/ccm-core/src/com/arsdigita/bebop/portal/PortletRenderer.java 2005-09-22 04:57:12 UTC (rev 900) @@ -67,7 +67,55 @@ */ void generateXML(PageState pageState, Element parentElement); - boolean isDirty(); + /** + * Developers of portlets may specify the rules that decide if the cached version + * of the portlet output is considered to be out of date, and it's attributes must be + * refreshed from the database. + * + * Implementing this method + * and getCacheKey can save trips to the database to retrieve a particular + * portlet's attributes + * + * @param pageState the PageState of the current request. + * @return true if the portlet must be refreshed, false if cached version is still valid + */ + boolean isDirty(PageState state); + /** + * Key for lookup of cached generated XML. Implementing this method + * and isDirty can save trips to the database to retrieve a particular + * portlet's attributes. + * + * PageState argument allows stateful portlets to cache particular states. Treat with caution. + * For example avoid the condition where all cached states are dirty, but + * a check on one state leads to refresh of output for that state, and subsequent checks + * for isDirty on other states return false. This can be avoided by ensuring that isDirty is + * dependent on the particular state being checked eg - a check on the cached time of the cached xml + * or by overriding invalidateCachedVersion to discard all cached states + * + * Also, ensure that you don't use the entire state as a key - the container can retrieve state defining information + * such as a page number or name of visible component from the components and use that as key + * + * If you are not caching a stateful portlet, ignore the PageState argument. + * + * nb - in this revision, return type has been restricted to String from Object in order that + * rendered output may be cached in a CacheTable in order that cached version may be + * invalidated across all nodes if edited on one node + * + * (chr...@we...) + * + * @param pageState the PageState of the current request. + * @return a key for the cache entry nb - while this is often a string representation of + * a portlet id, it is not necessarily so - eg content item portlet cached by item id, so + * several portlets may share the same cached XML + */ + String getCacheKey(PageState state); + + /** + * remove cached version of this portletRenderer's output. + * Implementors should ensure that the cached version is invalidated across all nodes + * for example by using a com.arsdigita.caching.CacheTable for the cache + * @param state + */ + void invalidateCachedVersion(PageState state); - Object getCacheKey(); } Modified: trunk/ccm-core/src/com/arsdigita/portal/apportlet/TimestampCachingPortletRenderer.java =================================================================== --- trunk/ccm-core/src/com/arsdigita/portal/apportlet/TimestampCachingPortletRenderer.java 2005-09-22 04:15:27 UTC (rev 899) +++ trunk/ccm-core/src/com/arsdigita/portal/apportlet/TimestampCachingPortletRenderer.java 2005-09-22 04:57:12 UTC (rev 900) @@ -18,6 +18,7 @@ */ package com.arsdigita.portal.apportlet; +import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.portal.AbstractPortletRenderer; import com.arsdigita.portal.Portlet; import java.util.Date; @@ -39,8 +40,8 @@ m_portlet = portlet; } - public boolean isDirty() { - Date cached = getDateCached(); + public boolean isDirty(PageState state) { + Date cached = getDateCached(state); Date timestamp = getTimestamp(); if (cached == null || timestamp == null) { @@ -51,8 +52,8 @@ } } - public Object getCacheKey() { - return m_portlet.getID(); + public String getCacheKey(PageState state) { + return m_portlet.getID().toString(); } public abstract Date getTimestamp(); |