You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by ju...@apache.org on 2020/01/28 20:34:27 UTC

[jspwiki] 26/32: JSPWIKI-120: rename CommandResolver as DefaultCommandResolver and extract CommandResolver interface from it

This is an automated email from the ASF dual-hosted git repository.

juanpablo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jspwiki.git

commit 6c42eb192724ef8c6ca691bc3ec392a8550f3ebb
Author: juanpablo <ju...@apache.org>
AuthorDate: Thu Jan 23 22:43:45 2020 +0100

    JSPWIKI-120: rename CommandResolver as DefaultCommandResolver and extract CommandResolver interface from it
    
    Also, do not instantiate it directly on WikiEngine
---
 .../src/main/java/org/apache/wiki/WikiEngine.java  |   8 +-
 .../java/org/apache/wiki/ui/CommandResolver.java   | 294 ++-------------------
 ...ndResolver.java => DefaultCommandResolver.java} | 109 ++------
 .../src/main/resources/ini/classmappings.xml       |   4 +
 4 files changed, 40 insertions(+), 375 deletions(-)

diff --git a/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java b/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java
index 873d583..29079b6 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java
@@ -405,9 +405,6 @@ public class WikiEngine  {
 
         log.debug( "Configuring WikiEngine..." );
 
-        //  Initializes the CommandResolver
-        m_commandResolver = new CommandResolver( this, props );
-
         //  Create and find the default working directory.
         m_workDir = TextUtil.getStringProperty( props, PROP_WORKDIR, null );
 
@@ -453,6 +450,8 @@ public class WikiEngine  {
         //
         // FIXME: This part of the code is getting unwieldy.  We must think of a better way to do the startup-sequence.
         try {
+            //  Initializes the CommandResolver
+            m_commandResolver = ClassUtil.getMappedObject( CommandResolver.class.getName(), this, props );
             final Class< ? > urlclass = ClassUtil.findClass( "org.apache.wiki.url",
                                                              TextUtil.getStringProperty( props, PROP_URLCONSTRUCTOR, "DefaultURLConstructor" ) );
             m_urlConstructor = ( URLConstructor ) urlclass.getDeclaredConstructor().newInstance();
@@ -520,8 +519,7 @@ public class WikiEngine  {
         } catch( final Exception e ) {
             // Final catch-all for everything
             log.fatal( "JSPWiki could not start, due to an unknown exception when starting.",e );
-            throw new WikiException( "Failed to start. Caused by: " + e.getMessage() +
-                                     "; please check log files for better information.", e );
+            throw new WikiException( "Failed to start. Caused by: " + e.getMessage() + "; please check log files for better information.", e );
         }
 
         //
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/ui/CommandResolver.java b/jspwiki-main/src/main/java/org/apache/wiki/ui/CommandResolver.java
index 0e68e70..350523f 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/ui/CommandResolver.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/ui/CommandResolver.java
@@ -18,21 +18,12 @@
  */
 package org.apache.wiki.ui;
 
-import org.apache.log4j.Logger;
-import org.apache.wiki.InternalWikiException;
-import org.apache.wiki.WikiEngine;
 import org.apache.wiki.WikiPage;
-import org.apache.wiki.WikiProvider;
 import org.apache.wiki.api.exceptions.ProviderException;
-import org.apache.wiki.auth.GroupPrincipal;
-import org.apache.wiki.parser.MarkupParser;
-import org.apache.wiki.util.TextUtil;
 
 import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
+import java.util.Arrays;
+
 
 /**
  * <p>Resolves special pages, JSPs and Commands on behalf of a WikiEngine. CommandResolver will automatically resolve page names
@@ -53,70 +44,10 @@ import java.util.Properties;
  *
  * @since 2.4.22
  */
-public final class CommandResolver {
-    /** Prefix in jspwiki.properties signifying special page keys. */
-    private static final String PROP_SPECIALPAGE = "jspwiki.specialPage.";
-
-    /** Private map with request contexts as keys, Commands as values */
-    private static final Map< String, Command > CONTEXTS;
-
-    /** Private map with JSPs as keys, Commands as values */
-    private static final Map< String, Command > JSPS;
-
-    /* Store the JSP-to-Command and context-to-Command mappings */
-    static {
-        CONTEXTS = new HashMap<>();
-        JSPS = new HashMap<>();
-        final Command[] commands = AbstractCommand.allCommands();
-        for( final Command command : commands ) {
-            JSPS.put( command.getJSP(), command );
-            CONTEXTS.put( command.getRequestContext(), command );
-        }
-    }
-
-    private static final Logger LOG = Logger.getLogger( CommandResolver.class );
-
-    private final WikiEngine m_engine;
-
-    /** If true, we'll also consider english plurals (+s) a match. */
-    private final boolean m_matchEnglishPlurals;
-
-    /** Stores special page names as keys, and Commands as values. */
-    private final Map<String, Command> m_specialPages;
-
-    /**
-     * Constructs a CommandResolver for a given WikiEngine. This constructor will extract the special page references for this wiki and
-     * store them in a cache used for resolution.
-     *
-     * @param engine the wiki engine
-     * @param properties the properties used to initialize the wiki
-     */
-    public CommandResolver( final WikiEngine engine, final Properties properties ) {
-        m_engine = engine;
-        m_specialPages = new HashMap<>();
-
-        // Skim through the properties and look for anything with the "special page" prefix. Create maps that allow us look up
-        // the correct Command based on special page name. If a matching command isn't found, create a RedirectCommand.
-        for( final String key : properties.stringPropertyNames() ) {
-            if ( key.startsWith( PROP_SPECIALPAGE ) ) {
-                String specialPage = key.substring( PROP_SPECIALPAGE.length() );
-                String jsp = properties.getProperty( key );
-                if ( jsp != null ) {
-                    specialPage = specialPage.trim();
-                    jsp = jsp.trim();
-                    Command command = JSPS.get( jsp );
-                    if ( command == null ) {
-                        final Command redirect = RedirectCommand.REDIRECT;
-                        command = redirect.targetedCommand( jsp );
-                    }
-                    m_specialPages.put( specialPage, command );
-                }
-            }
-        }
+public interface CommandResolver {
 
-        // Do we match plurals?
-        m_matchEnglishPlurals = TextUtil.getBooleanProperty( properties, WikiEngine.PROP_MATCHPLURALS, true );
-    }
+    /** Prefix in jspwiki.properties signifying special page keys. */
+    String PROP_SPECIALPAGE = "jspwiki.specialPage.";
 
     /**
      * Attempts to locate a wiki command for a supplied request context. The resolution technique is simple: we examine the list of
@@ -126,12 +57,11 @@ public final class CommandResolver {
      * @param context the request context
      * @return the resolved context
      */
-    public static Command findCommand( final String context ) {
-        final Command command = CONTEXTS.get( context );
-        if ( command == null ) {
-            throw new IllegalArgumentException( "Unsupported wiki context: " + context + "." );
-        }
-        return command;
+    static Command findCommand( final String context ) {
+        return Arrays.stream( AbstractCommand.allCommands() )
+                     .filter( c -> c.getRequestContext().equals( context ) )
+                     .findFirst()
+                     .orElseThrow( () -> new IllegalArgumentException( "Unsupported wiki context: " + context + "." ) );
     }
 
     /**
@@ -152,68 +82,7 @@ public final class CommandResolver {
      * @param defaultContext the request context to use by default
      * @return the resolved wiki command
      */
-    public Command findCommand( final HttpServletRequest request, final String defaultContext ) {
-        // Corner case if request is null
-        if ( request == null ) {
-            return findCommand( defaultContext );
-        }
-
-        Command command = null;
-
-        // Determine the name of the page (which may be null)
-        String pageName = extractPageFromParameter( defaultContext, request );
-
-        // Can we find a special-page command matching the extracted page?
-        if ( pageName != null ) {
-            command = m_specialPages.get( pageName );
-        }
-
-        // If we haven't found a matching command yet, extract the JSP path and compare to our list of special pages
-        if ( command == null ) {
-            command = extractCommandFromPath( request );
-
-            // Otherwise: use the default context
-            if ( command == null ) {
-                command = CONTEXTS.get( defaultContext );
-                if ( command == null ) {
-                    throw new IllegalArgumentException( "Wiki context " + defaultContext + " is illegal." );
-                }
-            }
-        }
-
-        // For PageCommand.VIEW, default to front page if a page wasn't supplied
-        if( PageCommand.VIEW.equals( command ) && pageName == null ) {
-            pageName = m_engine.getFrontPage();
-        }
-
-        // These next blocks handle targeting requirements
-
-        // If we were passed a page parameter, try to resolve it
-        if ( command instanceof PageCommand && pageName != null ) {
-            // If there's a matching WikiPage, "wrap" the command
-            final WikiPage page = resolvePage( request, pageName );
-            return command.targetedCommand( page );
-        }
-
-        // If "create group" command, target this wiki
-        final String wiki = m_engine.getApplicationName();
-        if ( WikiCommand.CREATE_GROUP.equals( command ) ) {
-            return WikiCommand.CREATE_GROUP.targetedCommand( wiki );
-        }
-
-        // If group command, see if we were passed a group name
-        if( command instanceof GroupCommand ) {
-            String groupName = request.getParameter( "group" );
-            groupName = TextUtil.replaceEntities( groupName );
-            if ( groupName != null && groupName.length() > 0 ) {
-                final GroupPrincipal group = new GroupPrincipal( groupName );
-                return command.targetedCommand( group );
-            }
-        }
-
-        // No page provided; return an "ordinary" command
-        return command;
-    }
+    Command findCommand( HttpServletRequest request, String defaultContext );
 
     /**
      * <p>Returns the correct page name, or <code>null</code>, if no such page can be found. Aliases are considered.</p>
@@ -228,37 +97,7 @@ public final class CommandResolver {
      * @return The rewritten page name, or <code>null</code>, if the page does not exist.
      * @throws ProviderException if the underlyng page provider that locates pages throws an exception
      */
-    public String getFinalPageName( final String page ) throws ProviderException {
-        boolean isThere = simplePageExists( page );
-        String  finalName = page;
-
-        if ( !isThere && m_matchEnglishPlurals ) {
-            if ( page.endsWith( "s" ) ) {
-                finalName = page.substring( 0, page.length() - 1 );
-            } else {
-                finalName += "s";
-            }
-
-            isThere = simplePageExists( finalName );
-        }
-
-        if( !isThere ) {
-            finalName = MarkupParser.wikifyLink( page );
-            isThere = simplePageExists(finalName);
-
-            if( !isThere && m_matchEnglishPlurals ) {
-                if( finalName.endsWith( "s" ) ) {
-                    finalName = finalName.substring( 0, finalName.length() - 1 );
-                } else {
-                    finalName += "s";
-                }
-
-                isThere = simplePageExists( finalName );
-            }
-        }
-
-        return isThere ? finalName : null;
-    }
+    String getFinalPageName( String page ) throws ProviderException;
 
     /**
      * <p>If the page is a special page, this method returns a direct URL to that page; otherwise, it returns <code>null</code>.</p>
@@ -268,53 +107,7 @@ public final class CommandResolver {
      * @param page the page name ro search for
      * @return the URL of the special page, if the supplied page is one, or <code>null</code>
      */
-    public String getSpecialPageReference( final String page ) {
-        final Command command = m_specialPages.get( page );
-        if ( command != null ) {
-            return m_engine.getURLConstructor().makeURL( command.getRequestContext(), command.getURLPattern(), null );
-        }
-
-        return null;
-    }
-
-    /**
-     * Extracts a Command based on the JSP path of an HTTP request. If the JSP requested matches a Command's <code>getJSP()</code>
-     * value, that Command is returned.
-     *
-     * @param request the HTTP request
-     * @return the resolved Command, or <code>null</code> if not found
-     */
-    protected Command extractCommandFromPath( final HttpServletRequest request ) {
-        String jsp = request.getServletPath();
-
-        // Take everything to right of initial / and left of # or ?
-        final int hashMark = jsp.indexOf( '#' );
-        if ( hashMark != -1 ) {
-            jsp = jsp.substring( 0, hashMark );
-        }
-        final int questionMark = jsp.indexOf( '?' );
-        if ( questionMark != -1 ) {
-            jsp = jsp.substring( 0, questionMark );
-        }
-        if ( jsp.startsWith( "/" ) ) {
-            jsp = jsp.substring( 1 );
-        }
-
-        // Find special page reference?
-        for( final Map.Entry< String, Command > entry : m_specialPages.entrySet() ) {
-            final Command specialCommand = entry.getValue();
-            if( specialCommand.getJSP().equals( jsp ) ) {
-                return specialCommand;
-            }
-        }
-
-        // Still haven't found a matching command? Ok, see if we match against our standard list of JSPs
-        if ( jsp.length() > 0 && JSPS.containsKey( jsp ) ) {
-            return JSPS.get( jsp );
-        }
-
-        return null;
-    }
+    String getSpecialPageReference( final String page );
 
     /**
      * Determines the correct wiki page based on a supplied request context and HTTP request. This method attempts to determine the page
@@ -331,30 +124,7 @@ public final class CommandResolver {
      * @param request the HTTP request
      * @return the resolved page name
      */
-    protected String extractPageFromParameter( final String requestContext, final HttpServletRequest request ) {
-        // Extract the page name from the URL directly
-        try {
-            String page = m_engine.getURLConstructor().parsePage( requestContext, request, m_engine.getContentEncoding() );
-            if ( page != null ) {
-                try {
-                    // Look for singular/plural variants; if one not found, take the one the user supplied
-                    final String finalPage = getFinalPageName( page );
-                    if ( finalPage != null ) {
-                        page = finalPage;
-                    }
-                } catch( final ProviderException e ) {
-                    // FIXME: Should not ignore!
-                }
-                return page;
-            }
-        } catch( final IOException e ) {
-            LOG.error( "Unable to create context", e );
-            throw new InternalWikiException( "Big internal booboo, please check logs." , e );
-        }
-
-        // Didn't resolve; return null
-        return null;
-    }
+    String extractPageFromParameter( String requestContext, HttpServletRequest request );
 
     /**
      * Looks up and returns the correct, versioned WikiPage based on a supplied page name and optional <code>version</code> parameter
@@ -364,40 +134,6 @@ public final class CommandResolver {
      * @param page the name of the page to look up; this page <em>must</em> exist
      * @return the wiki page
      */
-    protected WikiPage resolvePage( final HttpServletRequest request, String page ) {
-        // See if the user included a version parameter
-        int version = WikiProvider.LATEST_VERSION;
-        final String rev = request.getParameter( "version" );
-        if ( rev != null ) {
-            try {
-                version = Integer.parseInt( rev );
-            } catch( final NumberFormatException e ) {
-                // This happens a lot with bots or other guys who are trying to test if we are vulnerable to e.g. XSS attacks.  We catch
-                // it here so that the admin does not get tons of mail.
-            }
-        }
-
-        WikiPage wikipage = m_engine.getPageManager().getPage( page, version );
-        if ( wikipage == null ) {
-            page = MarkupParser.cleanLink( page );
-            wikipage = new WikiPage( m_engine, page );
-        }
-        return wikipage;
-    }
-
-    /**
-     * Determines whether a "page" exists by examining the list of special pages and querying the page manager.
-     *
-     * @param page the page to seek
-     * @return <code>true</code> if the page exists, <code>false</code> otherwise
-     * @throws ProviderException if the underlyng page provider that locates pages
-     * throws an exception
-     */
-    protected boolean simplePageExists( final String page ) throws ProviderException {
-        if ( m_specialPages.containsKey( page ) ) {
-            return true;
-        }
-        return m_engine.getPageManager().pageExists( page );
-    }
+    WikiPage resolvePage( HttpServletRequest request, String page );
 
 }
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/ui/CommandResolver.java b/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultCommandResolver.java
similarity index 60%
copy from jspwiki-main/src/main/java/org/apache/wiki/ui/CommandResolver.java
copy to jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultCommandResolver.java
index 0e68e70..65348ed 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/ui/CommandResolver.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultCommandResolver.java
@@ -34,28 +34,13 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
+
 /**
- * <p>Resolves special pages, JSPs and Commands on behalf of a WikiEngine. CommandResolver will automatically resolve page names
- * with singular/plural variants. It can also detect the correct Command based on parameters supplied in an HTTP request, or due to the
- * JSP being accessed.</p>
- * <p>
- * <p>CommandResolver's static {@link #findCommand(String)} method is the simplest method; it looks up and returns the Command matching
- * a supplied wiki context. For example, looking up the request context <code>view</code> returns {@link PageCommand#VIEW}. Use this method
- * to obtain static Command instances that aren't targeted at a particular page or group.</p>
- * <p>For more complex lookups in which the caller supplies an HTTP request, {@link #findCommand(HttpServletRequest, String)} will
- * look up and return the correct Command. The String parameter <code>defaultContext</code> supplies the request context to use
- * if it cannot be detected. However, note that the default wiki context may be overridden if the request was for a "special page."</p>
- * <p>For example, suppose the WikiEngine's properties specify a special page called <code>UserPrefs</code> that redirects to
- * <code>UserPreferences.jsp</code>. The ordinary lookup method {@linkplain #findCommand(String)} using a supplied context <code>view</code>
- * would return {@link PageCommand#VIEW}. But the {@linkplain #findCommand(HttpServletRequest, String)} method, when passed the same context
- * (<code>view</code>) and an HTTP request containing the page parameter value <code>UserPrefs</code>, will instead return
- * {@link WikiCommand#PREFS}.</p>
+ * <p>Default implementation for {@link CommandResolver}</p>
  *
  * @since 2.4.22
  */
-public final class CommandResolver {
-    /** Prefix in jspwiki.properties signifying special page keys. */
-    private static final String PROP_SPECIALPAGE = "jspwiki.specialPage.";
+public final class DefaultCommandResolver implements CommandResolver {
 
     /** Private map with request contexts as keys, Commands as values */
     private static final Map< String, Command > CONTEXTS;
@@ -74,7 +59,7 @@ public final class CommandResolver {
         }
     }
 
-    private static final Logger LOG = Logger.getLogger( CommandResolver.class );
+    private static final Logger LOG = Logger.getLogger( DefaultCommandResolver.class );
 
     private final WikiEngine m_engine;
 
@@ -91,7 +76,7 @@ public final class CommandResolver {
      * @param engine the wiki engine
      * @param properties the properties used to initialize the wiki
      */
-    public CommandResolver( final WikiEngine engine, final Properties properties ) {
+    public DefaultCommandResolver( final WikiEngine engine, final Properties properties ) {
         m_engine = engine;
         m_specialPages = new HashMap<>();
 
@@ -119,43 +104,13 @@ public final class CommandResolver {
     }
 
     /**
-     * Attempts to locate a wiki command for a supplied request context. The resolution technique is simple: we examine the list of
-     * Commands returned by {@link AbstractCommand#allCommands()} and return the one whose <code>requestContext</code> matches the
-     * supplied context. If the supplied context does not resolve to a known Command, this method throws an {@link IllegalArgumentException}.
-     *
-     * @param context the request context
-     * @return the resolved context
-     */
-    public static Command findCommand( final String context ) {
-        final Command command = CONTEXTS.get( context );
-        if ( command == null ) {
-            throw new IllegalArgumentException( "Unsupported wiki context: " + context + "." );
-        }
-        return command;
-    }
-
-    /**
-     * <p>Attempts to locate a Command for a supplied wiki context and HTTP request, incorporating the correct WikiPage into the command
-     * if required. This method will first determine what page the user requested by delegating to {@link #extractPageFromParameter(String, HttpServletRequest)}.
-     * If this page equates to a special page, we return the Command corresponding to that page. Otherwise, this method simply returns the
-     * Command for the supplied request context.</p>
-     * <p>The reason this method attempts to resolve against special pages is because some of them resolve to contexts that may be different
-     * from the one supplied. For example, a VIEW request context for the special page "UserPreferences" should return a PREFS context instead.</p>
-     * <p>When the caller supplies a request context and HTTP request that specifies an actual wiki page (rather than a special page),
-     * this method will return a "targeted" Command that includes the resolved WikiPage as the target. (See {@link #resolvePage(HttpServletRequest, String)}
-     * for the resolution algorithm). Specifically, the Command will return a non-<code>null</code> value for
-     * its {@link AbstractCommand#getTarget()} method.</p>
-     * <p><em>Note: if this method determines that the Command is the VIEW PageCommand, then the Command returned will always be targeted to
-     * the front page.</em></p>
-     *
-     * @param request the HTTP request; if <code>null</code>, delegates to {@link #findCommand(String)}
-     * @param defaultContext the request context to use by default
-     * @return the resolved wiki command
+     * {@inheritDoc}
      */
+    @Override
     public Command findCommand( final HttpServletRequest request, final String defaultContext ) {
         // Corner case if request is null
         if ( request == null ) {
-            return findCommand( defaultContext );
+            return CommandResolver.findCommand( defaultContext );
         }
 
         Command command = null;
@@ -216,18 +171,9 @@ public final class CommandResolver {
     }
 
     /**
-     * <p>Returns the correct page name, or <code>null</code>, if no such page can be found. Aliases are considered.</p>
-     * <p>In some cases, page names can refer to other pages. For example, when you have matchEnglishPlurals set, then a page name
-     * "Foobars" will be transformed into "Foobar", should a page "Foobars" not exist, but the page "Foobar" would. This method gives
-     * you the correct page name to refer to. </p>
-     * <p>This facility can also be used to rewrite any page name, for example, by using aliases. It can also be used to check the
-     * existence of any page.</p>
-     *
-     * @since 2.4.20
-     * @param page the page name.
-     * @return The rewritten page name, or <code>null</code>, if the page does not exist.
-     * @throws ProviderException if the underlyng page provider that locates pages throws an exception
+     * {@inheritDoc}
      */
+    @Override
     public String getFinalPageName( final String page ) throws ProviderException {
         boolean isThere = simplePageExists( page );
         String  finalName = page;
@@ -261,13 +207,9 @@ public final class CommandResolver {
     }
 
     /**
-     * <p>If the page is a special page, this method returns a direct URL to that page; otherwise, it returns <code>null</code>.</p>
-     * <p>Special pages are non-existant references to other pages. For example, you could define a special page reference "RecentChanges"
-     * which would always be redirected to "RecentChanges.jsp" instead of trying to find a Wiki page called "RecentChanges".</p>
-     *
-     * @param page the page name ro search for
-     * @return the URL of the special page, if the supplied page is one, or <code>null</code>
+     * {@inheritDoc}
      */
+    @Override
     public String getSpecialPageReference( final String page ) {
         final Command command = m_specialPages.get( page );
         if ( command != null ) {
@@ -317,21 +259,10 @@ public final class CommandResolver {
     }
 
     /**
-     * Determines the correct wiki page based on a supplied request context and HTTP request. This method attempts to determine the page
-     * requested by a user, taking into acccount special pages. The resolution algorithm will:
-     * <ul>
-     * <li>Extract the page name from the URL according to the rules for the current {@link org.apache.wiki.url.URLConstructor}. If a
-     * page name was passed in the request, return the correct name after taking into account potential plural matches.</li>
-     * <li>If the extracted page name is <code>null</code>, attempt to see if a "special page" was intended by examining the servlet path.
-     * For example, the request path "/UserPreferences.jsp" will resolve to "UserPreferences."</li>
-     * <li>If neither of these methods work, this method returns <code>null</code></li>
-     * </ul>
-     *
-     * @param requestContext the request context
-     * @param request the HTTP request
-     * @return the resolved page name
+     * {@inheritDoc}
      */
-    protected String extractPageFromParameter( final String requestContext, final HttpServletRequest request ) {
+    @Override
+    public String extractPageFromParameter( final String requestContext, final HttpServletRequest request ) {
         // Extract the page name from the URL directly
         try {
             String page = m_engine.getURLConstructor().parsePage( requestContext, request, m_engine.getContentEncoding() );
@@ -357,14 +288,10 @@ public final class CommandResolver {
     }
 
     /**
-     * Looks up and returns the correct, versioned WikiPage based on a supplied page name and optional <code>version</code> parameter
-     * passed in an HTTP request. If the <code>version</code> parameter does not exist in the request, the latest version is returned.
-     *
-     * @param request the HTTP request
-     * @param page the name of the page to look up; this page <em>must</em> exist
-     * @return the wiki page
+     * {@inheritDoc}
      */
-    protected WikiPage resolvePage( final HttpServletRequest request, String page ) {
+    @Override
+    public WikiPage resolvePage( final HttpServletRequest request, String page ) {
         // See if the user included a version parameter
         int version = WikiProvider.LATEST_VERSION;
         final String rev = request.getParameter( "version" );
diff --git a/jspwiki-main/src/main/resources/ini/classmappings.xml b/jspwiki-main/src/main/resources/ini/classmappings.xml
index 3f5bd1f..160761c 100644
--- a/jspwiki-main/src/main/resources/ini/classmappings.xml
+++ b/jspwiki-main/src/main/resources/ini/classmappings.xml
@@ -116,6 +116,10 @@
     <mappedClass>org.apache.wiki.tasks.DefaultTasksManager</mappedClass>
   </mapping>
   <mapping>
+    <requestedClass>org.apache.wiki.ui.CommandResolver</requestedClass>
+    <mappedClass>org.apache.wiki.ui.DefaultCommandResolver</mappedClass>
+  </mapping>
+  <mapping>
     <requestedClass>org.apache.wiki.ui.EditorManager</requestedClass>
     <mappedClass>org.apache.wiki.ui.EditorManager</mappedClass>
   </mapping>