<?xml version="1.0"?>
<!--
 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.
-->

<document>

 <properties>
  <title>URL Mapper Howto</title>
 </properties>

<body>

<section name="Introduction">

<p>
Unaltered Turbine URLs may look like this:
<code>http://www.foo.com:8080/CONTEXT/servlet/MAPPING/template/Foo.vm</code>.<br/>
But you want shorter URLs? Maybe this URL would suit you better:
<code>http://www.foo.com:8080/CONTEXT/servlet/beautiful/world</code>
</p>

This HOWTO describes, how you can control the pathinfo or query part of a url (behind the webapproot and the context) and the context with a mapping (routing) file 
defined in xml, json or yaml format to become more simplified or beautiful! 

</section>

<section name="Turbine URLMapper Configuration">

<p>
You need to
<ul>
<li>register the URL Mapper service in turbine configuration (TR.properties) </li>
<li>register the valve in the pipeline (turbine-classic-pipeline.xml)</li>
</ul> 
</p>


<p>Consider the following example configuration:

MappedTemplateLink is for now optional, you can add it as a separate tool or just replace the existing TemplateLink.
</p>

<source><![CDATA[
	# -------------------------------------------------------------------
	#
	#  U R L  M A P P E R  S E R V I C E
	#
	# -------------------------------------------------------------------
	
	# required
	services.URLMapperService.classname=org.apache.turbine.services.urlmapper.TurbineURLMapperService
	
	# configFile is required here! xml, json and yml supported as extension.
	services.URLMapperService.configFile = /conf/turbine-url-mapping.xml
	
	# new mapper (optional)
	tool.request.mlink=org.apache.turbine.services.urlmapper.MappedTemplateLink
	# tool.request.jlink= org.apache.turbine.services.pull.tools.TemplateLink
]]></source>

<p>To resolve a provided / mapped URL add the valve into pipeline (pipeline.default.descriptor = /conf/turbine-classic-pipeline.xml). 
</p>

<source><![CDATA[
  <valves>
    <valve>org.apache.turbine.services.urlmapper.URLMapperValve</valve>
    ...

]]></source>

<p>This will check if the provided URL matches any pattern, resolves it given in the path or implicitly as defined in

the URLMapperService's configfile.</p> 

</section>

<section name="Define your patterns">

<p>The URL Mapping Mechanism uses a well defined pattern format. 
The pattern format scheme is defined as follows, e.g. in JSON:</p>

<source><![CDATA[
 "pattern":  "/(?<webAppRoot>[.\\-\\w]+)/(?<contextPath>\\w+)/(?<resolvableParam>\\w+)/beautifulname"
]]></source>

<p>That is <strong>resolvableParam</strong> is just a specific parameter name or key, which should be resolved after the context slash and before the next URL part, which starts with "/beatifulname". 

It  has to be set like this</p> 

<source><![CDATA[
 /(?<resolvableParam>\\w+)
]]></source>

Technically this isa non matching named group, with the group name <strong>resolvableParam</strong> and 

in this case a sequence of alphabetical characters with minimal length of 1. 

The same is true for the predefined <strong>webAppRoot</strong>  and <strong>contextPath</strong>. 

<p>Another condition to be met, is that the parameter name must follow the "Java Named Group pattern characters restriction":</p>

<source><![CDATA[
 NAMED_GROUPS_PATTERN = Pattern.compile("(?<([a-zA-Z][a-zA-Z0-9]*)>.+?)");
]]></source>

<p>
Any parameter is resolved as a <i>group name</i>
 (<a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html">Java Pattern->Groups and Capturing->Group name</a>).
 
These group names are predefined (symbolic group name)):
 
<ul><li><strong>&lt;webAppRoot&gt;</strong></li><li><strong>&lt;contextPath&gt;</strong></li></ul>

Be aware, that this does not allow replacing parameters containing other characters (e.g underscore or hyphens). You may use implicit parameter matching. 

Following is an example for a configuration : 
</p>

<source><![CDATA[
<url-mapping name="default">
    <maps>
        <map>
            <pattern>/(?&lt;webAppRoot&gt;[.\\-\\w]+)/(?&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>
        ...
]]></source>

<p>Three parameters are evaluated:

<ul><li>a parameter name <strong>template</strong> and value <strong>Book.vm</strong></li>
<li>a parameter <strong>detail</strong> and value <strong>0</strong></li>
<li>a parameter <strong>bookId</strong> with <i>any</i> value, e.g. <strong>4</strong></li>
</ul>
This will be converted, if matched, to an URL like <strong>/book/4</strong>. 
The pattern uses type restrictions for the value, e.g. number for the bookId and a extended character set for the webAppRoot, which will be applied in (back resolving) <strong>mapFromURL</strong>.
</p>

<p>Another example in <strong>JSON</strong> format, which is much more readable, if not viewing in a browser is here (showing a shortened URL by replacing two parameters):</p>

<source><![CDATA[
{
  "name": "default",
  "maps": [
	{
		"pattern": "/(?<webAppRoot>[\\w]+)/(?<contextPath>\\w+)/register",
		"implicit-parameters": {
			"page": "Register",
			"role": "anon"
		}
	},
        ...
]]></source>

</section>

<section name="Service Description">

<p>The main methods of the service <strong>TurbineURLMapperService</strong> are</p> 

<ul>
<li><strong>mapToUrl</strong>, which as the Javadoc explains "maps a set of parameters (contained in TurbineURI PathInfo and QueryData) to a TurbineURIs"</li>
<li><strong>mapFromUrl</strong>, which "maps a simplified URL to a set of parameters"</li>
</ul> 

  <subsection name="Matrix">
      <table>
        <tr>
         <th colspan="6">Turbine URL Mapper Model</th>
        </tr>
        <tr>
          <th>Mechanism</th><th>Method</th><th>Pattern</th><th>Implicit Param</th><th>Override Param</th><th>Ignore Param</th>
        </tr>
       
        <tr><th>Converts Parameterized URL to simplified URL</th><th>mapToUrl</th><td>
         "Match Group Name": The pattern of the target URL after evaluation of parameters. If a group name is set, a matching parameter key must be provided and the value will replace the group name in the target URL.</td>
         <td>"Exact Filter", "Reduce": If a parameter key is is set implicitely, both key and value must exactly matched by a parameter pair in the provided (unmapped) URI. It will then be removed</td>
         <td>- An override could be achieved by hard coding it in the pattern and filterign in implicit param. 
         On the other hand you can then ignore the parameter</td>
         <td>The parameter will be removed from the required parameter key set and also from the target URL if it is provided as a group name</td></tr>
        
        <tr><th>Resolves URL to Params for evaluating by the backend</th><th>mapFromUrl</th><td>
        The pattern of the URL to be matched to evaluate parameter resolving</td>
        <td>Param key/value will be set implicitely</td>
        <td>Overrides (provided) URL parameter with provided value</td>
        <td>will remove parameter key/value from result parameter list, even if provided as capturing group name</td></tr>
      </table>
    </subsection>
    
   <p>N.B. Symbolic group names <i>webAppRoot</i> and <i>context</i> could not be ignored or overridden!</p>
   
   
</section>
<section name="Usage">

<p>
Use the methods getRelative or getAbsoluteLink of the provided convenience class MappedTemplateLink class (of type TemplateLink) 
in a velocity template like this:
</p>

<source><![CDATA[
  
  $mlink.addPathInfo("world","nice").getRelativeLink()
  ## should result into a URL: /beautiful/world
  
]]></source>

<p>
<p>Alternatively you can use the service explicitely in Java, e.g. in a Java Action or other class if you inject the URLMapperService (or provide this in a shared  controller class).</p>

<source><![CDATA[

   // inside any assembler you may alternatively use annotation 
   // @TurbineService( "URLMapperService" ) urlMapper;
   
   URLMapperService urlMapper = (URLMapperService) TurbineServices.getInstance().getService(URLMapperService.SERVICE_NAME);

  // Any turbineURI ..e.g. from PoolService or 
  TurbineURI uri  ... 
          
  urlMapper.mapToURL( uri );

  
  // use it, e.g by putting it into a velocity context (org.apache.velocity.context.Context(
  context.put("myLink", link);
     
]]></source>

 
More examples ...
</p>


</section>

</body>
</document>
