Content Management and the Apache Jelly engine
Summarize
Summary of Content Management and the Apache Jelly engine
Apache Jelly is a Java- and XML-based scripting engine used within ServiceNow to transform XML into executable code. It is primarily used to render dynamic UI elements such as forms, lists, and UI Pages. ServiceNow customers familiar with JavaScript, XML, XHTML, or HTML will find Jelly syntax approachable. Jelly scripts run effectively in dynamic content blocks but may encounter issues in static blocks.
Show less
Jelly code is structured with specific namespaces and tags that control execution phases. Understanding these phases and namespaces is key to writing effective Jelly scripts for content management.
Key Features
- Two-Phase Parsing: Jelly scripts use two processing phases, identified by prefixes
jandgfor phase one, andj2andg2for phase two. This allows complex rendering logic and content caching optimization. - Namespace Usage: The
jprefix marks native Jelly tags, whilegprefixes are ServiceNow platform-specific extensions. The second-phase tags (j2andg2) are processed after caching the first phase results. - Dynamic Script Execution: The
g:evaluatetag enables running server-side scripts inside Jelly, similar to business rules, allowing access to GlideRecord queries and other script includes. - Content Escaping: Output expressions use bracket notation with options to escape content for HTML, JavaScript, or other contexts, ensuring secure and proper rendering.
- Full XML Support: Jelly scripts support embedding normal XML without parsing, facilitating rich markup inside scripts.
Practical Usage for ServiceNow Customers
- Use the
<j:jelly>root tag with appropriate namespaces to write Jelly scripts for content blocks. - For dynamic content that requires server-side data, include
g:evaluatetags to run GlideRecord queries or call business logic. - Loop through GlideRecord results using
j:whileand conditionally display data withj:ifstatements. - Always close tags properly and use self-closing tags (e.g.,
<br />) for XHTML compliance. - Enable the Two phase option on content forms if using second-phase tags (
g2orj2) to ensure correct script processing. - Leverage content escaping options (e.g.,
${HTML:...}) when outputting variable data to prevent injection or formatting issues.
Expected Outcomes
By applying Apache Jelly scripting in ServiceNow content management, customers can create dynamic, data-driven UI content blocks that integrate server-side logic seamlessly. This enables tailored displays such as reports, lists, or customized pages that respond to real-time platform data. Proper use of Jelly's two-phase processing improves performance through caching and provides flexibility for complex rendering requirements.
Apache Jelly is a Java-based and XML-based scripting and processing engine for turning XML into executable code.
The Apache Jelly engine closely resembles XML and should be comfortable for developers familiar with JavaScript, XML, XHTML, or HTML. In the ServiceNow instance, the Apache Jelly engine renders items such as forms, lists, and UI Pages. Apache Jelly code renders well within a dynamic content block, but can have issues when used in static blocks. You can use Jelly tags, calls, and statements, but HTML acts just like XHTML.
<?xml version= "1.0" encoding= "utf-8" ?>
<j:jelly trim = "false" xmlns:j = "jelly:core" xmlns:g = "glide" xmlns:j2 = "null" xmlns:g2 = "null" >
<j:if test = "${current_page.getName()=='Solutions'}" >
<h1 class = "page_name" > <b> <a href = "solutions.do?" title = "${gs.getMessage('Solutions')}" >${gs.getMessage('Solutions')}</a> </b> </h1>
<p class = "page_description" >
${current_page.getDescription()}
</p> <br />
</j:if>
<j:if test = "${current_page.getName()=='IT 3.0'}" >
<h1 class = "page_name" > <b> <a href = "solutions.do?" title = "${gs.getMessage('Solutions')}" >${gs.getMessage('Solutions')}</a> </b> | ${current_page.getName()}</h1>
<p class = "page_description" >
${current_page.getDescription()}
</p> <br />
</j:if>
</j:jelly>Ensure that all tags are closed. If the tag is not a naturally closing tag, then place a
forward slash before the end bracket. For example, a <BR /> or an
<IMG src="cms.png" />.
Include the following tag with all Apache Jelly scripts.
<j:jelly trim= "false" xmlns:j= "jelly:core" xmlns:g= "glide" xmlns:j2= "null" xmlns:g2= "null" >- Apache Jelly script uses multiple namespaces.
- There are two types of prefixes in tags: j and g. The j prefix is used for tags that are natively part of Apache Jelly. The g prefix is used for tags that the ServiceNow platform created and is using for platform purposes.
The j2 and g2 prefixes are just like j and g, except that they are processed in a second phase. The Apache Jelly script parser runs through each j and g tag respectively. For example:
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<j:set var="jvar_phase1" value="Hello" />
<j2:set var="jvar_phase2" value="World" />
${jvar_phase1} $[jvar_phase2]
</j:jelly>In phase 1, the parser runs through all the j and g tags. It then caches the result. Before it runs the second phase, it takes the j and g namespaces and moves the namespaces to the second phase. It looks something like the following code.
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="jelly:core" xmlns:g2="glide">
<j2:set var="jvar_phase2" value="World" />
Hello $[jvar_phase2]
</j:jelly>Another example is to create a report of all open incidents assigned to each group. For this purpose, you could use a report and save time, but it is a good example for learning Jelly. Start with the Jelly tag:
<j:jelly trim= "false" xmlns:j= "jelly:core" xmlns:g= "glide" xmlns:j2= "null" xmlns:g2= "null" >
</j:jelly>First, you need a list of open incidents. Use a g2:evaluate tag. The
evaluate tag runs the script. Anything inside the tag is parsed like a business rule, so,
for example, you can call global business rules, script includes, and gliderecord.
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:evaluate var="jvar_groups" object="true">
var now_GR = new GlideRecord("sys_user_group");
gr.orderBy('name');
gr.query();
gr;
</g:evaluate>
</j:jelly>This script is in phase 1 because frequent changes to incident assignment groups are not
expected. Also notice the var attribute on the evaluate tag. This attribute
specifies what variable is set from this block. At the end of the script, there is a
gr on a line by itself. That last line is what sets the variable.
You can omit the jvar_groups variable, but then all the variables in the
evaluate tag become Apache Jelly variables. The object=true specifies that
the variable is not a primitive data type. If object=true is omitted, the
script would break because jvar_groups would only be able to hold items
like integers and strings.
After the evaluate tag, loop through these groups and find the incidents for each one.
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:evaluate var="jvar_groups" object="true">
var now_GR = new GlideRecord("sys_user_group");
gr.orderBy('name');
gr.query();
gr;
</g:evaluate>
<table>
<tr>
<th>Name</th>
<th>Incidents</th>
</tr>
<j:while test="${jvar_groups.next()}">
<tr>
<td>${HTML:jvar_groups.getValue('name')}</td>
<td></td>
</tr>
</j:while>
</table>
</j:jelly>j:while loop. It is a normal while loop and can iterate through a
GlideRecord object. Also notice that you output a value with
${HTML:jvar_groups.getValue('name')}. Here are the important elements:- The outer brackets,
${}, specify the output of the variable and the phase in which the variable is output:${}means first phase,$[]means second phase. - HTML before the expression is for escaping the output. The expression
jvar_groups.getValue('name')is being escaped for HTML. For other types of escaping, there are JS (Javascript), NS (No Script), and some other options.
To select only one record and not iterate through many records, the code looks like the following example:
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:evaluate var="jvar_groups" object="true">
var now_GR = new GlideRecord("sys_user_group");
gr.orderBy('name');
gr.query();
gr;
</g:evaluate>
<j:if test="${jvar_groups.next()}">
We found ${HTML:jvar_groups.getValue('name')}
</j:if>
</j:jelly>