Configuring custom jexl java expressions
Overview
JEXL is an expression language in Java. Grouper evaluates JEXL expressions in many places (provisioning translations, the loader, the custom UI, rules, and more).
Depending on the API and what the JEXL is used for, the evaluation may allow references to static methods on Java classes you deploy. For example, the provisioning framework translations allow static class references. (The legacy PSPNG provisioner also allowed this; PSPNG was removed in v6.)
To call your own Java from a JEXL expression you compile a class with a public static method, deploy it as a jar in the Grouper webapp, and reference it by its fully qualified package.Class.method(...) name.
Applies to v4+. Two preconditions:
The calling API must allow static classes (the
allowStaticClassesflag on the EL evaluation) — not every place that evaluates JEXL does.Privileges: deploying a jar requires write access to the Grouper application server / container filesystem (the webapp
WEB-INF/lib) and the ability to restart Grouper — i.e. a Grouper installer/administrator, not an end user.
Compile the class
First compile a class. You can compile it outside Grouper, check out the Grouper code, add a Maven dependency, or compile in the container — whatever is convenient.
This example uses Grouper libraries, so add a simple Maven dependency (for example, in Eclipse). Set the <version> to the Grouper version you run.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>sampleJexl</groupId>
<artifactId>sampleJexl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/edu.internet2.middleware.grouper/grouper -->
<dependency>
<groupId>edu.internet2.middleware.grouper</groupId>
<artifactId>grouper</artifactId>
<version>2.5.35</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>Make a class with a public static method:
package sampleJexl;
import edu.internet2.middleware.grouper.util.GrouperUtil;
public class SampleClass {
public static void main(String[] args) {
}
public static String tableauADGroupName(String groupName) {
/* Strip contextual stuff */
String tableauName = groupName.replace("app:tableau:org:","")
.replaceAll(":roles:",":")
.replaceAll(":protected:",":")
.replaceAll(":projects:",":");
/*
convert some:prefix:blah:blah_suffix
to just some:prefix:blah_suffix
That is squeeze out the last folder before the group.
*/
/* by indexes */
/*
int lastColon = tableauName.lastIndexOf(":");
int prevColon = tableauName.lastIndexOf(":",lastColon - 1);
tableauName = tableauName.substring(0,prevColon) + tableauName.substring(lastColon);
*/
/* With grouper util methods */
String parentFolder = GrouperUtil.parentStemNameFromName(tableauName);
String grandParentFolder = GrouperUtil.parentStemNameFromName(parentFolder);
String groupSuffix = GrouperUtil.suffixAfterChar(tableauName, ':');
tableauName = grandParentFolder + ":" + groupSuffix;
/* _ and : turn into - */
tableauName = tableauName.replaceAll("[_:]","-");
/* Take the last 48 chars, or the whole string,
so the full group name is <= 64 chars as required by AD
*/
int cutoff = Math.max(tableauName.length() - 48, 0);
tableauName = tableauName.substring(cutoff);
/* Add the prefix */
tableauName = "Grouper-Tableau-" + tableauName;
return tableauName;
}
}
Test the expression
To invoke a static method, use the fully qualified package, class, and method name. In this case the package is sampleJexl, the class is SampleClass, and the method is tableauADGroupName.
public static void main(String[] args) {
// Grouper-Tableau-Enterprise-HR-LaborRelations_project_leaders
String result = GrouperUtil.substituteExpressionLanguage(
"${sampleJexl.SampleClass.tableauADGroupName('app:tableau:org:Enterprise:HR:projects:LaborRelations:roles:LaborRelations_project_leaders')}",
null, true, false, false);
if (!"Grouper-Tableau-Enterprise-HR-LaborRelations-project-leaders".equals(result)) {
throw new RuntimeException(result);
}
System.out.println("Success");
}
Deploy the jar
Package the compiled class (and its source, if you like) into a jar. One simple approach is to copy the class file and source into a directory, make a zip, and rename it to .jar:
Add the jar to slashRoot/opt/grouper/grouperWebapp/WEB-INF/lib (or include it in a sub-image), and restart Grouper.