Use Case
Suppose you want to display the Date column from the Document Index live table as time ago. So instead of showing "2015/07/17 15:44" you would like to display "2 days ago". Of course, you can do this from the server side, but for the purpose of this tutorial we will achieve this using a JavaScript library called Moment.js. It can parse, validate, manipulate, and display dates from JavaScript.
Integration Options
There are several ways we can integrate Moment.js in XWiki:
- copy moment.js somewhere in /resources
$xwiki.jsfx.use('path/to/moment.js')
- you need file system access
- it leads to a custom XWiki WAR and thus upgrade complexity
- Extension Manager doesn't support installing resources in the WAR
- attach moment.js to a wiki page
<script src="$xwiki.getAttachmentURL('Demo.MomentJS', 'moment.js')"
type="text/javascript"></script>
- installable as XAR extension but moment.js code is included in the extension sources
- can slow/break the blame view on GitHub
- copy moment.js in a JSX object
$xwiki.jsx.use('Demo.MomentJS')
- the library code is still included in the extension sources
- when you upgrade the library version you need to ask your users to clear the browser cache or you need to put the library version in the document name
$xwiki.jsx.use('Demo.MomentJSv2_10_3')
- but then you need to update your code too which is bad because the dependency version should be part of the configuration.
- Load moment.js from CDN
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment.js"
type="text/javascript"></script>
- the library code is not included in the extension sources any more
- but the version is still specified in the code
- and XWiki might be behind a Proxy/Firewall with limited internet access
- Deploy moment.js as a WebJar and load it using RequireJS and the WebJar Script Service.
What is a WebJar?
- A JAR (Java Archive) file that packages client-side web libraries
- It can contain any resource file that is usable from the client side: JavaScript, CSS, HTML, client-side templates (e.g. Mustache, Handlebars), JSON, etc.
- Check www.webjars.org for more information and the list of available WebJars you can use
- Most WebJar are published on Maven Central so you can integrate them in your Maven build
- All resource paths must follow this convention:
- META-INF/resources/webjars/${name}/${version}
- META-INF/resources/webjars/jquery/1.11.1/jquery.js
- META-INF/resources/webjars/jstree/3.0.8/themes/default/style.css
How can we use WebJars
- Deployed like a normal JAR inside WEB-INF/lib or through Extension Manager
- Maven Project Dependency
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jstree</artifactId>
<version>3.0.8</version>
<scope>runtime</scope>
</dependency>
- Script Service
<script href="$services.webjars.url('momentjs', 'min/moment.js')"
type="text/javascript" />
Why should we use WebJars?
- Installable with Extension Manager
- Explicit & Transitive Dependencies
- Library code is not included in your sources
- Versioning and Cache
- The library version is not specified in your source code
- But it is part of the resource URL so there's no need to clear the browser cache after an upgrade
http://<server>/xwiki/webjars/momentjs/2.10.3/min/moment.min.js
- Both minified and non-minified resources are usually available
- You can debug using the non-minified version
Still, adding the script tag manually is not nice. We have RequireJS for this though.
What is RequireJS?
- RequireJS is a JavaScript file and module loader
- You can organize your code in modules that declare explicitly their dependencies
- Modules are loaded / imported asynchronously, with all their transitive dependencies
- This is called *Asynchronous Module Definition* (ADM)
- Modules can export (publish) APIs (e.g. an object or a function) which are "injected" in your code
How can we use RequireJS?
- Define a new module
define('climb-mountain', ['boots', 'backpack', 'poles'], function(boots, $bp, poles) {
// Prepare the climb tools.
/* ... */
// Export the API
return function(mountainName) {
// Climb the specified mountain.
};
});
- Use existing modules
require.config({
paths: {
moment: [
'//cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment.min',
"$!services.webjars.url('momentjs', 'min/moment.min')"
],
'climb-mountain': '$xwiki.getURL("Fun.ClimbMountain", "jsx", "language=$xcontext.language")'
}
});
require(['jquery', 'moment', 'climb-mountain'], function($, moment, climb) {
climb('Mont Blanc');
});
- Examples from the FAQ:
Why should we use RequireJS?
- Clear declaration of dependencies and avoids the use of globals
- Module identifiers can be mapped to different paths which allows swapping out implementation
- This is great for creating mocks for unit testing
- Encapsulates the module definition. Gives you the tools to avoid polluting the global namespace.
- The JavaScript code becomes more modularized
- We can use different versions of a lib at the same time
Time Ago LiveTable Date: First Version
Using a JSX:
require.config({
paths: {
moment: "$services.webjars.url('momentjs', 'min/moment.min')"
}
});
require(['jquery', 'moment', 'xwiki-events-bridge'], function($, moment) {
$(document).on('xwiki:livetable:newrow', function(event, data) {
var dateString = data.data['doc_date'];
var timeAgo = moment(dateString, "YYYY/MM/DD HH:mm").fromNow();
$(data.row).find('td.doc_date').html(timeAgo);
};
});
Time Ago LiveTable Date: Second Version
Let's make it more generic:
define(['jquery', 'moment', 'xwiki-events-bridge'], function($, moment) {
return function(column, liveTableId, dateFormat) {
column = column || 'doc.date';
column = column.replace(/^doc\./, 'doc_');
dateFormat = dateFormat || 'YYYY/MM/DD HH:mm';
var eventName = 'xwiki:livetable:newrow';
if (liveTableId) {
eventName = 'xwiki:livetable:' + liveTableId + ':newrow';
}
$(document).on(eventName, function(event, data) {
var dateString = data.data[column];
var timeAgo = moment(dateString, dateFormat).fromNow();
$(data.row).find('td.' + column).html(timeAgo);
});
};
});
How can we package WebJars?
Unfortunately there's no dedicated/integrated Maven plugin for packaging a WebJar so we need to mix a couple of standard Maven plugins:
- We put the resources in src/main/resources as expected for a Maven project
- src/main/resources/livetable-timeago.js
- Copy the WebJar resources to the right path before packing the jar
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-webjar-resources</id>
<phase>validate</phase>
<goals>
<goal>resources</goal>
</goals>
<configuration>
<!-- Follow the specifications regarding the WebJar content path. -->
<outputDirectory>
${project.build.outputDirectory}/META-INF/resources/webjars/${project.artifactId}/${project.version}
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
- Package the WebJar resources as a JAR
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<includes>
<!-- Include only the WebJar content -->
<include>META-INF/**</include>
</includes>
</configuration>
</plugin>
Why should we package WebJars?
- See Why should we use WebJars?
- Group resources by functionality
- State dependencies clearly
- Apply quality tools
- Static code verification (JSHint)
- Unit and integration tests (Jasmine)
- Minification (YUI Compressor)
Let's add some quality tools.
What is JSHint?
JSHint is a tool that helps to detect errors and potential problems in your JavaScript code.
[ERROR] 3,18: This function has too many parameters. (4)
[ERROR] 6,50: Missing semicolon.
[ERROR] 11,18: Blocks are nested too deeply. (3)
[ERROR] 16,5: 'foo' is not defined.
How can we use JSHint?
<plugin>
<groupId>com.cj.jshintmojo</groupId>
<artifactId>jshint-maven-plugin</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<goals>
<goal>lint</goal>
</goals>
</execution>
</executions>
<configuration>
<globals>require,define,document</globals>
<!-- See http://jshint.com/docs/options/ -->
<options>maxparams:3,maxdepth:2,eqeqeq,undef,unused,immed,latedef,noarg,noempty,nonew</options>
<directories>
<directory>src/main/resources</directory>
</directories>
</configuration>
</plugin>
What is Jasmine?
- Jasmine is a DOM-less simple JavaScript testing framework
- It does not rely on browsers, DOM, or any JavaScript framework
- It has a Maven plugin
- Not as nice as Mockito on Java but still very useful
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
How can we use Jasmine?
Let's add an unit test in src/test/javascript/livetable-timeago.js:
// Mock module dependencies.
var $ = jasmine.createSpy('$');
define('jquery', [], function() {
return $;
});
var moment = jasmine.createSpy('moment');
define('moment', [], function() {
return moment;
});
define('xwiki-events-bridge', [], {});
// Unit tests
define(['livetable-timeago'], function(timeAgo) {
describe('Live Table Time Ago module', function() {
it('Change date to time ago using defaults', function() {
// Setup mocks.
var $doc = jasmine.createSpyObj('$doc', ['on']);
var $row = jasmine.createSpyObj('$row', ['find']);
var $cell = jasmine.createSpyObj('$cell', ['html']);
var eventData = {
data: {doc_date: '2015/07/19 12:35'},
row: {}
};
$.andCallFake(function(selector) {
if (selector === document) {
return $doc;
} else if (selector === eventData.row) {
return $row;
} else if (selector === 'td.doc_date') {
return $cell;
}
});
$doc.on.andCallFake(function(eventName, listener) {
eventName == 'xwiki:livetable:newrow' && listener(null, eventData);
});
$row.find.andCallFake(function(selector) {
if (selector === 'td.doc_date') {
return $cell;
}
});
var momentObj = jasmine.createSpyObj('momentObj', ['fromNow']);
moment.andCallFake(function(dateString, dateFormat) {
if (dateString === eventData.data.doc_date && dateFormat === 'YYYY/MM/DD HH:mm') {
return momentObj;
}
});
var timeAgoDate = '1 day ago';
momentObj.fromNow.andReturn(timeAgoDate);
// Run the operation.
timeAgo();
// Verify the results.
expect($cell.html).toHaveBeenCalledWith(timeAgoDate);
});
});
});
We use the Jasmine Maven plugin to run the tests:
<plugin>
<groupId>com.github.searls</groupId>
<artifactId>jasmine-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<specRunnerTemplate>REQUIRE_JS</specRunnerTemplate>
<preloadSources>
<source>webjars/require.js</source>
</preloadSources>
<jsSrcDir>${project.basedir}/src/main/resources</jsSrcDir>
<timeout>10</timeout>
</configuration>
</plugin>
Failed to execute the [velocity] macro. Cause: [The execution of the [velocity] script macro is not allowed in [xwiki:Documentation.DevGuide.FrontendResources.IntegratingJavaScriptLibraries.WebHome]. Check the rights of its last author or the parameters if it's rendered from another script.]. Click on this message for details.
org.xwiki.rendering.macro.MacroExecutionException: The execution of the [velocity] script macro is not allowed in [xwiki:Documentation.DevGuide.FrontendResources.IntegratingJavaScriptLibraries.WebHome]. Check the rights of its last author or the parameters if it's rendered from another script.
at org.xwiki.rendering.macro.script.AbstractScriptMacro.execute(AbstractScriptMacro.java:178)
at org.xwiki.rendering.macro.script.AbstractScriptMacro.execute(AbstractScriptMacro.java:58)
at org.xwiki.rendering.internal.transformation.macro.MacroTransformation.transform(MacroTransformation.java:311)
at org.xwiki.rendering.internal.transformation.DefaultRenderingContext.transformInContext(DefaultRenderingContext.java:183)
at org.xwiki.rendering.internal.transformation.DefaultTransformationManager.performTransformations(DefaultTransformationManager.java:88)
at org.xwiki.display.internal.DocumentContentAsyncExecutor.executeInCurrentExecutionContext(DocumentContentAsyncExecutor.java:396)
at org.xwiki.display.internal.DocumentContentAsyncExecutor.execute(DocumentContentAsyncExecutor.java:269)
at org.xwiki.display.internal.DocumentContentAsyncRenderer.execute(DocumentContentAsyncRenderer.java:112)
at org.xwiki.rendering.async.internal.block.AbstractBlockAsyncRenderer.render(AbstractBlockAsyncRenderer.java:157)
at org.xwiki.rendering.async.internal.block.AbstractBlockAsyncRenderer.render(AbstractBlockAsyncRenderer.java:54)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.syncRender(DefaultAsyncRendererExecutor.java:290)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.render(DefaultAsyncRendererExecutor.java:267)
at org.xwiki.rendering.async.internal.block.DefaultBlockAsyncRendererExecutor.execute(DefaultBlockAsyncRendererExecutor.java:125)
at org.xwiki.display.internal.DocumentContentDisplayer.display(DocumentContentDisplayer.java:67)
at org.xwiki.display.internal.DocumentContentDisplayer.display(DocumentContentDisplayer.java:43)
at org.xwiki.display.internal.DefaultDocumentDisplayer.display(DefaultDocumentDisplayer.java:96)
at org.xwiki.display.internal.DefaultDocumentDisplayer.display(DefaultDocumentDisplayer.java:39)
at org.xwiki.sheet.internal.SheetDocumentDisplayer.display(SheetDocumentDisplayer.java:123)
at org.xwiki.sheet.internal.SheetDocumentDisplayer.display(SheetDocumentDisplayer.java:52)
at org.xwiki.display.internal.ConfiguredDocumentDisplayer.display(ConfiguredDocumentDisplayer.java:68)
at org.xwiki.display.internal.ConfiguredDocumentDisplayer.display(ConfiguredDocumentDisplayer.java:42)
at com.xpn.xwiki.doc.XWikiDocument.display(XWikiDocument.java:1388)
at com.xpn.xwiki.doc.XWikiDocument.getRenderedContent(XWikiDocument.java:1524)
at com.xpn.xwiki.doc.XWikiDocument.displayDocument(XWikiDocument.java:1474)
at com.xpn.xwiki.doc.XWikiDocument.displayDocument(XWikiDocument.java:1443)
at com.xpn.xwiki.api.Document.displayDocument(Document.java:788)
at jdk.internal.reflect.GeneratedMethodAccessor479.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.doInvoke(UberspectImpl.java:571)
at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.invoke(UberspectImpl.java:554)
at org.apache.velocity.runtime.parser.node.ASTMethod.execute(ASTMethod.java:221)
at org.apache.velocity.runtime.parser.node.ASTReference.execute(ASTReference.java:368)
at org.apache.velocity.runtime.parser.node.ASTReference.value(ASTReference.java:704)
at org.apache.velocity.runtime.parser.node.ASTExpression.value(ASTExpression.java:75)
at org.apache.velocity.runtime.parser.node.ASTSetDirective.render(ASTSetDirective.java:242)
at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:147)
at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:439)
at org.apache.velocity.runtime.parser.node.ASTIfStatement.render(ASTIfStatement.java:190)
at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:147)
at org.xwiki.velocity.internal.directive.TryCatchDirective.render(TryCatchDirective.java:86)
at org.apache.velocity.runtime.parser.node.ASTDirective.render(ASTDirective.java:304)
at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:439)
at org.apache.velocity.Template.merge(Template.java:358)
at org.apache.velocity.Template.merge(Template.java:262)
at org.xwiki.velocity.internal.InternalVelocityEngine.evaluate(InternalVelocityEngine.java:225)
at com.xpn.xwiki.internal.template.VelocityTemplateEvaluator.evaluateContent(VelocityTemplateEvaluator.java:105)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.evaluateContent(TemplateAsyncRenderer.java:219)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.renderVelocity(TemplateAsyncRenderer.java:174)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.render(TemplateAsyncRenderer.java:135)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.render(TemplateAsyncRenderer.java:54)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.lambda$syncRender$0(DefaultAsyncRendererExecutor.java:284)
at com.xpn.xwiki.internal.security.authorization.DefaultAuthorExecutor.call(DefaultAuthorExecutor.java:98)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.syncRender(DefaultAsyncRendererExecutor.java:284)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.render(DefaultAsyncRendererExecutor.java:267)
at org.xwiki.rendering.async.internal.block.DefaultBlockAsyncRendererExecutor.render(DefaultBlockAsyncRendererExecutor.java:154)
at com.xpn.xwiki.internal.template.InternalTemplateManager.render(InternalTemplateManager.java:904)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderFromSkin(InternalTemplateManager.java:866)
at com.xpn.xwiki.internal.template.InternalTemplateManager.render(InternalTemplateManager.java:853)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderNoException(InternalTemplateManager.java:808)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderNoException(InternalTemplateManager.java:800)
at com.xpn.xwiki.internal.template.DefaultTemplateManager.renderNoException(DefaultTemplateManager.java:79)
at com.xpn.xwiki.internal.template.DefaultTemplateManager.renderNoException(DefaultTemplateManager.java:73)
at org.xwiki.template.script.TemplateScriptService.render(TemplateScriptService.java:54)
at jdk.internal.reflect.GeneratedMethodAccessor279.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.doInvoke(UberspectImpl.java:571)
at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.invoke(UberspectImpl.java:554)
at org.apache.velocity.runtime.parser.node.ASTMethod.execute(ASTMethod.java:221)
at org.apache.velocity.runtime.parser.node.ASTReference.execute(ASTReference.java:368)
at org.apache.velocity.runtime.parser.node.ASTReference.render(ASTReference.java:492)
at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:147)
at org.apache.velocity.runtime.directive.VelocimacroProxy.render(VelocimacroProxy.java:218)
at org.apache.velocity.runtime.directive.RuntimeMacro.render(RuntimeMacro.java:331)
at org.apache.velocity.runtime.directive.RuntimeMacro.render(RuntimeMacro.java:261)
at org.apache.velocity.runtime.parser.node.ASTDirective.render(ASTDirective.java:304)
at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:439)
at org.apache.velocity.Template.merge(Template.java:358)
at org.apache.velocity.Template.merge(Template.java:262)
at org.xwiki.velocity.internal.InternalVelocityEngine.evaluate(InternalVelocityEngine.java:225)
at com.xpn.xwiki.internal.template.VelocityTemplateEvaluator.evaluateContent(VelocityTemplateEvaluator.java:105)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.evaluateContent(TemplateAsyncRenderer.java:219)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.renderVelocity(TemplateAsyncRenderer.java:174)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.render(TemplateAsyncRenderer.java:135)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.render(TemplateAsyncRenderer.java:54)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.lambda$syncRender$0(DefaultAsyncRendererExecutor.java:284)
at com.xpn.xwiki.internal.security.authorization.DefaultAuthorExecutor.call(DefaultAuthorExecutor.java:98)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.syncRender(DefaultAsyncRendererExecutor.java:284)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.render(DefaultAsyncRendererExecutor.java:267)
at org.xwiki.rendering.async.internal.block.DefaultBlockAsyncRendererExecutor.render(DefaultBlockAsyncRendererExecutor.java:154)
at com.xpn.xwiki.internal.template.InternalTemplateManager.render(InternalTemplateManager.java:904)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderFromSkin(InternalTemplateManager.java:866)
at com.xpn.xwiki.internal.template.InternalTemplateManager.render(InternalTemplateManager.java:853)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderNoException(InternalTemplateManager.java:808)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderNoException(InternalTemplateManager.java:800)
at com.xpn.xwiki.internal.template.DefaultTemplateManager.renderNoException(DefaultTemplateManager.java:79)
at com.xpn.xwiki.internal.template.DefaultTemplateManager.renderNoException(DefaultTemplateManager.java:73)
at org.xwiki.template.script.TemplateScriptService.render(TemplateScriptService.java:54)
at jdk.internal.reflect.GeneratedMethodAccessor279.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.doInvoke(UberspectImpl.java:571)
at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.invoke(UberspectImpl.java:554)
at org.apache.velocity.runtime.parser.node.ASTMethod.execute(ASTMethod.java:221)
at org.apache.velocity.runtime.parser.node.ASTReference.execute(ASTReference.java:368)
at org.apache.velocity.runtime.parser.node.ASTReference.render(ASTReference.java:492)
at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:147)
at org.apache.velocity.runtime.directive.VelocimacroProxy.render(VelocimacroProxy.java:218)
at org.apache.velocity.runtime.directive.RuntimeMacro.render(RuntimeMacro.java:331)
at org.apache.velocity.runtime.directive.RuntimeMacro.render(RuntimeMacro.java:261)
at org.apache.velocity.runtime.parser.node.ASTDirective.render(ASTDirective.java:304)
at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:147)
at org.apache.velocity.runtime.parser.node.ASTIfStatement.render(ASTIfStatement.java:171)
at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:147)
at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:439)
at org.apache.velocity.runtime.parser.node.ASTIfStatement.render(ASTIfStatement.java:190)
at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:439)
at org.apache.velocity.Template.merge(Template.java:358)
at org.apache.velocity.Template.merge(Template.java:262)
at org.xwiki.velocity.internal.InternalVelocityEngine.evaluate(InternalVelocityEngine.java:225)
at com.xpn.xwiki.internal.template.VelocityTemplateEvaluator.evaluateContent(VelocityTemplateEvaluator.java:105)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.evaluateContent(TemplateAsyncRenderer.java:219)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.renderVelocity(TemplateAsyncRenderer.java:174)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.render(TemplateAsyncRenderer.java:135)
at com.xpn.xwiki.internal.template.TemplateAsyncRenderer.render(TemplateAsyncRenderer.java:54)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.lambda$syncRender$0(DefaultAsyncRendererExecutor.java:284)
at com.xpn.xwiki.internal.security.authorization.DefaultAuthorExecutor.call(DefaultAuthorExecutor.java:98)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.syncRender(DefaultAsyncRendererExecutor.java:284)
at org.xwiki.rendering.async.internal.DefaultAsyncRendererExecutor.render(DefaultAsyncRendererExecutor.java:267)
at org.xwiki.rendering.async.internal.block.DefaultBlockAsyncRendererExecutor.render(DefaultBlockAsyncRendererExecutor.java:154)
at com.xpn.xwiki.internal.template.InternalTemplateManager.render(InternalTemplateManager.java:904)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderFromSkin(InternalTemplateManager.java:866)
at com.xpn.xwiki.internal.template.InternalTemplateManager.renderFromSkin(InternalTemplateManager.java:846)
at com.xpn.xwiki.internal.template.InternalTemplateManager.render(InternalTemplateManager.java:832)
at com.xpn.xwiki.internal.template.DefaultTemplateManager.render(DefaultTemplateManager.java:91)
at com.xpn.xwiki.internal.template.DefaultTemplateManager.render(DefaultTemplateManager.java:85)
at com.xpn.xwiki.XWiki.evaluateTemplate(XWiki.java:2564)
at com.xpn.xwiki.web.Utils.parseTemplate(Utils.java:180)
at com.xpn.xwiki.web.XWikiAction.execute(XWikiAction.java:651)
at com.xpn.xwiki.web.XWikiAction.execute(XWikiAction.java:339)
at com.xpn.xwiki.web.LegacyActionServlet.service(LegacyActionServlet.java:108)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at com.xpn.xwiki.web.ActionFilter.doFilter(ActionFilter.java:122)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.xwiki.wysiwyg.filter.ConversionFilter.doFilter(ConversionFilter.java:61)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.xwiki.container.servlet.filters.internal.SetHTTPHeaderFilter.doFilter(SetHTTPHeaderFilter.java:63)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.xwiki.resource.servlet.RoutingFilter.doFilter(RoutingFilter.java:132)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.xwiki.container.servlet.filters.internal.SavedRequestRestorerFilter.doFilter(SavedRequestRestorerFilter.java:208)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.xwiki.container.servlet.filters.internal.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:354)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:433)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:840)