📄 cachingprovider.java
字号:
/* JSPWiki - a JSP-based WikiWiki clone. Copyright (C) 2001-2005 Janne Jalkanen (Janne.Jalkanen@iki.fi) This program 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 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */package com.ecyrd.jspwiki.providers;import java.io.IOException;import java.io.StringReader;import java.io.StringWriter;import java.util.*;import org.apache.log4j.Logger;import com.ecyrd.jspwiki.*;import com.ecyrd.jspwiki.util.ClassUtil;import com.opensymphony.oscache.base.Cache;import com.opensymphony.oscache.base.NeedsRefreshException;import com.opensymphony.oscache.base.events.*;/** * Provides a caching page provider. This class rests on top of a * real provider class and provides a cache to speed things up. Only * if the cache copy of the page text has expired, we fetch it from * the provider. * <p> * This class also detects if someone has modified the page * externally, not through JSPWiki routines, and throws the proper * RepositoryModifiedException. * <p> * Heavily based on ideas by Chris Brooking. * <p> * Since 2.1.52 uses the OSCache library from OpenSymphony. * * @author Janne Jalkanen * @since 1.6.4 * @see RepositoryModifiedException */// FIXME: Synchronization is a bit inconsistent in places.// FIXME: A part of the stuff is now redundant, since we could easily use the text cache// for a lot of things. RefactorMe.public class CachingProvider implements WikiPageProvider{ private static final Logger log = Logger.getLogger(CachingProvider.class); private WikiPageProvider m_provider; // FIXME: Find another way to the search engine to use instead of from WikiEngine? private WikiEngine m_engine; private Cache m_cache; private Cache m_negCache; // Cache for holding non-existing pages private Cache m_textCache; private Cache m_historyCache; private long m_cacheMisses = 0; private long m_cacheHits = 0; private long m_historyCacheMisses = 0; private long m_historyCacheHits = 0; private int m_expiryPeriod = 30; /** * This can be very long, as normally all modifications are noticed in an earlier * stage. */ private int m_pageContentExpiryPeriod = 24*60*60; // FIXME: This MUST be cached somehow. private boolean m_gotall = false; private CacheItemCollector m_allCollector = new CacheItemCollector(); /** * Defines, in seconds, the amount of time a text will live in the cache * at most before requiring a refresh. */ public static final String PROP_CACHECHECKINTERVAL = "jspwiki.cachingProvider.cacheCheckInterval"; public static final String PROP_CACHECAPACITY = "jspwiki.cachingProvider.capacity"; private static final int DEFAULT_CACHECAPACITY = 1000; // Good most wikis private static final String OSCACHE_ALGORITHM = "com.opensymphony.oscache.base.algorithm.LRUCache"; public void initialize( WikiEngine engine, Properties properties ) throws NoRequiredPropertyException, IOException { log.debug("Initing CachingProvider"); // // Cache consistency checks // m_expiryPeriod = TextUtil.getIntegerProperty( properties, PROP_CACHECHECKINTERVAL, m_expiryPeriod ); log.debug("Cache expiry period is "+m_expiryPeriod+" s"); // // Text cache capacity // int capacity = TextUtil.getIntegerProperty( properties, PROP_CACHECAPACITY, DEFAULT_CACHECAPACITY ); log.debug("Cache capacity "+capacity+" pages."); m_cache = new Cache( true, false, true ); // // OSCache documentation sucks big time. The clazz-parameter is completely // undefined; I had to read the source code to figure out that you need // to declare what type of a listener you are adding by sending the type // of the interface. // m_cache.addCacheEventListener( m_allCollector, CacheEntryEventListener.class ); m_negCache = new Cache( true, false, true ); m_textCache = new Cache( true, false, true, false, OSCACHE_ALGORITHM, capacity ); m_historyCache = new Cache( true, false, true, false, OSCACHE_ALGORITHM, capacity ); // // Find and initialize real provider. // String classname = WikiEngine.getRequiredProperty( properties, PageManager.PROP_PAGEPROVIDER ); // engine is used for getting the search engine m_engine = engine; try { Class providerclass = ClassUtil.findClass( "com.ecyrd.jspwiki.providers", classname ); m_provider = (WikiPageProvider)providerclass.newInstance(); log.debug("Initializing real provider class "+m_provider); m_provider.initialize( engine, properties ); } catch( ClassNotFoundException e ) { log.error("Unable to locate provider class "+classname,e); throw new IllegalArgumentException("no provider class"); } catch( InstantiationException e ) { log.error("Unable to create provider class "+classname,e); throw new IllegalArgumentException("faulty provider class"); } catch( IllegalAccessException e ) { log.error("Illegal access to provider class "+classname,e); throw new IllegalArgumentException("illegal provider class"); } } private WikiPage getPageInfoFromCache( String name ) throws ProviderException, RepositoryModifiedException { // Sanity check; seems to occur sometimes if( name == null ) return null; try { WikiPage item = (WikiPage)m_cache.getFromCache( name, m_expiryPeriod ); if( item != null ) return item; return null; } catch( NeedsRefreshException e ) { WikiPage cached = (WikiPage)e.getCacheContent(); // int version = (cached != null) ? cached.getVersion() : WikiPageProvider.LATEST_VERSION; WikiPage refreshed = m_provider.getPageInfo( name, WikiPageProvider.LATEST_VERSION ); if( refreshed == null && cached != null ) { // Page has been removed evilly by a goon from outer space log.debug("Page "+name+" has been removed externally."); m_cache.putInCache( name, null ); m_textCache.putInCache( name, null ); m_historyCache.putInCache( name, null ); // We cache a page miss m_negCache.putInCache( name, name ); throw new RepositoryModifiedException( "Removed: "+name, name ); } else if( cached == null ) { // The page did not exist in the first place if( refreshed != null ) { // We must now add it m_cache.putInCache( name, refreshed ); // Requests for this page are now no longer denied m_negCache.putInCache( name, null ); throw new RepositoryModifiedException( "Added: "+name, name ); // return refreshed; } else { // Cache page miss m_negCache.putInCache( name, name ); m_cache.cancelUpdate( name ); } } else if( cached.getVersion() != refreshed.getVersion() ) { // The newest version has been deleted, but older versions still remain log.debug("Page "+cached.getName()+" newest version deleted, reloading..."); m_cache.putInCache( name, refreshed ); // Requests for this page are now no longer denied m_negCache.putInCache( name, null ); m_textCache.flushEntry( name ); m_historyCache.flushEntry( name ); return refreshed; } else if( Math.abs(refreshed.getLastModified().getTime()-cached.getLastModified().getTime()) > 1000L ) { // Yes, the page has been modified externally and nobody told us log.info("Page "+cached.getName()+" changed, reloading..."); m_cache.putInCache( name, refreshed ); // Requests for this page are now no longer denied m_negCache.putInCache( name, null ); m_textCache.flushEntry( name ); m_historyCache.flushEntry( name ); throw new RepositoryModifiedException( "Modified: "+name, name ); } else { // Refresh the cache by putting the same object back m_cache.putInCache( name, cached ); // Requests for this page are now no longer denied m_negCache.putInCache( name, null ); } return cached; } } public boolean pageExists( String pageName ) { if( pageName == null ) return false; // // First, check the negative cache if we've seen it before // try { String isNonExistant = (String) m_negCache.getFromCache( pageName, m_expiryPeriod ); if( isNonExistant != null ) return false; // No such page } catch( NeedsRefreshException e ) { m_negCache.cancelUpdate(pageName); } WikiPage p = null; try { p = getPageInfoFromCache( pageName ); } catch( RepositoryModifiedException e ) { // The repository was modified, we need to check now if the page was removed or // added. // TODO: This information would be available in the exception, but we would // need to subclass. try { p = getPageInfoFromCache( pageName ); } catch( Exception ex ) { return false; } // This should not happen } catch( ProviderException e ) { log.info("Provider failed while trying to check if page exists: "+pageName); return false; } // // A null item means that the page either does not // exist, or has not yet been cached; a non-null // means that the page does exist. // if( p != null ) { return true; } // // If we have a list of all pages in memory, then any page // not in the cache must be non-existent. // // FIXME: There's a problem here; if someone modifies the // repository by adding a page outside JSPWiki, // we won't notice it. if( m_gotall ) { return false; } // // We could add the page to the cache here as well, // but in order to understand whether that is a // good thing or not we would need to analyze // the JSPWiki calling patterns extensively. Presumably // it would be a good thing if pageExists() is called // many times before the first getPageText() is called, // and the whole page is cached. // return m_provider.pageExists( pageName ); } /** * @throws RepositoryModifiedException If the page has been externally modified. */ public String getPageText( String pageName, int version ) throws ProviderException, RepositoryModifiedException { String result = null; if( pageName == null ) return null; if( version == WikiPageProvider.LATEST_VERSION ) { result = getTextFromCache( pageName ); } else { WikiPage p = getPageInfoFromCache( pageName ); // // Or is this the latest version fetched by version number? // if( p != null && p.getVersion() == version ) { result = getTextFromCache( pageName ); } else { result = m_provider.getPageText( pageName, version ); } } return result;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -