You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by me...@apache.org on 2009/08/30 17:48:07 UTC

svn commit: r809361 - in /incubator/jspwiki/trunk: ./ src/WebContent/WEB-INF/classes/plugin/ src/java/org/apache/wiki/ src/java/org/apache/wiki/content/ src/java/org/apache/wiki/plugin/ tests/java/org/apache/wiki/plugin/

Author: metskem
Date: Sun Aug 30 15:48:06 2009
New Revision: 809361

URL: http://svn.apache.org/viewvc?rev=809361&view=rev
Log:
3.0.0-svn-150
        
        * Introduced the new PageViewPlugin written by Andre van Dalen.
        This plugin counts page views and presents the statistics on page views
        See http://www.jspwiki.org/wiki/PageViewPlugin for the complete
        documentation.
        A bit of i18n work for the translators too.....

Added:
    incubator/jspwiki/trunk/src/java/org/apache/wiki/plugin/PageViewPlugin.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/PageViewPluginTest.java
Modified:
    incubator/jspwiki/trunk/ChangeLog
    incubator/jspwiki/trunk/README
    incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources.properties
    incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources_nl.properties
    incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/content/ReferenceManager.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/AllTests.java

Modified: incubator/jspwiki/trunk/ChangeLog
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/ChangeLog?rev=809361&r1=809360&r2=809361&view=diff
==============================================================================
--- incubator/jspwiki/trunk/ChangeLog (original)
+++ incubator/jspwiki/trunk/ChangeLog Sun Aug 30 15:48:06 2009
@@ -1,3 +1,16 @@
+2009-08-30 Harry Metske <me...@apache.org>
+
+        * 3.0.0-svn-150
+        
+        * Introduced the new PageViewPlugin written by Andre van Dalen.
+        This plugin counts page views and presents the statistics on page views
+        See http://www.jspwiki.org/wiki/PageViewPlugin for the complete
+        documentation.
+        A bit of i18n work for the translators too.....
+        
+        * a bit less verbosity in ReferenceManager.checkValueString(), 
+        we now only dump the illegal page name instead of dumping a stacktrace.
+        
 2009-08-30 Dirk Frederickx <br...@apache.org>
 
         * 3.0.0-svn-149 - align with 2.8.3-svn-16

Modified: incubator/jspwiki/trunk/README
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/README?rev=809361&r1=809360&r2=809361&view=diff
==============================================================================
--- incubator/jspwiki/trunk/README (original)
+++ incubator/jspwiki/trunk/README Sun Aug 30 15:48:06 2009
@@ -182,6 +182,7 @@
 The following people (in alphabetical order) have contributed code to JSPWiki:
 
 Andrew R. Jaquith
+Andre van Dalen
 Arent-Jan Banck
 Christoph Sauer
 Chuck Smith

Modified: incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources.properties
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources.properties?rev=809361&r1=809360&r2=809361&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources.properties (original)
+++ incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources.properties Sun Aug 30 15:48:06 2009
@@ -108,4 +108,7 @@
 plugin.interwikilinks.noconstruct=Unable to construct InterwikiLinks plugin output (see logs)
 
 # UndefinedPagesPlugin
-plugin.undefined.parm.invalid=parameter is not valid for the UndefinedPagesPlugin
\ No newline at end of file
+plugin.undefined.parm.invalid=parameter is not valid for the UndefinedPagesPlugin
+
+# PageViewPlugin
+plugin.pageview.error=exception while executing PageViewPlugin, stacktrace follows
\ No newline at end of file

Modified: incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources_nl.properties
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources_nl.properties?rev=809361&r1=809360&r2=809361&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources_nl.properties (original)
+++ incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/plugin/PluginResources_nl.properties Sun Aug 30 15:48:06 2009
@@ -117,4 +117,7 @@
 plugin.interwikilinks.noconstruct=Kon InterwikiLinks plugin output niet genereren (kijk in de logs)
 
 # UndefinedPagesPlugin
-plugin.undefined.parm.invalid=parameter is not geldig voor de UndefinedPagesPlugin
\ No newline at end of file
+plugin.undefined.parm.invalid=parameter is not geldig voor de UndefinedPagesPlugin
+
+# PageViewPlugin
+plugin.pageview.error=fout bij het uitvoeren van PageViewPlugin, stacktrace volgt
\ No newline at end of file

Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java?rev=809361&r1=809360&r2=809361&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java Sun Aug 30 15:48:06 2009
@@ -77,7 +77,7 @@
      *  <p>
      *  If the build identifier is empty, it is not added.
      */
-    public static final String     BUILD         = "149";
+    public static final String     BUILD         = "150";
 
     /**
      *  This is the generic version string you should use

Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/content/ReferenceManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/ReferenceManager.java?rev=809361&r1=809360&r2=809361&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/content/ReferenceManager.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/content/ReferenceManager.java Sun Aug 30 15:48:06 2009
@@ -1062,10 +1062,11 @@
      */
     private void checkValueString( String v )
     {
+        int ch = 0;
         boolean highChar = false;
         for ( int i = 0; i < v.length(); i++ )
         {
-            int ch = v.charAt( i );
+            ch = v.charAt( i );
             if ( ch < 32 || ch > 127 )
             {
                 highChar = true;
@@ -1075,7 +1076,8 @@
         }
         if ( highChar )
         {
-            Thread.dumpStack();
+            System.out.println( "non Roman value detected in String " + v );
+//            Thread.dumpStack();
         }
     }
 

Added: incubator/jspwiki/trunk/src/java/org/apache/wiki/plugin/PageViewPlugin.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/plugin/PageViewPlugin.java?rev=809361&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/plugin/PageViewPlugin.java (added)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/plugin/PageViewPlugin.java Sun Aug 30 15:48:06 2009
@@ -0,0 +1,809 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.  
+ */
+package org.apache.wiki.plugin;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.wiki.content.ReferenceManager;
+import org.apache.wiki.util.RegExpUtil;
+import org.apache.wiki.util.TextUtil;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.WikiEngine;
+import org.apache.wiki.api.PluginException;
+import org.apache.wiki.api.WikiPage;
+import org.apache.wiki.event.WikiEngineEvent;
+import org.apache.wiki.event.WikiEvent;
+import org.apache.wiki.event.WikiEventListener;
+import org.apache.wiki.log.Logger;
+import org.apache.wiki.log.LoggerFactory;
+import org.apache.wiki.plugin.InitializablePlugin;
+import org.apache.wiki.plugin.PluginManager;
+import org.apache.wiki.plugin.WikiPlugin;
+import org.apache.wiki.providers.ProviderException;
+import org.apache.wiki.util.WikiBackgroundThread;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Counts the number of times each page has been viewed. Parameters:
+ * count=yes|no show=none|count|list entries=max number of list entries
+ * min=minimum page count to be listed sort=name|count Default values are
+ * show=none and sort=name.
+ * 
+ * @since 2.8
+ */
+public class PageViewPlugin extends AbstractFilteredPlugin implements WikiPlugin, InitializablePlugin
+{
+    private static final Logger log = LoggerFactory.getLogger( PageViewPlugin.class );
+
+    /** The page view manager. */
+    private static PageViewManager c_singleton;
+
+    /** Constant for the 'count' parameter / value. */
+    private static final String PARAM_COUNT = "count";
+
+    /** Name of the 'entries' parameter. */
+    private static final String PARAM_MAX_ENTRIES = "entries";
+
+    /** Name of the 'max' parameter. */
+    private static final String PARAM_MAX_COUNT = "max";
+
+    /** Name of the 'min' parameter. */
+    private static final String PARAM_MIN_COUNT = "min";
+
+    /** Name of the 'refer' parameter. */
+    private static final String PARAM_REFER = "refer";
+
+    /** Name of the 'sort' parameter. */
+    private static final String PARAM_SORT = "sort";
+
+    /** Constant for the 'none' parameter value. */
+    private static final String STR_NONE = "none";
+
+    /** Constant for the 'list' parameter value. */
+    private static final String STR_LIST = "list";
+
+    /** Constant for the 'yes' parameter value. */
+    private static final String STR_YES = "yes";
+
+    /** Constant for empty string. */
+    private static final String STR_EMPTY = "";
+
+    /** Constant for Wiki markup separator. */
+    private static final String STR_SEPARATOR = "----";
+
+    /** Constant for comma-separated list separator. */
+    private static final String STR_COMMA = ",";
+
+    /** Constant for no-op glob exression. */
+    private static final String STR_GLOBSTAR = "*";
+
+    /** Constant for file storage. */
+    private static final String COUNTER_PAGE = "PageCount.txt";
+
+    /** Constant for storage interval in seconds. */
+    private static final int STORAGE_INTERVAL = 60;
+
+    /**
+     * Initialize the PageViewPlugin and its singleton.
+     * 
+     * @param engine The wiki engine.
+     */
+    public void initialize( WikiEngine engine )
+    {
+
+        log.info( "initializing PageViewPlugin" );
+
+        synchronized( this )
+        {
+            if( c_singleton == null )
+            {
+
+                c_singleton = new PageViewManager(  );
+
+                c_singleton.initialize( engine );
+            }
+        }
+    }
+
+    /**
+     * Cleanup the singleton reference.
+     */
+    private void cleanup()
+    {
+
+        log.info( "cleaning up PageView Manager" );
+
+        c_singleton = null;
+    }
+
+    /**
+     *  {@inheritDoc}
+     */
+    public String execute( WikiContext context, Map<String,Object> params ) throws PluginException
+    {
+        ResourceBundle rb = context.getBundle(WikiPlugin.CORE_PLUGINS_RESOURCEBUNDLE);
+        PageViewManager manager = c_singleton;
+        String result = STR_EMPTY;
+
+        if( manager != null )
+        {
+
+            try
+            {
+                result = manager.execute( context, params );
+            }
+            catch( ProviderException e )
+            {
+               log.error("exception while executing PageViewPlugin, stacktrace follows",e);
+               result = rb.getString( "plugin.pageview.error" );
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Page view manager, handling all storage.
+     */
+    public final class PageViewManager implements WikiEventListener
+    {
+        /** Are we initialized? */
+        private boolean m_initialized;
+
+        /** The page counters. */
+        private Map<String, Counter> m_counters;
+
+        /** The page counters in storage format. */
+        private Properties m_storage;
+
+        /** Are all changes stored? */
+        private boolean m_dirty;
+
+        /** The page count storage background thread. */
+        private Thread m_pageCountSaveThread;
+
+        /** The work directory. */
+        private String m_workDir ;
+
+        /** Comparator for descending sort on page count. */
+        private final Comparator<Object> m_compareCountDescending = new Comparator<Object>() {
+            public int compare( Object o1, Object o2 )
+            {
+                final int v1 = getCount( o1 );
+                final int v2 = getCount( o2 );
+                return (v1 == v2) ? ((String) o1).compareTo( (String) o2 ) : (v1 < v2) ? 1 : -1;
+            }
+        };
+
+        /**
+         * Initialize the page view manager.
+         * 
+         * @param engine The wiki engine.
+         */
+        public synchronized void initialize( WikiEngine engine )
+        {
+
+            log.info( "initializing PageView Manager" );
+
+            m_workDir = engine.getWorkDir();
+
+            engine.addWikiEventListener( this );
+
+            if( m_counters == null )
+            {
+
+                // Load the counters into a collection
+                m_storage = new Properties();
+                m_counters = new TreeMap<String, Counter>();
+
+                loadCounters();
+            }
+
+            // backup counters every 5 minutes
+            if( m_pageCountSaveThread == null )
+            {
+                m_pageCountSaveThread = new CounterSaveThread( engine, 5 * STORAGE_INTERVAL, this );
+                m_pageCountSaveThread.start();
+            }
+
+            m_initialized = true;
+
+        }
+
+        /**
+         * Handle the shutdown event via the page counter thread.
+         * 
+         */
+        private synchronized void handleShutdown()
+        {
+
+            log.info( "handleShutdown: The counter store thread was shut down." );
+
+            cleanup();
+
+            if( m_counters != null )
+            {
+
+                m_dirty = true;
+                storeCounters();
+
+                m_counters.clear();
+                m_counters = null;
+
+                m_storage.clear();
+                m_storage = null;
+            }
+
+            m_initialized = false;
+
+            m_pageCountSaveThread = null;
+        }
+
+        /**
+         * Inspect wiki events for shutdown.
+         * 
+         * @param event The wiki event to inspect.
+         */
+        public void actionPerformed( WikiEvent event )
+        {
+
+            if( event instanceof WikiEngineEvent )
+            {
+                if( event.getType() == WikiEngineEvent.SHUTDOWN )
+                {
+
+                    log.info( "Detected wiki engine shutdown" );
+                    handleShutdown();
+                }
+            }
+        }
+
+        /**
+         * Count a page hit, present a pages' counter or output a list of
+         * pagecounts.
+         * 
+         * @param context the wiki context
+         * @param params the plugin parameters
+         * @return String Wiki page snippet
+         * @throws PluginException Malformed pattern parameter.
+         * @throws ProviderException if something goes wrong with finding pages
+         */
+        public String execute( WikiContext context, Map<String,Object> params ) throws PluginException, ProviderException
+        {
+            WikiEngine engine = context.getEngine();
+            WikiPage page = context.getPage();
+            String result = STR_EMPTY;
+
+            if( page != null )
+            {
+                // get parameters
+                String pagename = page.getName();
+                String count =  (String) params.get( PARAM_COUNT );
+                String show =  (String) params.get( PARAM_SHOW );
+                int entries = TextUtil.parseIntParameter(  (String) params.get( PARAM_MAX_ENTRIES ), Integer.MAX_VALUE );
+                final int max = TextUtil.parseIntParameter(  (String) params.get( PARAM_MAX_COUNT ), Integer.MAX_VALUE );
+                final int min = TextUtil.parseIntParameter(  (String) params.get( PARAM_MIN_COUNT ), Integer.MIN_VALUE );
+                String sort =  (String) params.get( PARAM_SORT );
+                String body =  (String) params.get( PluginManager.PARAM_BODY );
+                Pattern[] exclude = compileGlobs( PARAM_EXCLUDE,  (String) params.get( PARAM_EXCLUDE ) );
+                Pattern[] include = compileGlobs( PARAM_INCLUDE,  (String) params.get( PARAM_INCLUDE ) );
+                Pattern[] refer = compileGlobs( PARAM_REFER,  (String) params.get( PARAM_REFER ) );
+                boolean increment = false;
+
+                // increment counter?
+                if( STR_YES.equals( count ) )
+                {
+                    increment = true;
+                }
+                else
+                {
+                    count = null;
+                }
+
+                // default increment counter?
+                if( (show == null || STR_NONE.equals( show )) && count == null )
+                {
+                    increment = true;
+                }
+
+                // filter on referring pages?
+                Collection<String> referrers = null;
+
+                if( refer != null )
+                {
+                    ReferenceManager refManager = engine.getReferenceManager();
+
+                    Iterator iter = refManager.findCreated().iterator();
+
+                    while ( iter != null && iter.hasNext() )
+                    {
+
+                        String name = (String) iter.next();
+                        boolean use = false;
+
+                        for( int n = 0; !use && n < refer.length; n++ )
+                        {
+                            Matcher matcher = refer[n].matcher( name );
+                            use = matcher.matches( );
+                        }
+
+                        if( use )
+                        {
+                            Collection<String> refs = engine.getReferenceManager().findRefersTo( name );
+
+                            if( refs != null && !refs.isEmpty() )
+                            {
+                                if( referrers == null )
+                                {
+                                    referrers = new HashSet<String>();
+                                }
+                                referrers.addAll( refs );
+                            }
+                        }
+                    }
+                }
+
+                synchronized( this )
+                {
+                    Counter counter = m_counters.get( pagename );
+
+                    // only count in view mode, keep storage values in sync
+                    if( increment && WikiContext.VIEW.equalsIgnoreCase( context.getRequestContext() ) )
+                    {
+                        if( counter == null )
+                        {
+                            counter = new Counter();
+                            m_counters.put( pagename, counter );
+                        }
+                        counter.increment();
+                        m_storage.setProperty( pagename, counter.toString() );
+                        m_dirty = true;
+                    }
+
+                    if( show == null || STR_NONE.equals( show ) )
+                    {
+                        // nothing to show
+
+                    }
+                    else if( PARAM_COUNT.equals( show ) )
+                    {
+                        // show page count
+                        result = counter.toString();
+
+                    }
+                    else if( body != null && 0 < body.length() && STR_LIST.equals( show ) )
+                    {
+                        // show list of counts
+                        String header = STR_EMPTY;
+                        String line = body;
+                        String footer = STR_EMPTY;
+                        int start = body.indexOf( STR_SEPARATOR );
+
+                        // split body into header, line, footer on ----
+                        // separator
+                        if( 0 < start )
+                        {
+                            header = body.substring( 0, start );
+
+                            start = skipWhitespace( start + STR_SEPARATOR.length(), body );
+
+                            int end = body.indexOf( STR_SEPARATOR, start );
+
+                            if( start >= end )
+                            {
+                                line = body.substring( start );
+
+                            }
+                            else
+                            {
+                                line = body.substring( start, end );
+
+                                end = skipWhitespace( end + STR_SEPARATOR.length(), body );
+
+                                footer = body.substring( end );
+                            }
+                        }
+
+                        // sort on name or count?
+                        Map<String, Counter> sorted = m_counters;
+
+                        if( sort != null && PARAM_COUNT.equals( sort ) )
+                        {
+                            sorted = new TreeMap<String, Counter>( m_compareCountDescending );
+
+                            sorted.putAll( m_counters );
+                        }
+
+                        // build a messagebuffer with the list in wiki markup
+                        StringBuffer buf = new StringBuffer( header );
+                        MessageFormat fmt = new MessageFormat( line );
+                        Object[] args = new Object[] { pagename, STR_EMPTY, STR_EMPTY };
+                        Iterator<Map.Entry<String,Counter>> iter = sorted.entrySet().iterator();
+
+                        while ( iter != null && 0 < entries && iter.hasNext() )
+                        {
+
+                            Entry<String, Counter> entry = iter.next();
+                            String name = entry.getKey();
+
+                            // check minimum count
+                            final int value = entry.getValue().getValue();
+                            boolean use = min <= value && value <= max;
+
+                            // did we specify a refer-to page?
+                            if( use && referrers != null )
+                            {
+
+                                use = referrers.contains( name );
+                            }
+
+                            // did we specify what pages to include?
+                            if( use && include != null )
+                            {
+                                use = false;
+
+                                for( int n = 0; !use && n < include.length; n++ )
+                                {
+                                    Matcher matcher = include[n].matcher( name );
+                                    use = matcher.matches( );
+                                }
+                            }
+
+                            // did we specify what pages to exclude?
+                            if( use && null != exclude )
+                            {
+                                for( int n = 0; use && n < exclude.length; n++ )
+                                {
+                                    Matcher matcher = exclude[n].matcher( name );
+                                    use &= !matcher.matches(  );
+                                }
+                            }
+
+                            if( use )
+                            {
+                                args[1] = engine.beautifyTitle( name );
+                                args[2] = entry.getValue();
+
+                                fmt.format( args, buf, null );
+
+                                entries--;
+                            }
+                        }
+                        buf.append( footer );
+
+                        // let the engine render the list
+                        result = engine.textToHTML( context, buf.toString() );
+                    }
+                }
+            }
+            return result;
+        }
+
+        /**
+         * Compile regexp parameter.
+         * 
+         * @param name The name of the parameter.
+         * @param value The parameter value.
+         * @return Pattern[] The compiled patterns, or <code>null</code>.
+         * @throws PluginException On malformed patterns.
+         */
+        private Pattern[] compileGlobs( String name, String value ) throws PluginException
+        {
+
+            Pattern[] result = null;
+
+            if( value != null && 0 < value.length() && !STR_GLOBSTAR.equals( value ) )
+            {
+                try
+                {
+                    String[] ptrns = StringUtils.split( value, STR_COMMA );
+
+                    result = new Pattern[ptrns.length];
+
+                    for( int n = 0; n < ptrns.length; n++ )
+                    {
+                        result[n] = Pattern.compile( RegExpUtil.globToPerl5( ptrns[n].toCharArray() ,RegExpUtil.DEFAULT_MASK ));
+                    }
+                }
+                catch( PatternSyntaxException e )
+                {
+                    throw new PluginException( "Parameter " + name + " has a pattern syntax error: " + e.getMessage() );
+                }
+            }
+
+            return result;
+        }
+
+        /**
+         * Adjust ofsset skipping whitespace.
+         * 
+         * @param offset The offset in value to adjust.
+         * @param value String in which offset points.
+         * @return int Adjusted offset into value.
+         */
+        private int skipWhitespace( int offset, String value )
+        {
+            while ( Character.isWhitespace( value.charAt( offset ) ) )
+            {
+                offset++;
+            }
+            return offset;
+        }
+
+        /**
+         * Retrieve a page count.
+         * 
+         * @return int The page count for the given key.
+         * @param key the key for the Counter
+         */
+        protected int getCount( Object key )
+        {
+            return m_counters.get( key ).getValue();
+        }
+
+        /**
+         * Load the page view counters from file.
+         */
+        private void loadCounters()
+        {
+            if( m_counters != null && m_storage != null )
+            {
+
+                    log.info( "loadCounters" );
+                synchronized( this )
+                {
+
+                    InputStream fis = null;
+
+                    try
+                    {
+                        fis = new FileInputStream( new File( m_workDir, COUNTER_PAGE ) );
+
+                        m_storage.load( fis );
+
+                    }
+                    catch( IOException ioe )
+                    {
+                        log.error( "loadCounters: Can't load page counter store: " + ioe.getMessage() + " , will create a new one" );
+
+                    }
+                    finally
+                    {
+                        try
+                        {
+                            if( fis != null )
+                            {
+                                fis.close();
+                            }
+                        }
+                        catch( Exception ignore )
+                        {
+                            /** ignore */
+                        }
+                    }
+
+                    // Copy the collection into a sorted map
+                    Iterator<Entry<Object,Object>> iter = m_storage.entrySet().iterator();
+
+                    while ( iter != null && iter.hasNext() )
+                    {
+                        Entry<Object,Object> entry = iter.next();
+
+                        m_counters.put( (String) entry.getKey(), new Counter( (String) entry.getValue() ) );
+                    }
+
+                
+                        log.info( "loadCounters: counters.size=" + m_counters.size() );
+                }
+            }
+        }
+
+        /**
+         * Save the page view counters to file.
+         * 
+         */
+        protected void storeCounters()
+        {
+            if( m_counters != null && m_storage != null && m_dirty )
+            {
+
+       
+                    log.info( "storeCounters: counters.size=" + m_counters.size() );
+                synchronized( this )
+                {
+
+                    OutputStream fos = null;
+
+                    // Write out the collection of counters
+                    try
+                    {
+                        fos = new FileOutputStream( new File( m_workDir, COUNTER_PAGE ) );
+
+                        m_storage.store( fos, "\n# The number of times each page has been viewed.\n# Do not modify.\n" );
+                        fos.flush();
+
+                        m_dirty = false;
+
+                    }
+                    catch( IOException ioe )
+                    {
+                        log.error( "storeCounters: Can't store counters: " + ioe.getMessage() );
+
+                    }
+                    finally
+                    {
+                        try
+                        {
+                            if( fos != null )
+                            {
+                                fos.close();
+                            }
+                        }
+                        catch( Exception ignore )
+                        {
+                            /** ignore */
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Is the given thread still current?
+         * 
+         * @return boolean <code>true</code> iff the thread is still the current
+         *         background thread.
+         * @param thrd
+         */
+        private synchronized boolean isRunning( Thread thrd )
+        {
+            return m_initialized && thrd == m_pageCountSaveThread;
+        }
+
+    }
+
+    /**
+     * Counter for page hits collection.
+     */
+    private static final class Counter
+    {
+
+        /** The count value. */
+        private int m_count;
+
+        /**
+         * Create a new counter.
+         */
+        public Counter()
+        {
+        }
+
+        /**
+         * Create and initialise a new counter.
+         * 
+         * @param value Count value.
+         */
+        public Counter( String value )
+        {
+
+            setValue( value );
+        }
+
+        /**
+         * Increment counter.
+         */
+        public void increment()
+        {
+            m_count++;
+        }
+
+        /**
+         * Get the count value.
+         * 
+         * @return int
+         */
+        public int getValue()
+        {
+
+            return m_count;
+        }
+
+        /**
+         * Set the count value.
+         * 
+         * @param value String representation of the count.
+         */
+        public void setValue( String value )
+        {
+            try
+            {
+                m_count = Integer.parseInt( value );
+
+            }
+            catch( Exception ignore )
+            {
+                m_count = 0;
+            }
+        }
+
+        /**
+         * @return String String representation of the count.
+         */
+        public String toString()
+        {
+            return String.valueOf( m_count );
+        }
+    }
+
+    /**
+     * Background thread storing the page counters.
+     */
+    static final class CounterSaveThread extends WikiBackgroundThread
+    {
+
+        /** The page view manager. */
+        private final PageViewManager m_manager;
+
+        /**
+         * Create a wiki background thread to store the page counters.
+         * 
+         * @param engine The wiki engine.
+         * @param interval Delay in seconds between saves.
+         * @param pageViewManager
+         */
+        public CounterSaveThread( WikiEngine engine, int interval, PageViewManager pageViewManager )
+        {
+
+            super( engine, interval );
+
+            if( pageViewManager == null )
+            {
+                throw new IllegalArgumentException( "Manager cannot be null" );
+            }
+
+            m_manager = pageViewManager;
+        }
+
+        /**
+         * Save the page counters to file.
+         */
+        public void backgroundTask()
+        {
+
+            if( m_manager.isRunning( this ) )
+            {
+                m_manager.storeCounters();
+            }
+        }
+    }
+}

Modified: incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/AllTests.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/AllTests.java?rev=809361&r1=809360&r2=809361&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/AllTests.java (original)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/AllTests.java Sun Aug 30 15:48:06 2009
@@ -51,6 +51,7 @@
         suite.addTest( RecentChangesPluginTest.suite() );
         suite.addTest( IndexPluginTest.suite() );
         suite.addTest( InterWikiLinksPluginTest.suite() );
+        suite.addTest( PageViewPluginTest.suite() );
 
         return suite;
     }

Added: incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/PageViewPluginTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/PageViewPluginTest.java?rev=809361&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/PageViewPluginTest.java (added)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/plugin/PageViewPluginTest.java Sun Aug 30 15:48:06 2009
@@ -0,0 +1,204 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.  
+ */
+package org.apache.wiki.plugin;
+
+import java.util.Properties;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+
+import org.apache.wiki.TestEngine;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.api.WikiPage;
+import org.apache.wiki.providers.ProviderException;
+
+public class PageViewPluginTest extends TestCase
+
+{
+    Properties props = new Properties();
+
+    TestEngine testEngine;
+
+    WikiContext context;
+
+    PluginManager manager;
+
+    public void setUp() throws Exception
+    {
+        props.load( TestEngine.findTestProperties() );
+
+        testEngine = new TestEngine( props );
+
+        // create pages that should be counted
+        testEngine.saveText( "TestPage01", "this is test page 01 [{PageViewPlugin}]" );
+        testEngine.saveText( "TestPage02", "this is test page 02 [{PageViewPlugin}]" );
+
+        manager = new PluginManager( testEngine, props );
+    }
+
+    public void tearDown()
+    {
+        try
+        {
+            testEngine.deletePage( "TestPage01" );
+            testEngine.deletePage( "TestPage02" );
+            testEngine.deletePage( "PageViews" );
+        }
+        catch( ProviderException e )
+        {
+            e.printStackTrace();
+        }
+        TestEngine.emptyWorkDir();
+    }
+
+    public void testShowCountsBasic() throws Exception
+    {
+        WikiPage page1 = testEngine.getPage( "TestPage01" );
+        WikiContext context1 = testEngine.getWikiContextFactory().newViewContext( page1 );
+        WikiPage page2 = testEngine.getPage( "TestPage02" );
+        WikiContext context2 = testEngine.getWikiContextFactory().newViewContext( page2 );
+
+        // generate counts:
+        testEngine.getHTML( context1, page1 );
+        testEngine.getHTML( context2, page2 );
+        testEngine.getHTML( context2, page2 );
+
+        // mind the double \n in the following string:
+        String pageViewPageContent = "[{PageViewPlugin show='list''\n\n* {1} ({2} views)\n}]";
+        testEngine.saveText( "PageViews", pageViewPageContent );
+
+        WikiPage pageviews = testEngine.getPage( "PageViews" );
+        WikiContext contextPV = testEngine.getWikiContextFactory().newViewContext( pageviews );
+
+        String result = testEngine.getHTML( contextPV, pageviews );
+        // System.out.println( result );
+
+        assertTrue( result.contains( "TestPage01</a> (2 views)" ) );
+
+        assertTrue( result.contains( "TestPage02</a> (3 views)" ) );
+    }
+
+    public void testShowCountsExclude() throws Exception
+    {
+        testEngine.saveText( "TestPageExcluded", "this is test page that should be excluded [{PageViewPlugin}]" );
+
+        WikiPage page1 = testEngine.getPage( "TestPage01" );
+        WikiContext context1 = testEngine.getWikiContextFactory().newViewContext( page1 );
+        WikiPage page2 = testEngine.getPage( "TestPage02" );
+        WikiContext context2 = testEngine.getWikiContextFactory().newViewContext( page2 );
+
+        // generate counts:
+        testEngine.getHTML( context1, page1 );
+        testEngine.getHTML( context2, page2 );
+
+        // mind the double \n in the following string:
+        String pageViewPageContent = "[{PageViewPlugin show='list' exclude='TestPageExcl*' '\n\n* {1} ({2} views)\n}]";
+        testEngine.saveText( "PageViews", pageViewPageContent );
+
+        WikiPage pageviews = testEngine.getPage( "PageViews" );
+        WikiContext contextPV = testEngine.getWikiContextFactory().newViewContext( pageviews );
+
+        String result = testEngine.getHTML( contextPV, pageviews );
+        // System.out.println( result );
+
+        assertTrue( result.contains( "TestPage01" ) );
+
+        // this page should not have been shown:
+        assertFalse( result.contains( "TestPageExcluded" ) );
+
+        testEngine.deletePage( "TestPageExcluded" );
+    }
+
+    public void testShowCountsSorted() throws Exception
+    {
+        WikiPage page1 = testEngine.getPage( "TestPage01" );
+        WikiContext context1 = testEngine.getWikiContextFactory().newViewContext( page1 );
+        WikiPage page2 = testEngine.getPage( "TestPage02" );
+        WikiContext context2 = testEngine.getWikiContextFactory().newViewContext( page2 );
+
+        // generate counts:
+        testEngine.getHTML( context1, page1 );
+        testEngine.getHTML( context2, page2 );
+        testEngine.getHTML( context2, page2 );
+
+        // mind the double \n in the following string:
+        String pageViewPageContent = "[{PageViewPlugin show='list' sort=count '\n\n* {1} ({2} views)\n}]";
+        testEngine.saveText( "PageViews", pageViewPageContent );
+
+        WikiPage pageviews = testEngine.getPage( "PageViews" );
+        WikiContext contextPV = testEngine.getWikiContextFactory().newViewContext( pageviews );
+
+        String result = testEngine.getHTML( contextPV, pageviews );
+        // System.out.println( result );
+
+        int start1 = result.indexOf( "TestPage01" );
+        int start2 = result.indexOf( "TestPage02" );
+
+        // page2 should be showed before page1
+        assertTrue( start2 < start1 );
+    }
+
+    public void testShowCountEntries() throws Exception
+    {
+        // create pages that should be counted
+        testEngine.saveText( "TestPage03", "this is test page 03 [{PageViewPlugin}]" );
+        testEngine.saveText( "TestPage04", "this is test page 04 [{PageViewPlugin}]" );
+
+        WikiPage page1 = testEngine.getPage( "TestPage01" );
+        WikiContext context1 = testEngine.getWikiContextFactory().newViewContext( page1 );
+        WikiPage page2 = testEngine.getPage( "TestPage02" );
+        WikiContext context2 = testEngine.getWikiContextFactory().newViewContext( page2 );
+        WikiPage page3 = testEngine.getPage( "TestPage03" );
+        WikiContext context3 = testEngine.getWikiContextFactory().newViewContext( page3 );
+        WikiPage page4 = testEngine.getPage( "TestPage04" );
+        WikiContext context4 = testEngine.getWikiContextFactory().newViewContext( page4 );
+
+        // generate counts:
+        testEngine.getHTML( context1, page1 );
+        testEngine.getHTML( context2, page2 );
+        testEngine.getHTML( context2, page2 );
+        testEngine.getHTML( context3, page3 );
+        testEngine.getHTML( context4, page4 );
+
+        // mind the double \n in the following string:
+        String pageViewPageContent = "[{PageViewPlugin show='list' entries=3'\n\n* {1} ({2} views)\n}]";
+        testEngine.saveText( "PageViews", pageViewPageContent );
+
+        WikiPage pageviews = testEngine.getPage( "PageViews" );
+        WikiContext contextPV = testEngine.getWikiContextFactory().newViewContext( pageviews );
+
+        String result = testEngine.getHTML( contextPV, pageviews );
+        // System.out.println( result );
+
+        assertTrue( result.contains( "TestPage03" ) );
+
+        assertFalse( result.contains( "TestPage04" ) );
+
+        testEngine.deletePage( "TestPage03" );
+        testEngine.deletePage( "TestPage04" );
+    }
+
+    public static Test suite()
+    {
+        return new TestSuite( PageViewPluginTest.class );
+    }
+}