May 13, 2020
Estimated Post Reading Time ~

Sling Mappings

Sling mappings provide a way of re-mapping content based on the url that has been received. It is the best way of hiding content or giving it a more appropriate url
In AEM all of the content available to the user is in /content/site/... but we don't want our users to have to go to a url like https://site.com/content/site.html so we use sling mappings to redirect any request for/to/content/site, and we also want the reverse, if we have any content links on the page to /content/site/... to be change to be /...

Before I go into how you configure sling mappings I will make one statement. For reverse mappings, you can't have multiple mappings that affect the same content, AEM will always pick the first reverse mapping and use that.
i.e if we have a reverse mapping for /content/site/abc redirecting /abc for 2 sites then the url that it will be redirected to will be the first site, which as you can imagine is not what we would want.

This is why I tend to have a sling subproject with a subproject for every environment. If we are only doing forward mappings then it wouldn't matter because the forward mappings tend to be hostname specific and multiple mappings can point to the same place.

Sling mappings are only really useful on the publisher so i tend to rename the default /etc/map folder to /etc/map.publish and add configuration to tell the publisher to read it's mappings from here
<?xml version="1.0" encoding="UTF-8"/>
<jcr:root xmlns:sling=http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr=http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
resource.resolver.map.location="/etc/map.publish"/>


org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.xml
By doing this in the config.publish folder of your app you guarantee that the sling mappings will only ever be run on the publisher.

So lets look at the structure of the sling mapping folder
<protocol>
    <domain structure>
        <mapping1>
            .content.xml
        <mapping2>
            .content.xml


You can have as many mappings are you require to make your application work properly.

Obviously, the protocol is dependent on what the protocol is that hits the publisher. Quite often the communication between the dispatcher and the publisher is on http so even though your site might be on https you would still configure the sling mappings in the http folder. You can change this by adding RequestHeader set X-Forwarded-SSL "on" to your apache configuration in the dispatcher

Domain Structure
The domain structure is quite clever, it can tell based on a set of rules if your url has a port in it, imagine the following folders under the http folder

folder name                                        domain name
localhost                                      http://localhost
localhost.99                                 http://localhost:99
abc.localhost.com                         http://abc.localhost.com
abc.localhost.com.99                    http://abc.localhost:99

On top of this, you can configure the sling mapping itself so that it knows more about the url
<?xml version="1.0"encoding="UTF-8"?>
<jcr:root xmlns:sling=http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr=http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:Mapping"
sling:internalRedirect="/content/site/$1"
sling:match=".*\.localhost\.\d*/(.+)$"/>


The sling:match attribute is what is used to match incoming messages and can be any regex to match the inbound request.
We use sling:internalRedirect to determine where the content location is within AEM.

Reverse mappings are exactly that, the reverse. We put the regex into the sling:internalRedirect and the matchers ($1) in the sling:match
<?xml version="1.0" >encoding="UTF-8"?>
<jcr:root xmlns:sling=http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr=http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:Mapping"
sling:internalRedirect="[/content/site/(.+)]"
sling:match="$1"/>

Using regex in the sling:internalRedirect is normally not allowed unless you are doing a reverse mapping. By adding regex into the internalRedirect sling determines that the rule is only to be used as a reverse mapping. You may see error messages in the logs because of this.

If you have a base mapping such as
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling=http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr=http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:Mapping"
sling:internalRedirect="[/content/site/$1]"
sling:match="(.+)$"/>

The mapping above will redirect any request to its appropriate location under /content/site i.e. /abc.html will be /content/site/abc.html

If this was the only sling mapping you put in place you would have a lot of problems as calling for your client libs would fail because /etc/sample/clientlib.css would end up being /content/site/etc/sample/clientlib.css, because of this you will need to put in a few additional sling mapping rules.

By default rules are
  • root
    • Redirects the location of /
  • redirect
    • The mapping above for all default forward mappings
  • bin
    • Support for servlets on /bin
  • clientlibs
    • Support for AEM client libs on /etc/clientlibs
  • dam
    • Support for the dam in /content/dam/???
    • Can have a new location if used in conjunction with reverseDam
  • etc
    • Support for designs in /etc/designs
  • libs
    • Support for granite libs in /libs/granite
  • reverse
    • Support for reverse mappings of content
  • reverseDam
    • Support for reverse mappings to hide /content/dam
    • I don't always have reverseDam and quite often just leave the dam exposed
In a multi-tenancy situation where you have multiple apps on the same domain, I have all of the above mappings on the core application and allow the sub-applications to add their own appropriate mappings to the same folders.

Deployment
Having too many sling mappings on a server is messy, confusing and sometimes causes problems, especially when it comes to reverse mappings, as I mentioned AEM will choose the first reverse mapping for a path and it may not be the one you are after.

Because of this, I tend to have a sling subproject that is a heading for all environments and a sub-project for each of the environments that the build will be deployed in.

In the ui.apps I put each of the sling deployments into install.<environment> folders so it will only be deployed in the environments that it needs to. Because we have a project that has the sling mappings for each individual environment, I tend to also put the config.<environment> folders in there as well so that each environment only ever has content/configuration that is required for the environment


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.