Customize Link Processing

Introduction

Goal

Customize how internal links are processed in Bloomreach Experience Manager's delivery tier.

Background

Bloomreach Experience Manager is a content-oriented system and therefore its delivery tier must map documents to pages in a sitemap. Internal links within a site are created and resolved using this mapping. This process of creating and resolving links provides an extension point through the HstLinkProcessor interface. By implementing this interface developers can pre-process and/or post-process internal links.

HstLinkProcessor

The org.hippoecm.hst.core.linking.HstLinkProcessor interface defines two methods:

  • HstLink postProcess(HstLink link)
    Called after the delivery tier creates a link through org.hippoecm.hst.core.linking.HstLinkCreator.

  • HstLink preProcess(HstLink link)
    Called before the delivery tier matches a link through org.hippoecm.hst.core.request.HstSiteMapMatcher.

The delivery tier uses a chain of link processors. Any number of custom link processors can be added to the chain through Spring configuration (see example below).

HstLinkProcessor implementations must be thread-safe. In other words, they must not maintain state in member variables or otherwise.​

Example

Use Case

Consider a project created from the Bloomreach Experience Manager Maven archetype with the Simple Content feature added.

Documents of type myproject:contentdocument are stored in the repository folder /content/documents/myproject/content.

These documents are mapped to a URL and page template through the following sitemap items:

/hst:myproject/hst:configurations/myproject/hst:sitemap:
  /content:
    jcr:primaryType: hst:sitemapitem
    hst:componentconfigurationid: hst:pages/contentlist
    hst:relativecontentpath: content
    /_any_.html:
      jcr:primaryType: hst:sitemapitem
      hst:componentconfigurationid: hst:pages/contentpage
      hst:relativecontentpath: ${parent}/${1}

As a result, a document /content/documents/myproject/content/sample-document would be available at the URL http://localhost:8080/site/content/sample-document.html.

Now suppose you organize your simple content documents using subfolders with names corresponding to the first character of the names of documents inside them. For example:

/content/documents/myproject:
  /content:
    jcr:primaryType: hippostd:folder
    /a:
      jcr:primaryType: hippostd:folder
      /about-hippo:
        jcr:primaryType: hippo:handle
      /archetype:
        jcr:primaryType: hippo:handle
    /b:
      jcr:primaryType: hippostd:folder
      /best-practices:
        jcr:primaryType: hippo:handle
      /big-hippo:
        jcr:primaryType: hippo:handle
    /c:
      jcr:primaryType: hippostd:folder
      /code-formatting:
        jcr:primaryType: hippo:handle
      /create-project:
        jcr:primaryType: hippo:handle

This means that these documents will be available at URLs like http://localhost:8080/site/content/a/about-bloomreach.html, http://localhost:8080/site/content/b/best-practices.html etc.

However your requirements state that the URLs should not contain the subfolder name and look like http://localhost:8080/site/content/about-bloomreach.html, http://localhost:8080/site/content/best-practices.html etc.

This is a typical use case for a custom link processor.

Solution

The solution is to implement org.hippoecm.hst.core.linking.HstLinkProcessor as follows:

  • For any link created with a relative path starting with content/,  e.g. content/a/about-bloomreach, remove the subfolder path element (a/) so it becomes e.g. content/about-bloomreach and meets your URL requirements.
  • For any link matched in the sitemap with a relative path starting with content/, e.g. content/about-bloomreach, add the subfolder path element based on the first character of the path element following content/, so it becomes e.g. content/a/about-bloomreach and matches the relative content path of the document.

Implementation

HstLinkProcessor Implementation

site/components/src/main/java/org/example/ExampleHstLinkProcessor.java

package org.example;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hippoecm.hst.core.linking.HstLink;
import org.hippoecm.hst.linking.HstLinkProcessorTemplate;

public class ExampleHstLinkProcessor extends HstLinkProcessorTemplate {

    @Override
    protected HstLink doPostProcess(HstLink link) {
        String path = link.getPath();
        if (path.startsWith("content/")) {
            String pattern = "(content/)./(.+)";
            Pattern r = Pattern.compile(pattern);
            Matcher m = r.matcher(path);
            if (m.find()) {
                path = m.replaceAll("$1$2");
                link.setPath(path);
            }
        }
        return link;
    }

    @Override
    protected HstLink doPreProcess(HstLink link) {
        String path = link.getPath();
        if (path.startsWith("content/")) {
            String pattern = "(content)(/.)(.?)";
            Pattern r = Pattern.compile(pattern);
            Matcher m = r.matcher(path);
            if (m.find()) {
                path = m.replaceAll("$1$2$2$3");
                link.setPath(path);
            }
        }
        return link;
    }

}

Spring Configuration

site/components/src/main/resources/META-INF/hst-assembly/overrides/customLinkProcessors.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

  <bean id="org.hippoecm.hst.core.linking.HstLinkProcessor" class="org.hippoecm.hst.core.linking.HstLinkProcessorChain">
    <property name="processorsInChain">
      <list>
        <bean class="org.example.ExampleHstLinkProcessor" />
      </list>
    </property>
  </bean>

</beans>
Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?