Adobe

Unit Testing JCR Resource Resolver Mappings

Website Design. Developing Programming And Coding Technologies.

Recently, I had a task to shorten URLs and remove the HTML extension in AEM. On top of that, I had to add a context root to the URLs so that these mappings would happen.

  • /content/myapp/en_US.html -> /mycontext/
  • /content/myapp/en_US/homepage.html -> /mycontext/homepage/
  • /content/myapp/es_ES.html -> /mycontext/es_ES/
  • /content/myapp/es_ES/homepage.html -> /mycontext/es_ES/homepage/

Shorting URLs in AEM consists of two things:

  • The dispatcher will receive requests for short extension-less URLs and must rewrite them before passing them through to the publishers.
  • The links within the HTML markup rendered by AEM must be rewritten to their short extension-less versions.
Adobe - Content for Everyone
Content for Everyone

Companies that can quickly and consistently meet the demands of consumers are thriving in an era of infinite content. Learn about how to build fluid experiences for your omnichannel customers.

Get the Guide

Setting up Apache is straight forward. You need to inspect the incoming request and translate it to its long version before sending it on to AEM. What I had trouble with was configuring the Sling mappings to rewrite URLs. There are 2 ways to achieve this:

  • Update the content under /etc/map
  • Add the mapping to the resource.resolver.mapping property of the org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl OSGi configuration

What I Do

In general, I have always opted for the second. I find it is simpler to manage than managing content under /etc/map. The only annoying thing is that if you make a change it causes every OSGi component that depends on it to restart. Having to wait a long time between changes is frustrating. This is especially true if I am trying to work out new mapping rules.

Nowadays, I find myself doing more and more TDD. In particular, I’ve been leveraging wcm.io Sling Mocks. With JUnit 5 and AssertJ, I can develop Sling Models and OSGi components without ever having to load a page.

And this is it! I wrote the unit test and then I focused on creating a set of outgoing mappings. Once satisfied, I popped them into the OSGi configuration. Then, I sat around for 7 minutes until the system became responsive again, and the job was complete!

import static org.assertj.core.api.Assertions.assertThat;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import com.google.common.collect.ImmutableMap;
import io.wcm.testing.mock.aem.junit5.*;

@ExtendWith(AemContextExtension.class)
class UrlMappingTest {

    private static final ImmutableMap<String, Object> PROPERTIES =
        ImmutableMap.of("resource.resolver.mapping", ArrayUtils.toArray(
            "/:/",
            "^/content/myapp/en_US\\.html</mycontext/",
            "^/content/myapp/en_US/(.+)\\.html</mycontext/$1/",
            "^/content/myapp/(.+)\\.html</mycontext/$1/"
        ));

    private final AemContext context =
            new AemContextBuilder().resourceResolverType(ResourceResolverType.JCR_MOCK)
                                   .resourceResolverFactoryActivatorProps(PROPERTIES)
                                   .build();

    @Test
    public void testShortExtensionlessMappings() {

        final ResourceResolver resourceResolver = this.context.resourceResolver();

        final String defaultLocaleRoot =
            resourceResolver.map("/content/myapp/en_US.html");
        assertThat(defaultLocaleRoot).isEqualTo("/mycontext/");

        final String defaultLocalePath =
            resourceResolver.map("/content/myapp/en_US/hello-world.html");
        assertThat(defaultLocalePath).isEqualTo("/mycontext/hello-world/");

        final String localeRoot =
            resourceResolver.map("/content/myapp/es_US.html");
        assertThat(localeRoot).isEqualTo("/mycontext/es_US/");

        final String localePath =
            resourceResolver.map("/content/myapp/es_US/hello-world.html");
        assertThat(localePath).isEqualTo("/mycontext/es_US/hello-world/");

        final String qsParams =
            resourceResolver.map("/content/myapp/es_US/hello-world.html?foo=bar");
        assertThat(qsParams).isEqualTo("/mycontext/es_US/hello-world/?foo=bar");
    }
}

I hope you found this blog post useful. For any additional questions, please comment below and be sure to check out our other Adobe blogs.

About the Author

Juan Ayala is a Lead Developer in the Adobe practice at Perficient, Inc., focused on the Adobe Experience platform and the things revolving around it.

More from this Author

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to the Weekly Blog Digest:

Sign Up
Categories