diff --git a/ccm-core/src/com/arsdigita/bebop/PageState.java b/ccm-core/src/com/arsdigita/bebop/PageState.java index 18cd3ac78..33db8ce4d 100755 --- a/ccm-core/src/com/arsdigita/bebop/PageState.java +++ b/ccm-core/src/com/arsdigita/bebop/PageState.java @@ -25,7 +25,9 @@ import com.arsdigita.bebop.util.Traversal; import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.developersupport.DeveloperSupport; import com.arsdigita.util.Assert; +import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.web.ParameterMap; +import com.arsdigita.web.RedirectSignal; import com.arsdigita.web.URL; import com.arsdigita.web.Web; import com.arsdigita.xml.Element; @@ -406,16 +408,6 @@ public class PageState { return m_response; } - /** - * Return the page component with index i from the page model. - * - * @pre (i>=0) && (i < size()) - * @post return != null - */ - private Component getComponent(int i) { - return m_page.getComponent(i); - } - /** * The index of a component in the page model * @@ -496,7 +488,7 @@ public class PageState { */ public void setVisible(final Component c, final boolean v) { if (Assert.isEnabled()) { - Assert.truth(getPage().stateContains(c), + Assert.isTrue(getPage().stateContains(c), "Component" + c + " is not registered on Page " + getPage()); } @@ -703,7 +695,7 @@ public class PageState { * @pre c == null || getPage().stateContains(c) */ public void setControlEvent(Component c, String name, String value) { - Assert.assertTrue(c == null || getPage().stateContains(c), + Assert.isTrue(c == null || getPage().stateContains(c), "c == null || getPage().stateContains(c)"); if ( m_grabbingComponent != null && m_grabbingComponent != c ) { throw new IllegalStateException @@ -1070,4 +1062,22 @@ public class PageState { + "}"; return result; } + + /** + * Clear the control event then redirect to the new page state. + * + * @param isCommitRequested indicates if a commit required before the redirect + * + * @throws RedirectSignal to the new page state + * + * @see RedirectSignal#RedirectSignal(String, boolean) + */ + public void redirectWithoutControlEvent(boolean isCommitRequested) { + clearControlEvent(); + try { + throw new RedirectSignal(stateAsURL(), true); + } catch (IOException ioe) { + throw new UncheckedWrapperException(ioe); + } + } } diff --git a/ccm-core/src/com/arsdigita/caching/CacheServlet.java b/ccm-core/src/com/arsdigita/caching/CacheServlet.java index 902d5b989..e843b9052 100755 --- a/ccm-core/src/com/arsdigita/caching/CacheServlet.java +++ b/ccm-core/src/com/arsdigita/caching/CacheServlet.java @@ -63,6 +63,7 @@ public class CacheServlet extends HttpServlet { private static final String ID = "id"; private static final String KEY = "key"; private static final String HASH = "hash"; + private static final String REMOVEALL = "removeAll"; // If you change this, make sure that web.xml is changed as well static final String SERVLET_URL = "/expireCache"; @@ -73,16 +74,43 @@ public class CacheServlet extends HttpServlet { */ protected void doGet( HttpServletRequest req, HttpServletResponse res ) { String id = req.getParameter( ID ); - String key = req.getParameter( KEY ); - + String key = req.getParameter( KEY ); + String removeAll = req.getParameter( REMOVEALL ); + if (s_log.isInfoEnabled()) { s_log.info("Got remove request from " + req.getRemoteHost()); } - if (id == null || key == null) { - return; + if (id != null && key != null){ + //normal expire cache entry request + if (s_log.isInfoEnabled()) { + s_log.info("Got remove request from " + req.getRemoteHost()); + } + + String hash = req.getParameter( HASH ); + expireCacheEntry(id, key, hash); + } else if (id != null && key == null && removeAll != null) { + //purge a single cache request + if (s_log.isInfoEnabled()) { + s_log.info("Got remove all entries request from " + req.getRemoteHost()); + } + if(removeAll.equals("true")){ + removeCacheEntries(id); + } + } else if (id == null && key == null && removeAll != null) { + //purge all caches request + if (s_log.isInfoEnabled()) { + s_log.info("Got remove all cache request from " + req.getRemoteHost()); + } + if(removeAll.equals("true")){ + removeAllCache(); + } + } else { + s_log.error("Got an invalid cache request from " + req.getRemoteHost()); } - + } + + protected void expireCacheEntry(String id, String key, String hash) { id = URLDecoder.decode(id); key = URLDecoder.decode(key); @@ -94,7 +122,6 @@ public class CacheServlet extends HttpServlet { s_log.debug("Removing " + key + " from cache " + id); - final String hash = req.getParameter( HASH ); final Integer hashCode = getHashCode(hash); if (hashCode == null) { // unconditionally remove @@ -104,6 +131,25 @@ public class CacheServlet extends HttpServlet { } } + protected void removeCacheEntries(String id) { + id = URLDecoder.decode(id); + final CacheTable cache = CacheTable.getCache( id ); + if (cache == null) { + s_log.debug("No cache with id " + id); + return; + } + + s_log.debug("Removing all entries from cache " + id); + // unconditionally remove + cache.removeAllEntriesLocally(); + } + + protected void removeAllCache() { + s_log.debug("Removing all Cache tables"); + // unconditionally remove all + CacheTable.removeAllCacheTablesLocally(); + } + private Integer getHashCode(final String hash) { if (hash == null) { return null; @@ -134,7 +180,33 @@ public class CacheServlet extends HttpServlet { removeFromPeers(cache_id, key); } - + + /** + * remote all entries from all purge-able tables in the peer's. + * + * The fact that there is no ID parameter tells the + * peers to purge all cache tables. + */ + static void removeAllFromPeers() { + final ParameterMap params = new ParameterMap(); + params.setParameter(REMOVEALL, "true"); + + notifyPeers(params); + } + + /** + * remote all entries from the peer's cache table with an id of cacheID. + * + * @param cacheID id of the cache table to purge + */ + static void removeAllEntriesFromPeersTable(String cacheID) { + final ParameterMap params = new ParameterMap(); + params.setParameter(ID, cacheID); + params.setParameter(REMOVEALL, "true"); + + notifyPeers(params); + } + /** * Sometimes we need to remove entries only from peer webservers. */ @@ -158,7 +230,11 @@ public class CacheServlet extends HttpServlet { */ private static void notifyPeers(final String id, final String key, - final String hash) { + final String hash) { + notifyPeers(makeParameterMap(id, key, hash)); + } + + private static void notifyPeers(ParameterMap params) { if (!Web.getConfig().getDeactivateCacheHostNotifications()) { s_log.debug("about to notify peers"); final Session session = SessionManager.getSession(); @@ -174,7 +250,7 @@ public class CacheServlet extends HttpServlet { f.set("currPort", new Integer(current.getPort())); while (hosts.next()) { final Host host = (Host) hosts.getDomainObject(); - notifyPeer(host, makeParameterMap(id, key, hash)); + notifyPeer(host, params); } } } diff --git a/ccm-core/src/com/arsdigita/caching/CacheTable.java b/ccm-core/src/com/arsdigita/caching/CacheTable.java index f09c90015..dfef2dcce 100755 --- a/ccm-core/src/com/arsdigita/caching/CacheTable.java +++ b/ccm-core/src/com/arsdigita/caching/CacheTable.java @@ -27,6 +27,7 @@ import java.math.BigDecimal; import java.util.Date; import java.util.HashSet; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -229,19 +230,14 @@ public class CacheTable { } public void setPurgeAllowed(boolean purgeAllowed) { - this.purgeAllowed = purgeAllowed; + this.purgeAllowed = purgeAllowed; } - public boolean isPurgeAllowed() { - return purgeAllowed; + public boolean isPurgeAllowed() { + return purgeAllowed; } - private void removeLRUEntry() { - m_list.removeLRUEntry(); - } - - /** * A convenience wrapper around {@link #put(String, Object)}. * @@ -425,6 +421,39 @@ public class CacheTable { } + public synchronized void removeAllEntriesLocally() { + m_list.clear(); + + if (s_log.isDebugEnabled()) { + s_log.debug("removed all entries from cache table " + m_cacheID); + } + } + + public static void removeAllCacheTables() { + if (s_log.isDebugEnabled()) { + s_log.debug("remove all entries from all purge-able cache tables"); + } + removeAllCacheTablesLocally(); + CacheServlet.removeAllFromPeers(); + } + + /** + * Iterator over all CacheTables in the cache + * and clear all purge-able ones + */ + public static synchronized void removeAllCacheTablesLocally() { + for(Iterator it = s_caches.values().iterator(); it.hasNext(); ) { + CacheTable ct = (CacheTable)it.next(); + if (ct.isPurgeAllowed()) { + ct.removeAll(); + if (s_log.isDebugEnabled()) { + s_log.debug("removed all entries from cache table " + ct.m_cacheID); + } + } + } + } + + /** *
Retrieves the object stored in cache. If no object by the * passed key can be found in cache (maybe because it's expired or @@ -468,6 +497,7 @@ public class CacheTable { String isShared(String tableID); boolean isPurgeAllowed(String tableID); void purge(String tableID); + void purgeAll(); } private static class BrowserImpl implements Browser { @@ -499,15 +529,19 @@ public class CacheTable { } public boolean isPurgeAllowed(String tableID) { - return getCacheTable(tableID).isPurgeAllowed(); + return getCacheTable(tableID).isPurgeAllowed(); } public void purge(String tableID) { - CacheTable table = getCacheTable(tableID); - if (!table.isPurgeAllowed()) { - throw new RuntimeException("Table "+tableID+" can't be purged."); - } - table.removeAll(); + CacheTable table = getCacheTable(tableID); + if (!table.isPurgeAllowed()) { + throw new RuntimeException("Table "+tableID+" can't be purged."); + } + table.removeAll(); + } + + public void purgeAll() { + CacheTable.removeAllCacheTables(); } private static CacheTable getCacheTable(String tableID) { diff --git a/ccm-core/src/com/arsdigita/webdevsupport/CacheTableBrowser.java b/ccm-core/src/com/arsdigita/webdevsupport/CacheTableBrowser.java index 9de0d51fb..ff2e19356 100755 --- a/ccm-core/src/com/arsdigita/webdevsupport/CacheTableBrowser.java +++ b/ccm-core/src/com/arsdigita/webdevsupport/CacheTableBrowser.java @@ -18,19 +18,6 @@ */ package com.arsdigita.webdevsupport; -import com.arsdigita.bebop.ControlLink; -import com.arsdigita.bebop.Label; -import com.arsdigita.bebop.PageState; -import com.arsdigita.bebop.SimpleContainer; -import com.arsdigita.bebop.Table; -import com.arsdigita.bebop.event.TableActionEvent; -import com.arsdigita.bebop.event.TableActionListener; -import com.arsdigita.bebop.table.TableModel; -import com.arsdigita.bebop.table.TableModelBuilder; -import com.arsdigita.caching.CacheTable; -import com.arsdigita.caching.CacheTable.TimestampedEntry; -import com.arsdigita.util.LockableImpl; - import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -40,6 +27,23 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.ListPanel; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.event.TableActionListener; +import com.arsdigita.bebop.table.TableModel; +import com.arsdigita.bebop.table.TableModelBuilder; +import com.arsdigita.caching.CacheTable; +import com.arsdigita.caching.CacheTable.TimestampedEntry; +import com.arsdigita.util.LockableImpl; + /** * @author Vadim Nasardinov (vadimn@redhat.com) @@ -56,23 +60,35 @@ final class CacheTableBrowser extends SimpleContainer { m_tableContents = new CacheTableContents(); m_listOfTables.addTableActionListener(new TableActionListener() { public void cellSelected(TableActionEvent ev) { - int column = ev.getColumn().intValue(); - switch (column) { - case 0: + int column = ev.getColumn().intValue(); + switch (column) { + case 0: PageState state = ev.getPageState(); - m_tableContents.setTableID(state,(String) ev.getRowKey()); - break; - case 5: - CacheTable.BROWSER.purge((String) ev.getRowKey()); - break; - default: - break; - } + m_tableContents.setTableID(state, (String) ev.getRowKey()); + break; + case 5: + CacheTable.BROWSER.purge((String) ev.getRowKey()); + ev.getPageState().redirectWithoutControlEvent(false); + default: + break; } + } - public void headSelected(TableActionEvent e) { } - }); + public void headSelected(TableActionEvent e) { + } + }); + ActionLink purgeAllLink = new ActionLink("Purge all caches"); + purgeAllLink.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + CacheTable.BROWSER.purgeAll(); + e.getPageState().redirectWithoutControlEvent(false); + } + }); + ListPanel container = new ListPanel(false); + container.add(purgeAllLink); + add(container); + add(m_listOfTables); add(m_tableContents); } @@ -137,7 +153,11 @@ final class CacheTableBrowser extends SimpleContainer { return String.valueOf(CacheTable.BROWSER.isShared(m_key)); case 5: if (CacheTable.BROWSER.isPurgeAllowed(m_key)) { - return new ControlLink(new Label("purge")); + if (CacheTable.BROWSER.getCurrentSize(m_key) > 0) { + return new ControlLink(new Label("purge")); + } else { + return new Label("(empty)"); + } } else { return new Label("can't be purged"); } @@ -161,8 +181,6 @@ final class CacheTableBrowser extends SimpleContainer { private final static String TABLE_ID_ATTR = CacheTableContents.class.getName(); - private String m_tableID; - public CacheTableContents() { super(new ModelBuilder(), new String[] { "Key", "Class", "Value", "Hits", "Hash code", "Timestamp" });