You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@turbine.apache.org by tv...@apache.org on 2021/01/05 14:52:24 UTC

svn commit: r1885149 - in /turbine/core/branches/URLMapperService: ./ conf/ conf/test/ src/java/org/apache/turbine/services/urlmapper/ src/java/org/apache/turbine/services/urlmapper/model/ src/test/org/apache/turbine/services/urlmapper/ src/test/org/ap...

Author: tv
Date: Tue Jan  5 14:52:24 2021
New Revision: 1885149

URL: http://svn.apache.org/viewvc?rev=1885149&view=rev
Log:
Experimental URL mapper implementation

Added:
    turbine/core/branches/URLMapperService/conf/test/TurbineURLMapperServiceTest.properties
    turbine/core/branches/URLMapperService/conf/turbine-url-mapping.xml
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/MappedTemplateLink.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperService.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperValve.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMapEntry.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMappingContainer.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterAdapter.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterList.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlPatternAdapter.java
    turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/package.html
    turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/
    turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/TurbineURLMapperServiceTest.java
    turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/model/
    turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/model/URLMappingContainerTest.java
Modified:
    turbine/core/branches/URLMapperService/pom.xml

Added: turbine/core/branches/URLMapperService/conf/test/TurbineURLMapperServiceTest.properties
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/conf/test/TurbineURLMapperServiceTest.properties?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/conf/test/TurbineURLMapperServiceTest.properties (added)
+++ turbine/core/branches/URLMapperService/conf/test/TurbineURLMapperServiceTest.properties Tue Jan  5 14:52:24 2021
@@ -0,0 +1,131 @@
+# 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.
+
+# -------------------------------------------------------------------
+#
+#  L O G 4 J 2 - L O G G I N G
+#
+# -------------------------------------------------------------------
+# log4j2 may loads automatically if found on classpath, cf. https://logging.apache.org/log4j/2.x
+log4j2.file = log4j2.xml
+
+# resource relative to context
+pipeline.default.descriptor = /conf/turbine-classic-pipeline.xml
+
+
+# If module.cache=true, then how large should we make the hashtables
+# by default.
+
+action.cache.size=20
+layout.cache.size=10
+navigation.cache.size=10
+page.cache.size=5
+screen.cache.size=50
+scheduledjob.cache.size=10
+
+# -------------------------------------------------------------------
+#
+#  M O D U L E  P A C K A G E S
+#
+# -------------------------------------------------------------------
+# This is the "classpath" for Turbine.  In order to locate your own
+# modules, you should add them to this path.  For example, if you have
+# com.company.actions, com.company.screens, com.company.navigations,
+# then this setting would be "com.company,org.apache.turbine.modules".
+# This path is searched in order.  For example, Turbine comes with a
+# screen module named "Login".  If you wanted to have your own screen
+# module named "Login", then you would specify the path to your
+# modules before the others.
+#
+# Note: org.apache.turbine.modules will always be added to the search
+# path.  If it is not explicitly added here, it will be added to the
+# end.
+#
+# Default: org.apache.turbine.modules
+# -------------------------------------------------------------------
+
+module.packages=@MODULE_PACKAGES@
+
+# Choose between the two available implementations of an Avalon container - ECM or YAAFI
+
+# services.AvalonComponentService.classname=org.apache.turbine.services.avaloncomponent.TurbineAvalonComponentService
+services.AvalonComponentService.classname=org.apache.turbine.services.avaloncomponent.TurbineYaafiComponentService
+
+services.RunDataService.classname=org.apache.turbine.services.rundata.TurbineRunDataService
+services.AssemblerBrokerService.classname=org.apache.turbine.services.assemblerbroker.TurbineAssemblerBrokerService
+services.TemplateService.classname=org.apache.turbine.services.template.TurbineTemplateService
+# required by url mapper service
+services.ServletService.classname=org.apache.turbine.services.servlet.TurbineServletService
+services.URLMapperService.classname=org.apache.turbine.services.urlmapper.TurbineURLMapperService
+
+# -------------------------------------------------------------------
+#
+#  R U N D A T A  S E R V I C E
+#
+# -------------------------------------------------------------------
+
+services.RunDataService.default.run.data=org.apache.turbine.services.rundata.DefaultTurbineRunData
+services.RunDataService.default.parameter.parser=org.apache.fulcrum.parser.DefaultParameterParser
+services.RunDataService.default.cookie.parser=org.apache.fulcrum.parser.DefaultCookieParser
+
+# -------------------------------------------------------------------
+#
+#  A S S E M B L E R  B R O K E R  S E R V I C E
+#
+# -------------------------------------------------------------------
+# A list of AssemblerFactory classes that will be registered
+# with TurbineAssemblerBrokerService
+# -------------------------------------------------------------------
+
+services.AssemblerBrokerService.screen=org.apache.turbine.services.assemblerbroker.util.java.JavaScreenFactory
+# services.AssemblerBrokerService.screen=org.apache.turbine.services.assemblerbroker.util.python.PythonScreenFactory
+services.AssemblerBrokerService.action=org.apache.turbine.services.assemblerbroker.util.java.JavaActionFactory
+services.AssemblerBrokerService.layout=org.apache.turbine.services.assemblerbroker.util.java.JavaLayoutFactory
+services.AssemblerBrokerService.page=org.apache.turbine.services.assemblerbroker.util.java.JavaPageFactory
+services.AssemblerBrokerService.navigation=org.apache.turbine.services.assemblerbroker.util.java.JavaNavigationFactory
+services.AssemblerBrokerService.scheduledjob=org.apache.turbine.services.assemblerbroker.util.java.JavaScheduledJobFactory
+
+# -------------------------------------------------------------------
+#
+#  T E M P L A T E  S E R V I C E
+#
+# -------------------------------------------------------------------
+
+# Roughly, the number of templates in each category.
+#
+# Defaults: layout=2, navigation=10, screen=50
+
+services.TemplateService.layout.cache.size=2
+services.TemplateService.navigation.cache.size=10
+services.TemplateService.screen.cache.size=50
+
+# -------------------------------------------------------------------
+#
+#  A V A L O N C O M P O N E N T  S E R V I C E
+#
+# -------------------------------------------------------------------
+
+services.AvalonComponentService.componentConfiguration = conf/test/fulcrumComponentConfiguration.xml
+services.AvalonComponentService.componentRoles = conf/test/fulcrumRoleConfiguration.xml
+services.AvalonComponentService.lookup = org.apache.fulcrum.cache.GlobalCacheService
+
+# -------------------------------------------------------------------
+#
+#  U R L  M A P P E R  S E R V I C E
+#
+# -------------------------------------------------------------------
+services.URLMapperService.configFile = /conf/turbine-url-mapping.xml

Added: turbine/core/branches/URLMapperService/conf/turbine-url-mapping.xml
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/conf/turbine-url-mapping.xml?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/conf/turbine-url-mapping.xml (added)
+++ turbine/core/branches/URLMapperService/conf/turbine-url-mapping.xml Tue Jan  5 14:52:24 2021
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<url-mapping name="default">
+    <maps>
+        <map>
+            <pattern>/(?&lt;contextPath&gt;\w+)/book/(?&lt;bookId&gt;\d+)</pattern>
+            <implicit-parameters>
+                <parameter key="template">Book.vm</parameter>
+                <parameter key="detail">0</parameter>
+            </implicit-parameters>
+        </map>
+        <map>
+            <pattern>/(?&lt;contextPath&gt;\w+)/book/(?&lt;bookId&gt;\d+)/(?&lt;detail&gt;\d)</pattern>
+            <implicit-parameters>
+                <parameter key="template">Book.vm</parameter>
+            </implicit-parameters>
+            <ignore-parameters>
+                <parameter key="view" />
+            </ignore-parameters>
+        </map>
+    </maps>
+</url-mapping>
\ No newline at end of file

Modified: turbine/core/branches/URLMapperService/pom.xml
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/pom.xml?rev=1885149&r1=1885148&r2=1885149&view=diff
==============================================================================
--- turbine/core/branches/URLMapperService/pom.xml (original)
+++ turbine/core/branches/URLMapperService/pom.xml Tue Jan  5 14:52:24 2021
@@ -22,7 +22,7 @@
   <parent>
     <groupId>org.apache.turbine</groupId>
     <artifactId>turbine-parent</artifactId>
-    <version>8-SNAPSHOT</version>
+    <version>7</version>
   </parent>
   <artifactId>turbine</artifactId>
   <name>Apache Turbine</name>

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/MappedTemplateLink.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/MappedTemplateLink.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/MappedTemplateLink.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/MappedTemplateLink.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,96 @@
+package org.apache.turbine.services.urlmapper;
+
+
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.services.pull.tools.TemplateLink;
+
+/**
+ * This is a pull to to be used in Templates to convert links in
+ * Templates into the correct references.
+ *
+ * The pull service might insert this tool into the Context.
+ * in templates.  Here's an example of its Velocity use:
+ *
+ * <p><code>
+ * $link.setPage("index.vm").addPathInfo("hello","world")
+ * This would return: http://foo.com/Turbine/template/index.vm/hello/world
+ * </code>
+ *
+ * <p>
+ *
+ * This is an application pull tool for the template system. You should <b>not</b>
+ * use it in a normal application!
+ *
+ * @author <a href="mbryson@mont.mindspring.com">Dave Bryson</a>
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
+ * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
+ * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
+ * @version $Id: TemplateLink.java 1854688 2019-03-03 10:36:42Z tv $
+ */
+
+public class MappedTemplateLink extends TemplateLink
+{
+    /**
+     * The URL Mapper service.
+     */
+    @TurbineService
+    private URLMapperService urlMapperService;
+
+    /**
+     * Builds the URL with all of the data URL-encoded as well as
+     * encoded using HttpServletResponse.encodeUrl(). The resulting
+     * URL is absolute; it starts with http/https...
+     *
+     * <pre>
+     * TemplateURI tui = new TemplateURI (data, "UserScreen");
+     * tui.addPathInfo("user","jon");
+     * tui.getAbsoluteLink();
+     * </pre>
+     *
+     * The above call to absoluteLink() would return the String:
+     * <p>
+     * http://www.server.com/servlets/Turbine/screen/UserScreen/user/jon
+     * <p>
+     * After rendering the URI, it clears the
+     * pathInfo and QueryString portions of the TemplateURI. So you can
+     * use the $link reference multiple times on a page and start over
+     * with a fresh object every time.
+     *
+     * @return A String with the built URL.
+     */
+    public String getAbsoluteLink()
+    {
+        urlMapperService.mapToURL(templateURI);
+        return super.getAbsoluteLink();
+    }
+
+
+    /**
+     * Builds the URL with all of the data URL-encoded as well as
+     * encoded using HttpServletResponse.encodeUrl(). The resulting
+     * URL is relative to the webserver root.
+     *
+     * <pre>
+     * TemplateURI tui = new TemplateURI (data, "UserScreen");
+     * tui.addPathInfo("user","jon");
+     * tui.getRelativeLink();
+     * </pre>
+     *
+     * The above call to absoluteLink() would return the String:
+     * <p>
+     * /servlets/Turbine/screen/UserScreen/user/jon
+     * <p>
+     * After rendering the URI, it clears the
+     * pathInfo and QueryString portions of the TemplateURI. So you can
+     * use the $link reference multiple times on a page and start over
+     * with a fresh object every time.
+     *
+     * @return A String with the built URL.
+     */
+    public String getRelativeLink()
+    {
+        urlMapperService.mapToURL(templateURI);
+        return super.getRelativeLink();
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,326 @@
+package org.apache.turbine.services.urlmapper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.fulcrum.parser.ParameterParser;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.turbine.services.InitializationException;
+import org.apache.turbine.services.TurbineBaseService;
+import org.apache.turbine.services.TurbineServices;
+import org.apache.turbine.services.servlet.ServletService;
+import org.apache.turbine.services.urlmapper.model.URLMapEntry;
+import org.apache.turbine.services.urlmapper.model.URLMappingContainer;
+import org.apache.turbine.util.uri.TurbineURI;
+import org.apache.turbine.util.uri.URIParam;
+
+/**
+ * The URL mapper service provides methods to map a set of parameters to a
+ * simplified URL and vice-versa. This service was inspired by the
+ * Liferay Friendly URL Mapper.
+ *
+ * A mapper valve and a link pull tool are provided for easy application.
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ *
+ * @see URLMapperService
+ * @see URLMapperTemplateLink
+ * @see URLMapperContentLink
+ * @see URLMapperValve
+ */
+public class TurbineURLMapperService
+        extends TurbineBaseService
+        implements URLMapperService
+{
+    /** Logging. */
+    private static final Logger log = LogManager.getLogger(TurbineURLMapperService.class);
+
+    /**
+     * The default configuration file.
+     */
+    private static final String DEFAULT_CONFIGURATION_FILE = "/WEB-INF/conf/turbine-url-mapping.xml";
+
+    /**
+     * The configuration key for the configuration file.
+     */
+    private static final String CONFIGURATION_FILE_KEY = "configFile";
+
+    /**
+     * The configuration file.
+     */
+    private String configFile;
+
+    /**
+     * The container with the URL mappings.
+     */
+    private URLMappingContainer container;
+
+    /**
+     * Non-public method to read the names of regex groups from a Pattern
+     */
+    private Method namedGroupsMethod;
+
+    /**
+     * Regex pattern for group names
+     */
+    private static final Pattern namedGroupsPattern = Pattern.compile("\\(\\?<([a-zA-Z][a-zA-Z0-9]*)>.+?\\)");
+
+    /**
+     * Symbolic group name for context path
+     */
+    private static final String CONTEXT_PATH_PARAMETER = "contextPath";
+
+    /**
+     * Symbolic group name for web application root
+     */
+    private static final String WEBAPP_ROOT_PARAMETER = "webAppRoot";
+
+    /**
+     * Symbolic group names that will not be added to parameters
+     */
+    private static final Set<String> DEFAULT_PARAMETERS = Stream.of(
+            CONTEXT_PATH_PARAMETER,
+            WEBAPP_ROOT_PARAMETER
+            ).collect(Collectors.toSet());
+
+    /**
+     * Map a set of parameters (contained in TurbineURI PathInfo and QueryData)
+     * to a TurbineURI
+     *
+     * @param uri the URI to be modified (with setScriptName())
+     */
+    @Override
+    public void mapToURL(TurbineURI uri)
+    {
+        // Create map from list, taking only the first appearance of a key
+        // PathInfo takes precedence
+        Map<String, Object> uriParameterMap = Stream.concat(
+                uri.getPathInfo().stream(),
+                uri.getQueryData().stream())
+                .collect(Collectors.toMap(
+                        URIParam::getKey,
+                        URIParam::getValue,
+                        (e1, e2) -> e1,
+                        LinkedHashMap::new));
+
+        Set<String> keys = new HashSet<>(uriParameterMap.keySet());
+
+        for (URLMapEntry urlMap : container.getMapEntries())
+        {
+            Set<String> entryKeys = new HashSet<>();
+
+            Map<String, Integer> groupNamesMap = urlMap.getGroupNamesMap();
+            if (groupNamesMap != null)
+            {
+                entryKeys.addAll(groupNamesMap.keySet());
+            }
+
+            Set<String> implicitKeysFound = urlMap.getImplicitParameters().entrySet().stream()
+                    .filter(entry -> Objects.equals(uriParameterMap.get(entry.getKey()), entry.getValue()))
+                    .map(Map.Entry::getKey)
+                    .collect(Collectors.toSet());
+
+            entryKeys.addAll(implicitKeysFound);
+            implicitKeysFound.forEach(key -> {
+                uri.removePathInfo(key);
+                uri.removeQueryData(key);
+            });
+
+            keys.removeAll(urlMap.getIgnoreParameters().keySet());
+
+            if (entryKeys.containsAll(keys))
+            {
+                Matcher matcher = namedGroupsPattern.matcher(urlMap.getUrlPattern().pattern());
+                StringBuffer sb = new StringBuffer();
+
+                while (matcher.find())
+                {
+                    String key = matcher.group(1);
+
+                    if (CONTEXT_PATH_PARAMETER.equals(key))
+                    {
+                        // remove
+                        matcher.appendReplacement(sb, "");
+                    }
+                    else if (WEBAPP_ROOT_PARAMETER.equals(key))
+                    {
+                        matcher.appendReplacement(sb, uri.getScriptName());
+                    }
+                    else
+                    {
+                        matcher.appendReplacement(sb,
+                                Matcher.quoteReplacement(
+                                        Objects.toString(uriParameterMap.get(key))));
+                        // Remove handled parameters (all of them!)
+                        uri.removePathInfo(key);
+                        uri.removeQueryData(key);
+                    }
+                }
+
+                matcher.appendTail(sb);
+                // Clean up
+                uri.setScriptName(sb.toString().replace("//", "/"));
+                break;
+            }
+        }
+    }
+
+    /**
+     * Map a simplified URL to a set of parameters
+     *
+     * @param url the URL
+     * @param pp a ParameterParser to use for parameter mangling
+     */
+    @Override
+    public void mapFromURL(String url, ParameterParser pp)
+    {
+        for (URLMapEntry urlMap : container.getMapEntries())
+        {
+            Matcher matcher = urlMap.getUrlPattern().matcher(url);
+            if (matcher.matches())
+            {
+                // extract parameters from URL
+                Map<String, Integer> groupNameMap = urlMap.getGroupNamesMap();
+
+                if (groupNameMap != null)
+                {
+                    groupNameMap.entrySet().stream()
+                        // ignore default parameters
+                        .filter(group -> !DEFAULT_PARAMETERS.contains(group.getKey()))
+                        .forEach(group ->
+                            pp.setString(group.getKey(), matcher.group(group.getValue().intValue())));
+                }
+
+                // add implicit parameters
+                urlMap.getImplicitParameters().entrySet().forEach(e ->
+                    pp.add(e.getKey(), e.getValue()));
+
+                // add override parameters
+                urlMap.getOverrideParameters().entrySet().forEach(e ->
+                    pp.setString(e.getKey(), e.getValue()));
+
+                // remove ignore parameters
+                urlMap.getIgnoreParameters().keySet().forEach(k ->
+                    pp.remove(k));
+
+                break;
+            }
+        }
+    }
+
+    /**
+     * Get the named groups from a Pattern
+     * This method uses reflection to call a non-public method of the
+     * Pattern class
+     *
+     * @param regex the pattern
+     * @return a Map of group names to group indices
+     *
+     * @throws InvocationTargetException  if the underlying method throws an
+     *     exception.
+     * @throws IllegalArgumentException if the method is an instance method
+     *     and the specified object argument is not an instance of the class
+     *     or interface declaring the underlying method (or of a subclass or
+     *     implementor thereof); if the number of actual and formal parameters
+     *     differ; if an unwrapping conversion for primitive arguments fails;
+     *     or if, after possible unwrapping, a parameter value cannot be
+     *     converted to the corresponding formal parameter type by a method
+     *     invocation conversion.
+     * @throws IllegalAccessException if this Method object is enforcing Java
+     *     language access control and the underlying method is inaccessible.
+     */
+    private Map<String, Integer> getNamedGroups(Pattern regex)
+            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
+    {
+        @SuppressWarnings("unchecked")
+        Map<String, Integer> groupNamesMap = (Map<String, Integer>) namedGroupsMethod.invoke(regex);
+        return groupNamesMap;
+    }
+
+    // ---- Service initialization ------------------------------------------
+
+    /**
+     * Initializes the service.
+     */
+    @Override
+    public void init() throws InitializationException
+    {
+        try
+        {
+            namedGroupsMethod = Pattern.class.getDeclaredMethod("namedGroups");
+            namedGroupsMethod.setAccessible(true);
+        }
+        catch (NoSuchMethodException | SecurityException e)
+        {
+            throw new InitializationException("Could not find method Pattern.getNamedGroups", e);
+        }
+
+        Configuration cfg = getConfiguration();
+
+        ServletService servletService = (ServletService)TurbineServices.getInstance().getService(ServletService.SERVICE_NAME);
+
+        configFile = cfg.getString(CONFIGURATION_FILE_KEY, DEFAULT_CONFIGURATION_FILE);
+
+        // context resource path has to begin with slash, cft.
+        // context.getResource
+        if (!configFile.startsWith("/"))
+        {
+            configFile = "/" + configFile;
+        }
+
+        try (InputStream reader = servletService.getResourceAsStream(configFile))
+        {
+            JAXBContext jaxb = JAXBContext.newInstance(URLMappingContainer.class);
+            Unmarshaller unmarshaller = jaxb.createUnmarshaller();
+            container = (URLMappingContainer) unmarshaller.unmarshal(reader);
+        }
+        catch (IOException | JAXBException e)
+        {
+            throw new InitializationException("Could not load configuration file " + configFile, e);
+        }
+
+        // Get groupNamesMap for every Pattern and store it in the entry
+        try
+        {
+            for (URLMapEntry urlMap : container.getMapEntries())
+            {
+                Map<String, Integer> groupNamesMap = getNamedGroups(urlMap.getUrlPattern());
+                urlMap.setGroupNamesMap(groupNamesMap);
+            }
+        }
+        catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
+        {
+            throw new InitializationException("Could not invoke method Pattern.getNamedGroups", e);
+        }
+
+        log.info("Loaded {} url-mappings from {}", Integer.valueOf(container.getMapEntries().size()), configFile);
+
+        setInit(true);
+    }
+
+    /**
+     * Returns to uninitialized state.
+     */
+    @Override
+    public void shutdown()
+    {
+        setInit(false);
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperService.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperService.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperService.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperService.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,57 @@
+package org.apache.turbine.services.urlmapper;
+
+import org.apache.fulcrum.parser.ParameterParser;
+import org.apache.turbine.services.Service;
+import org.apache.turbine.util.uri.TurbineURI;
+
+/*
+ * 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.
+ */
+
+/**
+ * The URL mapper service provides methods to map a set of parameters to a
+ * simplified URL and vice-versa. This service was inspired by the
+ * Liferay Friendly URL Mapper.
+ *
+ * A mapper valve and a link pull tool are provided for easy application.
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ */
+public interface URLMapperService extends Service
+{
+    /**
+     * The service identifier.
+     */
+    String SERVICE_NAME = "URLMapperService";
+
+    /**
+     * Map a set of parameters (contained in TurbineURI PathInfo and QueryData)
+     * to a TurbineURI
+     *
+     * @param uri the URI to be modified (with setScriptName())
+     */
+    void mapToURL(TurbineURI uri);
+
+    /**
+     * Map a simplified URL to a set of parameters
+     *
+     * @param url the URL
+     * @param pp a ParameterParser to use for parameter mangling
+     */
+    void mapFromURL(String url, ParameterParser pp);
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperValve.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperValve.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperValve.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/URLMapperValve.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,61 @@
+package org.apache.turbine.services.urlmapper;
+
+
+/*
+ * 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.
+ */
+
+
+import java.io.IOException;
+
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.pipeline.Valve;
+import org.apache.turbine.pipeline.ValveContext;
+import org.apache.turbine.util.RunData;
+import org.apache.turbine.util.TurbineException;
+
+/**
+ * This valve is responsible for parsing parameters out of
+ * simplified URLs.
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ */
+public class URLMapperValve
+    implements Valve
+{
+    /** Injected service instance */
+    @TurbineService
+    private URLMapperService urlMapperService;
+
+    /**
+     * @see org.apache.turbine.pipeline.Valve#invoke(PipelineData, ValveContext)
+     */
+    @Override
+    public void invoke(PipelineData pipelineData, ValveContext context)
+        throws IOException, TurbineException
+    {
+        RunData data = pipelineData.getRunData();
+        String uri = data.getRequest().getRequestURI();
+
+        urlMapperService.mapFromURL(uri, data.getParameters());
+
+        // Pass control to the next Valve in the Pipeline
+        context.invokeNext(pipelineData);
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMapEntry.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMapEntry.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMapEntry.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMapEntry.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,143 @@
+package org.apache.turbine.services.urlmapper.model;
+
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+/**
+ * The url map model class
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ */
+@XmlType(name="map")
+@XmlAccessorType(XmlAccessType.NONE)
+public class URLMapEntry
+{
+    private Pattern urlPattern;
+    private Map<String, String> implicit = new LinkedHashMap<>();
+    private Map<String, String> ignore = new LinkedHashMap<>();
+    private Map<String, String> override = new LinkedHashMap<>();
+
+    private Map<String, Integer> groupNamesMap;
+    private Set<String> relevantKeys = null;
+
+    /**
+     * @return the urlPattern
+     */
+    @XmlElement(name="pattern")
+    @XmlJavaTypeAdapter(XmlPatternAdapter.class)
+    public Pattern getUrlPattern()
+    {
+        return urlPattern;
+    }
+
+    /**
+     * @param urlPattern the urlPattern to set
+     */
+    protected void setUrlPattern(Pattern urlPattern)
+    {
+        this.urlPattern = urlPattern;
+    }
+
+    /**
+     * @return the implicit parameters
+     */
+    @XmlElement(name="implicit-parameters")
+    @XmlJavaTypeAdapter(XmlParameterAdapter.class)
+    public Map<String, String> getImplicitParameters()
+    {
+        return implicit;
+    }
+
+    /**
+     * @param implicit the implicit parameters to set
+     */
+    protected void setImplicitParameters(Map<String, String> implicit)
+    {
+        this.implicit = implicit;
+    }
+
+    /**
+     * @return the ignored parameters
+     */
+    @XmlElement(name="ignore-parameters")
+    @XmlJavaTypeAdapter(XmlParameterAdapter.class)
+    public Map<String, String> getIgnoreParameters()
+    {
+        return ignore;
+    }
+
+    /**
+     * @param ignore the ignored parameters to set
+     */
+    protected void setIgnoreParameters(Map<String, String> ignore)
+    {
+        this.ignore = ignore;
+    }
+
+    /**
+     * @return the override parameters
+     */
+    @XmlElement(name="override-parameters")
+    @XmlJavaTypeAdapter(XmlParameterAdapter.class)
+    public Map<String, String> getOverrideParameters()
+    {
+        return override;
+    }
+
+    /**
+     * @param override the override parameters to set
+     */
+    protected void setOverrideParameters(Map<String, String> override)
+    {
+        this.override = override;
+    }
+
+    /**
+     * Get the map of group names to group indices for the stored Pattern
+     *
+     * @return the groupNamesMap
+     */
+    public Map<String, Integer> getGroupNamesMap()
+    {
+        return groupNamesMap;
+    }
+
+    /**
+     * Set the map of group names to group indices for the stored Pattern
+     *
+     * @param groupNamesMap the groupNamesMap to set
+     */
+    public void setGroupNamesMap(Map<String, Integer> groupNamesMap)
+    {
+        this.groupNamesMap = groupNamesMap;
+    }
+
+    /**
+     * Get the set of relevant keys for comparison (cached for performance)
+     *
+     * @return the relevantKeys
+     */
+    public Set<String> getRelevantKeys()
+    {
+        return relevantKeys;
+    }
+
+    /**
+     * Set the set of relevant keys for comparison (cached for performance)
+     *
+     * @param relevantKeys the relevantKeys to set
+     */
+    public void setRelevantKeys(Set<String> relevantKeys)
+    {
+        this.relevantKeys = relevantKeys;
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMappingContainer.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMappingContainer.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMappingContainer.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/URLMappingContainer.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,73 @@
+package org.apache.turbine.services.urlmapper.model;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * URL Map Container Model Class
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ */
+@XmlRootElement(name="url-mapping")
+@XmlAccessorType(XmlAccessType.NONE)
+public class URLMappingContainer
+{
+    /**
+     * Name of this map.
+     */
+    @XmlAttribute
+    private String name;
+
+    /**
+     * The list of map entries
+     */
+    private CopyOnWriteArrayList<URLMapEntry> urlMapEntries = new CopyOnWriteArrayList<>();
+
+    /**
+     * Set the name of this map.
+     *
+     * @param name
+     *            Name of this map.
+     */
+    protected void setName(String name)
+    {
+        this.name = name;
+    }
+
+    /**
+     * Get the name of this map.
+     *
+     * @return String Name of this map.
+     */
+    public String getName()
+    {
+        return name;
+    }
+
+    /**
+     * Get the list of map entries
+     */
+    @XmlElementWrapper(name="maps")
+    @XmlElement(name="map")
+    public List<URLMapEntry> getMapEntries()
+    {
+        return urlMapEntries;
+    }
+
+    /**
+     * Set new map entries during deserialization
+     *
+     * @param newURLMapEntries the newURLMapEntries to set
+     */
+    protected void setMapEntries(List<URLMapEntry> newURLMapEntries)
+    {
+        this.urlMapEntries = new CopyOnWriteArrayList<URLMapEntry>(newURLMapEntries);
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterAdapter.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterAdapter.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterAdapter.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterAdapter.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,43 @@
+package org.apache.turbine.services.urlmapper.model;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+import org.apache.turbine.services.urlmapper.model.XmlParameterList.XmlParameter;
+
+/**
+ * Creates Map objects from XmlParameterList objects and vice-versa.
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ */
+public class XmlParameterAdapter extends XmlAdapter<XmlParameterList, Map<String, String>>
+{
+    /**
+     * @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
+     */
+    @Override
+    public Map<String, String> unmarshal(XmlParameterList xmlList) throws Exception
+    {
+        // Make sure that order is kept
+        return xmlList.getXmlParameters().stream()
+                .collect(Collectors.toMap(xml -> xml.key, xml -> xml.value,
+                        (e1, e2) -> e1, LinkedHashMap::new));
+    }
+
+    /**
+     * @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
+     */
+    @Override
+    public XmlParameterList marshal(Map<String, String> map) throws Exception
+    {
+        XmlParameterList xmlList = new XmlParameterList();
+        xmlList.setXmlParameters(map.entrySet().stream()
+                .map(entry -> new XmlParameter(entry.getKey(), entry.getValue()))
+                .collect(Collectors.toList()));
+
+        return xmlList;
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterList.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterList.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterList.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlParameterList.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,89 @@
+package org.apache.turbine.services.urlmapper.model;
+
+import java.util.List;
+
+/*
+ * 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.
+ */
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ * A JAXB Class for holding a list of entries with key (in an attribute) and a value.
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ */
+@XmlAccessorType(XmlAccessType.NONE)
+public class XmlParameterList
+{
+    public static class XmlParameter
+    {
+        @XmlAttribute
+        public String key;
+
+        @XmlValue
+        public String value;
+
+        /**
+         * Default Constructor
+         */
+        public XmlParameter()
+        {
+            // empty
+        }
+
+        /**
+         * Constructor
+         *
+         * @param key the key
+         * @param value the value
+         */
+        public XmlParameter(String key, String value)
+        {
+            this.key = key;
+            this.value = value;
+        }
+    }
+
+    private List<XmlParameter> xmlParameters;
+
+    /**
+     * Get the list of XmlParameters
+     *
+     * @return the xmlParameters
+     */
+    @XmlElement(name="parameter")
+    public List<XmlParameter> getXmlParameters()
+    {
+        return xmlParameters;
+    }
+
+    /**
+     * Set a list of XmlParameters
+     *
+     * @param xmlParameters the xmlParameters to set
+     */
+    public void setXmlParameters(List<XmlParameter> xmlParameters)
+    {
+        this.xmlParameters = xmlParameters;
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlPatternAdapter.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlPatternAdapter.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlPatternAdapter.java (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/model/XmlPatternAdapter.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,50 @@
+package org.apache.turbine.services.urlmapper.model;
+
+/*
+ * 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.
+ */
+
+import java.util.regex.Pattern;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+/**
+ * Creates Regex Pattern objects.
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ */
+public class XmlPatternAdapter extends XmlAdapter<String, Pattern>
+{
+    /**
+     * @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
+     */
+    @Override
+    public Pattern unmarshal(String urlPattern) throws Exception
+    {
+        return Pattern.compile(urlPattern);
+    }
+
+    /**
+     * @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
+     */
+    @Override
+    public String marshal(Pattern pattern) throws Exception
+    {
+        return pattern.pattern();
+    }
+}

Added: turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/package.html
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/package.html?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/package.html (added)
+++ turbine/core/branches/URLMapperService/src/java/org/apache/turbine/services/urlmapper/package.html Tue Jan  5 14:52:24 2021
@@ -0,0 +1,29 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+<!-- head part is ignored -->
+</head>
+
+<body>
+Provide back-and-forth-mapping facilities for simplified URLs
+<br>
+<font size="-2">$Id$</font>
+</body>
+</html>

Added: turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/TurbineURLMapperServiceTest.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/TurbineURLMapperServiceTest.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/TurbineURLMapperServiceTest.java (added)
+++ turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/TurbineURLMapperServiceTest.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,105 @@
+package org.apache.turbine.services.urlmapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.fulcrum.parser.ParameterParser;
+import org.apache.turbine.Turbine;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.TurbineServices;
+import org.apache.turbine.test.BaseTestCase;
+import org.apache.turbine.util.TurbineConfig;
+import org.apache.turbine.util.uri.TemplateURI;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TurbineURLMapperServiceTest extends BaseTestCase
+{
+    private TurbineConfig tc = null;
+
+    private URLMapperService urlMapper = null;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        tc =
+            new TurbineConfig(
+                ".",
+                "/conf/test/TurbineURLMapperServiceTest.properties");
+        tc.initialize();
+
+        urlMapper = (URLMapperService)TurbineServices.getInstance().getService(URLMapperService.SERVICE_NAME);
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+        if (tc != null)
+        {
+            tc.dispose();
+        }
+    }
+
+    @Test
+    public void testMapToURL() throws Exception
+    {
+        assertNotNull(urlMapper);
+        HttpServletRequest request = getMockRequest();
+        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
+
+        PipelineData pipelineData = getPipelineData(request, response, tc.getTurbine().getServletConfig());
+        assertNotNull(pipelineData);
+
+        TemplateURI uri = new TemplateURI(pipelineData.getRunData());
+        uri.clearResponse(); // avoid encoding on mocked HTTPServletResponse
+        uri.addPathInfo("bookId", 123);
+        uri.setTemplate("Book.vm");
+        uri.addQueryData("detail", 0);
+
+        urlMapper.mapToURL(uri);
+        assertEquals("/wow/book/123", uri.getRelativeLink());
+        assertTrue(uri.getPathInfo().isEmpty());
+        assertTrue(uri.getQueryData().isEmpty());
+
+        uri = new TemplateURI(pipelineData.getRunData());
+        uri.clearResponse(); // avoid encoding on mocked HTTPServletResponse
+        uri.addPathInfo("bookId", 123);
+        uri.setTemplate("Book.vm");
+        uri.addQueryData("detail", 1);
+        uri.addQueryData("detail", 2);
+        uri.addQueryData("view", "collapsed");
+
+        urlMapper.mapToURL(uri);
+        assertEquals("/wow/book/123/1?view=collapsed", uri.getRelativeLink());
+        assertTrue(uri.getPathInfo().isEmpty());
+        assertEquals(1, uri.getQueryData().size());
+    }
+
+    @Test
+    public void testMapFromURL() throws Exception
+    {
+        assertNotNull(urlMapper);
+        HttpServletRequest request = getMockRequest();
+        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
+
+        PipelineData pipelineData = getPipelineData(request, response, tc.getTurbine().getServletConfig());
+        assertNotNull(pipelineData);
+        ParameterParser pp = pipelineData.get(Turbine.class, ParameterParser.class);
+        assertNotNull(pp);
+        assertTrue(pp.keySet().isEmpty());
+
+        urlMapper.mapFromURL("/app/book/123/4", pp);
+
+        assertEquals(3, pp.keySet().size());
+        assertEquals(123, pp.getInt("bookId"));
+        assertEquals("Book.vm", pp.getString("template"));
+        assertEquals(4, pp.getInt("detail"));
+    }
+
+}

Added: turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/model/URLMappingContainerTest.java
URL: http://svn.apache.org/viewvc/turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/model/URLMappingContainerTest.java?rev=1885149&view=auto
==============================================================================
--- turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/model/URLMappingContainerTest.java (added)
+++ turbine/core/branches/URLMapperService/src/test/org/apache/turbine/services/urlmapper/model/URLMappingContainerTest.java Tue Jan  5 14:52:24 2021
@@ -0,0 +1,65 @@
+package org.apache.turbine.services.urlmapper.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class URLMappingContainerTest
+{
+    URLMappingContainer container;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        try (InputStream reader = new FileInputStream("conf/turbine-url-mapping.xml"))
+        {
+            JAXBContext jaxb = JAXBContext.newInstance(URLMappingContainer.class);
+            Unmarshaller unmarshaller = jaxb.createUnmarshaller();
+            container = (URLMappingContainer) unmarshaller.unmarshal(reader);
+        }
+    }
+
+    @Test
+    public void testGetName()
+    {
+        assertNotNull(container);
+        assertEquals("default", container.getName());
+    }
+
+    @Test
+    public void testGetMapEntries()
+    {
+        assertNotNull(container);
+
+        List<URLMapEntry> mapEntries = container.getMapEntries();
+        assertNotNull(mapEntries);
+        assertNotEquals(0, mapEntries.size());
+
+        URLMapEntry entry = mapEntries.get(0);
+        assertNotNull(entry);
+
+        Pattern pattern = entry.getUrlPattern();
+        assertNotNull(pattern);
+        assertTrue(pattern.matcher("/app/book/123").matches());
+
+        Map<String, String> implicit = entry.getImplicitParameters();
+        assertNotNull(implicit);
+        assertEquals(2, implicit.size());
+        assertEquals("Book.vm", implicit.get("template"));
+        assertEquals("0", implicit.get("detail"));
+    }
+
+}