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 <[email protected]> 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>
