How to build a locale aware VirtualURIMapping in Magnolia. With RegExp.

In a standard Magnolia installation, VirtualURIMapping are locale agnostic: they simply don’t care about locale. This is quite nice, if you have single locale website, but normally, this is not the case.
So.. I’0ve googled and found this and this. But not enough, I need to have Regexp in fromUri field.. so, let’s write that!

First, focus on configuration (Node2Bean in-primis):


And now the code. Unfortunately, standard RegexpVirtualURIMapping direct access the “toURI” class member and that is in memory (pooled in a Magnolia registry). So, we need to completely (?!?!) rewrite the class.

package com.matteopelucco.virtualurimapping;

import info.magnolia.cms.beans.config.DefaultVirtualURIMapping;
import info.magnolia.cms.beans.config.QueryAwareVirtualURIMapping;
import info.magnolia.context.MgnlContext;

import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

public class LocaleRegexpVirtualURIMapping extends DefaultVirtualURIMapping implements QueryAwareVirtualURIMapping {
  private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LocaleRegexpVirtualURIMapping .class);
  private Pattern regexp;
  // local mapping 
  private Map<string , String> localizedToURI = new LinkedHashMap</string><string , String>();

public MappingResult mapURI(final String uri) {
  return mapURI(uri, null);

public MappingResult mapURI(final String uri, String queryString) {
  if (regexp != null) {
    final Matcher matcher;
    if (queryString != null) {
      matcher = regexp.matcher(uri + "?" + queryString);
    } else {
      matcher = regexp.matcher(uri);

    if (matcher.find()) {
      final MappingResult r = new MappingResult();
      final int matcherCount = matcher.groupCount();
      try {
        final String replaced = matcher.replaceAll(getToURI()); // this is the only piece rewritten, since it direct accesses to class member
        r.setLevel(matcherCount + 1);
        return r;
      } catch (IndexOutOfBoundsException e) {
        log.warn("{} misconfigured: {}", toString(), e.getMessage());

  return null;

public void setFromURI(String fromURI) {
  this.fromURI = fromURI;
  this.regexp = Pattern.compile(fromURI);

 * Dynamic part
public String getToURI() {

  String language = "";
  Locale locale = MgnlContext.getAggregationState().getLocale();
  if (locale != null) {
    language = locale.getLanguage();

  return StringUtils.defaultIfEmpty(localizedToURI.get(language), super.getToURI());

/** node2bean **/
public Map</string><string , String> getLocalizedToURI() {
  return localizedToURI;

public void setLocalizedToURI(Map</string><string , String> localizedToURI) {
  this.localizedToURI = localizedToURI;

public void addLocalizedToURI(String key, String value) {
  this.localizedToURI.put(key, value);




And.. that’s it! Just configure the right mapping (yes, they are aware of RegeExp replacements!) and the game is done.
Hope it helps!

Latest articles

Written by:

One Comment

  1. Jozef Chocholáček

    Yes, it definitely helps! You’ve saved me few hours of investigating and coding, man. I owe you a beer, even two! 😉

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

− 2 = due