Fork me on GitHub

Introduction

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

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!

Turbine URLMapper Configuration

You need to

  • register the URL Mapper service in turbine configuration (TR.properties)
  • register the valve in the pipeline (turbine-classic-pipeline.xml)

Consider the following example configuration: MappedTemplateLink is for now optional, you can add it as a separate tool or just replace the existing TemplateLink.

	# -------------------------------------------------------------------
	#
	#  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

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

  <valves>
    <valve>org.apache.turbine.services.urlmapper.URLMapperValve</valve>
    ...

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.

Define your patterns

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

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

That is resolvableParam 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

 /(?<resolvableParam>\\w+)
Technically this isa non matching named group, with the group name resolvableParam and in this case a sequence of alphabetical characters with minimal length of 1. The same is true for the predefined webAppRoot and contextPath.

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

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

Any parameter is resolved as a group name (Java Pattern->Groups and Capturing->Group name). These group names are predefined (symbolic group name)):

  • <webAppRoot>
  • <contextPath>
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 :
<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>
        ...

Three parameters are evaluated:

  • a parameter name template and value Book.vm
  • a parameter detail and value 0
  • a parameter bookId with any value, e.g. 4
This will be converted, if matched, to an URL like /book/4. 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) mapFromURL.

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

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

Service Description

The main methods of the service TurbineURLMapperService are

  • mapToUrl, which as the Javadoc explains "maps a set of parameters (contained in TurbineURI PathInfo and QueryData) to a TurbineURIs"
  • mapFromUrl, which "maps a simplified URL to a set of parameters"

Matrix

Turbine URL Mapper Model
Mechanism Method Pattern Implicit Param Override Param Ignore Param
Converts Parameterized URL to simplified URL mapToUrl "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. "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 - 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 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
Resolves URL to Params for evaluating by the backend mapFromUrl The pattern of the URL to be matched to evaluate parameter resolving Param key/value will be set implicitely Overrides (provided) URL parameter with provided value will remove parameter key/value from result parameter list, even if provided as capturing group name

N.B. Symbolic group names webAppRoot and context could not be ignored or overridden!

Usage

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

  
  $mlink.addPathInfo("world","nice").getRelativeLink()
  ## should result into a URL: /beautiful/world
  

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).


   // 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);
     
More examples ...