March 31, 2015

Updating resource bundles in Hippo using Groovy script LAB

Bart Bart Vreeken is a passionate software developer and certified Oracle Java enterprise architect, working at Indivirtual . He has over 8 years of experience in software development with a focus on Java and core web technologies such as HTML5, CSS and Javascript. He also has extensive experience with Content Management Systems such as Hippo CMS. When Bart is not sitting behind his computer he likes to spend time with his kid. He also enjoys tennis and running. Code for this lab can be found in Github here.  

 

Introduction

Often, when deploying a new release through the DTAP environment, resource labels need to be updated as well. For example you added a new label for a button. Or you defined some configuration in a resource label, like a date pattern, which is used into your JSP file (although not considered a best practice, but we – developers - do it anyway). In most cases resource labels are tightly coupled to your release and you can’t afford to miss a label on production.

//onehippo-prod.global.ssl.fastly.net/binaries/ninecolumn/content/gallery/connect/labs/missing-label.png

Oops! A label is missing on production...

Updating the configuration is quite easy: The Hippo CMS uses a XML bootstrap mechanism to add (or change) the HST configuration. However, updating content in the CMS like resource bundles can be quite cumbersome. The process of editing the resource bundle XML is error prone and there is no proper mechanism to add new labels to an existing bundle. So every time you add a new resource label you need to do it manually. For every DTAP environment.

This article describes a solution using Groovy to update your existing resource bundle automatically when deploying a new release.  This strategy aims to ease the process of adding (or delete) labels for developers.

Solution

In brief, our solution consists of three parts. The first part is the data. The data contains all necessary instructions to add one or more labels to an existing bundle. It is stored in well-known property files and located in the any hippo plugin (jar files). Second, we use a utility Java class to read the property files from the classpath. The last part is the Groovy script which makes a call to the utility class, retrieves the data from the property files and eventually updates the resource bundle node with the new label(s).

//onehippo-prod.global.ssl.fastly.net/binaries/ninecolumn/content/gallery/connect/labs/solution-snapshot.png

The property files

The property files contain the instructions to load new resource labels into an existing resource bundle, or to delete an existing one. We came up with a very simple and readable syntax to express these instructions. Basically, the property file holds instructions for one single resource bundle only. Also, the instructions apply to adding or deleting labels. Not both. A typical property file should look like this:

#the ID of the resourcebundle
bundle.id=nl.company.cms.plugins.featurexyz.MyProfileBundle

#the instruction (ADD | DELETE)
bundle.action=ADD

#new message 1
profiel.mijn-gegevens.page.description.message=my profile
profiel.mijn-gegevens.page.description.message_nl=mijn gegevens
profiel.mijn-gegevens.page.description.description=mijn gegevens

#new message 2
...

Although it looks like overkill to have a property file for every single resource bundle, this mechanism works really well when developing new features for the Hippo CMS. The Hippo CMS has a pluggable architecture which makes new features available through plugins. So usually you have only one resource bundle per plugin and one property file to add new labels.

Note: The utility class in the sample download assumes that all property files (used for resource bundle instructions) are located in src/main/resources/labels. The resulting JAR artifact has a /labels package which holds all property files. The files can have any name you like and you can store as many files you need.

The Java utility class

The Java utility class is the glue between the property instruction files and the Groovy script. It scans the WebApp classpath for any property files. For every file it parses and processes the properties, creates a Java model structure and returns this model to the Groovy script.

Although this ‘utility glue’ can (probably) be developed in Groovy as well, I believe it’s a good idea to have a separate Java helper for this. Now, this relatively complicated process can be debugged easily and keeps your Groovy script small and clean: It does what it is designed for (separation of concerns): updating your JCR nodes.

Note: As mentioned in Using the updater editor, Groovy scripts are executed using a class loader in the CMS app context. Therefore make sure you put the utility class into the CMS web project or any dependency within the WEB-INF/lib of the CMS.war.

The Groovy script

Groovy scripts allow developers to run scripts against the Hippo (JCR) repository and change existing content. The attached sample script iterates through all existing resource bundle nodes using the following query path:

/jcr:root/content/*//element(*,resourcebundle:resourcebundle)

For every bundle node it checks for ‘pending tasks’ by calling the Java utility class. After the bundleList has been received, it starts processing the instructions.

 

String bundleId = node.getProperty("resourcebundle:id").getString();
List bundleList = ResourceBundleBootstrapUtilities.findResourceBundleConfig(bundleId);
for (item in bundleList) {
  log.debug "Possible candidate key: '${item.getKey()}'"
  boolean exists = doesKeyAlreadyExist(node, item.getKey());
  if (!exists && item.getAction().toString() == "ADD") {
    log.debug "Found non-existing key: '${item.getKey()}'. Will be ADDED";
    addItem(node, item);
  } else if (exists && item.getAction().toString() == "DELETE") {
    log.debug "Found existing key: '${item.getKey()}'. Will be REMOVED";
    removeItem(node, item);
  }
}

 

Although you can choose to execute the script manually using the Updater Editor, it’s probably a better idea to automate the execution. The Hippo CMS offers a perfect mechanism to automate script execution on startup by adding the script to the /hippo:configuration/hippo:update/hippo:queue in your bootstrap module.

Benefits

  • Easy to use. This strategy makes adding (or deleting) resource labels no longer a painful process for developers. Using property files to add new labels is straightforward and there’s no need to edit XML bootstrap files.

  • Easy to deploy. Developers don’t need to make release instructions for new or deleted labels (they always forget somehow!). Naturally, during deployment there’s no need to create these new labels manually, for every environment.

  • Integrated with your release. Your bundles updates are tightly coupled to your release because you simply put the property files in the software package you want to release.

Drawbacks

  • Local startup can take a bit longer. Depending on the number of resource bundles and property files starting your local environment can take a bit longer than normal.

Conclusion

Updating an existing resource bundle through a DTAP environment  takes some effort. Hippo CMS provides a powerful bootstrap mechanism to add or change (HST) configurations, but the mechanism is not capable of adding or deleting labels.

Fortunately, this problem can be solved easily by using the proposed solution in this article, using property files. New labels can be added in a simple and clean way by developers. Also, it doesn’t require additional effort to release these labels in the next DTAP environment.

Want to see for yourself what it's like to build on Hippo CMS?