From 5b4fce41f8065f5f1770b9568485f6e82d356df9 Mon Sep 17 00:00:00 2001 From: jensp Date: Tue, 14 May 2013 13:02:10 +0000 Subject: [PATCH] =?UTF-8?q?Klassendatei=20f=C3=BCr=20XMLDeliveryCache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.libreccm.org/ccm/trunk@2153 8810af33-2d31-482b-a856-94f89814c4df --- .../com/arsdigita/cms/XMLDeliveryCache.java | 326 ++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 ccm-cms/src/com/arsdigita/cms/XMLDeliveryCache.java diff --git a/ccm-cms/src/com/arsdigita/cms/XMLDeliveryCache.java b/ccm-cms/src/com/arsdigita/cms/XMLDeliveryCache.java new file mode 100644 index 000000000..f80b4ecae --- /dev/null +++ b/ccm-cms/src/com/arsdigita/cms/XMLDeliveryCache.java @@ -0,0 +1,326 @@ +package com.arsdigita.cms; + +import com.arsdigita.caching.CacheTable; +import com.arsdigita.persistence.OID; +import com.arsdigita.xml.Element; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A cache for generated XML. This class is a singleton. Use {@link #getInstance()} to obtain the only instance of this + * class. + * + * @author Jens Pelzetter + * @version $Id$ + */ +public final class XMLDeliveryCache { + + private static final String DEFAULT_CONTEXT = "{{DEFAULT_CONTEXT}}"; + private static final String LIST_MODE_CONTEXT = "{{LIST_MODE_CONTEXT}}"; + private static final XMLDeliveryCache INSTANCE = new XMLDeliveryCache(); + /** + * The real cache. + */ + private CacheTable cache = new CacheTable(XMLDeliveryCache.class.getName(), + CMSConfig.getInstance().getXmlCacheSize(), + CMSConfig.getInstance().getXmlCacheSize(), + true); + /** + * Maps from the OID of the master version of an item in the cache to the OID of the item in the cache. + */ + private Map cachedItems = new HashMap(); + + /** + * Private constructor + */ + private XMLDeliveryCache() { + //Nothing here at the moment + } + + /** + * Retrieves the instance of the XMLDeliveryCache + * + * @return + */ + public static XMLDeliveryCache getInstance() { + return INSTANCE; + } + + /** + * Checks if the XML representation of an item is cached. + * + * @param oid The OID of the item. + * @param context The context of the XML output. + * @param listMode Is the XML cached for list mode or normal mode. + * @return + */ + public boolean isCached(final OID oid, final String context, final boolean listMode) { + final CachedItem cachedItem = (CachedItem) cache.get(oid.toString()); + if (cachedItem == null) { + return false; + } + + final CachedXml cachedXml = cachedItem.get(context); + if (cachedXml == null) { + return false; + } + + if (listMode) { + return (cachedXml.getCachedXml(Mode.LIST_MODE) != null); + } else { + return (cachedXml.getCachedXml(Mode.DEFAULT) != null); + } + } + + /** + * Convenient method for calling {@link #isCached(OID, java.lang.String, boolean)} with {@code listMode = false}. + * + * @param oid + * @param context + * @return + */ + public boolean isCached(final OID oid, final String context) { + return isCached(oid, context, false); + } + + /** + * Convenient method for calling {@link #isCached(OID, java.lang.String, boolean)} with {@code context = ""} and + * {@code listMode = false}. + * + * @param oid + * @return + */ + public boolean isCached(final OID oid) { + return isCached(oid, "", false); + } + + /** + * Retrieves the cached XML from the cache and copies the cached XML to the provided parent element. Important: + * Before calling this method check if the XML for the item is cached using + * {@link #isCached(OID, java.lang.String, boolean)}. If this method is called for an item not in the cache this + * method will throw an {@link IllegalArgumentException}! + * + * @param parent The element to which the cached XML is copied. + * @param oid The OID of the item to retrieve. + * @param context The context of the cached XML. + * @param listMode Is the cached XML for the list mode or the normal mode. + * + */ + public void retrieveFromCache(final Element parent, + final OID oid, + final String context, + final boolean listMode) { + final CachedItem cachedItem = (CachedItem) cache.get(oid.toString()); + if (cachedItem == null) { + throw new IllegalArgumentException(String.format("The item with the OID '%s' is not cached.", + oid.toString())); + } + + final CachedXml cachedXml = cachedItem.get(context); + if (cachedXml == null) { + throw new IllegalArgumentException(String. + format( + "The item with the OID '%s' is not cached for context '%s'", + oid.toString(), + context)); + } + + Element cacheElem; + if (listMode) { + cacheElem = cachedXml.getCachedXml(Mode.LIST_MODE); + } else { + cacheElem = cachedXml.getCachedXml(Mode.DEFAULT); + } + + if (cacheElem == null) { + if (listMode) { + throw new IllegalArgumentException(String.format( + "The item with the OID '%s' is not cached for context '%s' in list mode", + oid.toString(), + context)); + } else { + if (listMode) { + throw new IllegalArgumentException(String.format( + "The item with the OID '%s' is not cached for context '%s'.", + oid.toString(), + context)); + } + } + } + + cacheElem.syncDocs(); + + final Iterator> attrs = cacheElem.getAttributes().entrySet().iterator(); + Map.Entry attr; + while (attrs.hasNext()) { + attr = attrs.next(); + parent.addAttribute(attr.getKey(), attr.getValue()); + } + final Iterator childs = cacheElem.getChildren().iterator(); + while (childs.hasNext()) { + copyElement(parent, childs.next()); + } + } + + /** + * Convenient method for calling + * {@link #retrieveFromCache(com.arsdigita.xml.Element, OID, java.lang.String, boolean)} with + * {@code listMode = false}. + * + * @param parent + * @param oid + * @param context + * + * @see #retrieveFromCache(Element, OID, String, boolean) + */ + public void retrieveFromCache(final Element parent, final OID oid, final String context) { + retrieveFromCache(parent, oid, context, false); + } + + /** + * Convenient method for calling + * {@link #retrieveFromCache(com.arsdigita.xml.Element, OID, java.lang.String, boolean)} with {@code context = ""} + * and {@code listMode = false}. + * + * @param parent + * @param oid + * + * @see #retrieveFromCache(Element, OID, String, boolean) + */ + public void retrieveFromCache(final Element parent, final OID oid) { + retrieveFromCache(parent, oid, "", false); + } + + /** + * Cache the XML output of an item for the given context and list mode. + * + * @param oid The OID of the item to cache. + * @param item The item to cache. + * @param parent The root element of the items XML output. + * @param context The context of the XML output. + * @param listMode If the XML is for the list mode. + */ + public void cache(final OID oid, + final ContentItem item, + final Element parent, + final String context, + final boolean listMode) { + CachedItem cachedItem = (CachedItem) cache.get(oid.toString()); + if (cachedItem == null) { + cachedItem = new CachedItem(); + cache.put(oid.toString(), cachedItem); + } + + CachedXml cachedXml = cachedItem.get(context); + if (cachedXml == null) { + cachedXml = new CachedXml(); + cachedItem.put(context, cachedXml); + } + + final Element cacheElem = new Element("cachedItem"); + final Iterator> attrs = parent.getAttributes().entrySet().iterator(); + Map.Entry attr; + while (attrs.hasNext()) { + attr = attrs.next(); + cacheElem.addAttribute(attr.getKey(), attr.getValue()); + } + final Iterator childs = parent.getChildren().iterator(); + while (childs.hasNext()) { + copyElement(cacheElem, childs.next()); + } + + if (listMode) { + cachedXml.putCachedXml(Mode.LIST_MODE, cacheElem); + } else { + cachedXml.putCachedXml(Mode.DEFAULT, cacheElem); + } + + if (item.getDraftVersion() != null) { + cachedItems.put(item.getDraftVersion().getOID().toString(), oid.toString()); + } + + if ((item instanceof ContentPage) && (((ContentPage) item).getContentBundle() != null)) { + cachedItems.put(((ContentPage) item).getContentBundle().getOID().toString(), oid.toString()); + } + } + + /** + * Removes an object from the cache. + * + * @param oid The OID of the item to remove from the cache or the master id of the item to remove. + */ + public void removeFromCache(final OID oid) { + cache.remove(oid.toString()); + + final String cachedId = cachedItems.get(oid.toString()); + if (cachedId != null) { + cache.remove(cachedId); + cachedItems.remove(oid.toString()); + } + } + + private void copyElement(final Element parent, final Element element) { + final Element copy = parent.newChildElement(element.getName()); + final Iterator attrs = element.getAttributes().entrySet().iterator(); + Map.Entry attr; + while (attrs.hasNext()) { + attr = (Map.Entry) attrs.next(); + copy.addAttribute((String) attr.getKey(), (String) attr.getValue()); + } + + final Iterator childs = element.getChildren().iterator(); + while (childs.hasNext()) { + copyElement(copy, (Element) childs.next()); + } + + if (element.getText() != null) { + copy.setText(element.getText()); + } + + if (element.getCDATASection() != null) { + copy.setCDATASection(element.getCDATASection()); + } + + } + + private class CachedItem { + + private final Map cachedXml = new HashMap(); + + public CachedItem() { + //Nothing + } + + public CachedXml get(final String context) { + return cachedXml.get(context); + } + + public void put(final String context, final CachedXml cachedXml) { + this.cachedXml.put(context, cachedXml); + } + } + + private class CachedXml { + + private final Map cachedXml = new EnumMap(Mode.class); + + public CachedXml() { + //Nothing + } + + public Element getCachedXml(final Mode mode) { + return cachedXml.get(mode); + } + + public void putCachedXml(final Mode mode, final Element element) { + cachedXml.put(mode, element); + } + } + + private enum Mode { + + DEFAULT, + LIST_MODE,} +}