Making AEM Groovy Without the Console
Updated: Feb 6, 2022
I was recently doing some research on error handling in AEM. I came across this Sling documentation page. Pretty standard stuff except for the very last line.
Thus to overwrite the default error handler servlet provide a servlet or script for the default extension, for example /apps/sling/servlet/errorhandler/default.groovy.
A groovy script, in the context of Sling. Was the documentation wrong? Did they mean to write jsp instead of groovy? I got down to doing some research.
By the end of this post, you will be writing the following in Groovy
OSGi components i.e. servlets, services, and sling models.
Scripts that Sling can resolve and execute.
GString templates that Sling can resolve and render.
For years I have associated Groovy with the AEM Groovy Console. If you Google "aem groovy script" you will get bombarded with the Groovy Console. Now try "sling groovy script". Two different things.
I found this document filed under "Old Stuff". As it instructs, you can deploy the groovy-all jar into Felix. It has a script engine GroovyScriptEngineFactory. Sling will use it to run groovy scripts. This is in fact what the AEM Groovy Console is doing. Check out their app/pom.xml file. There they are embedding groovy-all, version 2.4.15.
Sling also has support for other templating engines. You know the usual, JSP & HTL. It can also support Thymeleaf, FreeMaker, and Groovy GString.
So even though Sling supports Groovy scripts and templates, AEM does not ship with it. This is how Sling gets its groove back.
Deploying The Groovy 3.0 Runtime
As I mentioned, the Groovy Console ships with Groovy 2.4. That version has become 2.5 and 3.0. Version 4.0 is still under development. So 3.0.9 is the latest stable release as of January 2022.
Unfortunately, it is not as simple to deploy 3.0 as it was in 2.4. As of 2.5, there is no groovy-all bundle anymore. Only a groovy-all POM that has references to the sub-project bundles. We will need to deploy the Groovy core bundle, its fragments, and dependencies as needed. I am using an AEMaaCS archetype project for the following examples.
Update the all/pom.xml file. Add the following to the <dependencies> section
And embed the following bundles
That's it. At least for now. I am only embedding the JSON and Templates modules. If you need anything else you will need to embed that along with its dependencies.
Groovy can be compiled or interpreted at runtime. We will use the Groovy Maven Plugin to compile code in the core bundle. Add this plugin configuration to your core/pom.xml.
Also, since you will be writing Groovy, you'll need to add a dependency in the same core/pom.xml.
The groovy-eclipse-batch version tracks the version of Groovy we are using. To resolve that, you will need to add the following to your root pom.xml
And that's it. You are ready to start writing services, models, servlets, and filters as you would with Java. Create a new source folder in the core bundle core/src/main/groovy. Then create a new package com.mysite.core.groovy with a package-info.groovy file so it gets exported
and a Sling Servlet, using OSGi R7 Annotations. Here is a servlet I slapped together to search for unreferenced assets
Of course one of the biggest features of Groovy is its ability to compile at runtime. We will need a script engine & factory. Groovy already has a JSR-223 implementation. The GroovyScriptEngineFactory worked well in 2.4. It no longer behaves well in 2.5 and 3.0. Most likely because the class loader it is using can't find classes in the fragment bundles.
Instead of using that, here is my implementation (in Groovy)
You can verify that it loaded correctly by checking the Script Engines console. Now, whenever Sling encounters a script it needs to execute it will get a script engine from the factory. The script engine will compile it using GroovyShell into a runnable Script that it then runs.
⚠️ My script engine and factory implementations are very simple. Here are other examples of script engines out in the wild you can reference should you run into issues.
To test it out, go to CRX/DE and create a new file at /apps/sling/OrderedFolder/unref-assets-script.json.GET.groovy. It will do the same thing as the compiled servlet.
Like Groovy scripts, templates require a script engine & factory. And both are executed at runtime. The difference is that a script is a program that is compiled and executed. A template has variables and scriptlets that are expanded and replaced to render the final output.
Take a look at the Groovy Scripting Support bundle we deployed. It has a script engine that will use the GStringTemplateEngine. To test it, create a new file at /apps/sling/OrderedFolder/unref-assets-template.json.GET.gst.
Compilers, scripts, and templates. Oh my! In this post, I am not trying to say you should switch to Groovy in your AEM project. There are a lot of pros and cons you'll need to think about depending on your project and team. Instead, I am showing you how you could do it if you wanted to.
Out of the 3 ways you could use Groovy, compiling is the most useful. In fact, I plan to push for it on my next greenfield project. There may be some pushback. There is always some pushback 🤨
Scripting may have its applications. My only concern is the script engine & factory may need some tweaking. And I may need to work out questions that come up i.e. can you reference another script?
Templating on the other hand... is pretty useless with the GString Template Engine. It is pretty much like JSP. Worse. I can't find any real documentation or examples. It seems its intention is for simple applications i.e. email templates. Groovy does have other template engines like the MarkupTemplateEngine. I would challenge you to create a script factory & engine to hook it up.