Java 8 optimized, memory efficient, speedy template engine producing statically typed, plain java objects
Java 8 optimized, memory efficient, speedy template engine producing statically typed, plain java objects
The following Java versions and platforms are tested using GitHub workflows: The following platforms are tested using the Fizzed, Inc. build system:
Rocker is a Java 8+ optimized, near zero-copy rendering, speedy template engine that produces statically typed, plain java object templates that are compiled along with the rest of your project. No more "warm-up" time in production, slow reflection-based logic, or nasty surprises that should have been caught during development.
Write your templates using an intuitive, tagless syntax
with standard Java expressions for logic, iteration, and values. Use Rocker's
special ? presence operator for null-safe evaluation. All the heavy
lifting is done by the Rocker parser during development -- which keeps the runtime
dependencies down to just a handful of classes. Rocker will parse your templates
and generate well-documented Java source files (so you can easily inspect and
understand how it works).
Includes the following features:
? presence operator extends syntax for simplified handling of
null values.Project by Fizzed, Inc. (Follow on Twitter: @fizzed_inc)
Developing and maintaining opensource projects requires significant time. If you find this project useful or need commercial support, we'd love to chat. Drop us an email at [email protected]
Project sponsors may include the following benefits:
Based on the following template benchmark, Rocker is the clear winner. ~250% faster than Freemarker while also requiring orders-of-magnitude less memory.
Most templates are used for websites, so here is a quick sample showing how
Rocker templates work and can call each other during the rendering process.
Create a template containing a common header and footer as well as a placeholder
for body content. Create template src/main/java/views/main.rocker.html
@args (String title, RockerBody content)
@title
@content
The template we actually plan on showing to a user will render its content
within the context of the common/header footer. In Java terms, it's passing
a block of rendering code to be executed within another template. Create template
src/main/java/views/index.rocker.html
@args (String message)
@views.main.template("Home") -> {
Hello @message!
}
Hey, what about the RockerBody content argument? We cover it in more
detail in the syntax readme, but for now just understand that its
the only special type of argument and instructs Rocker that a template expects
a "body" to be passed to it.
The Rocker parser will generate a Java source file for each template. They
will be target/generated-sources/rocker/views/main.java and
target/generated-sources/rocker/views/index.java. In your application, you
can render the index template like so.
static public void main(String[] args) {
String output = views.index.template("World")
.render()
.toString();
}
The output will equal:
Home
Hello World!
Once you generate the Java sources and peek inside the code, it's simple
to see how this works. The views.index class creates a views.main template instance
and hands off rendering to it -- while also passing a block of itself that
it will render when views.main calls the @content variable. The syntax is
identical to how a lambda is defined in Java 8 (implemented with lambdas for Java 8
and anonymous inner classes for Java 6/7). Rocker does a number of things behind
the scenes to make sure templates that create other templates share the same
rendering context (output buffer, application-specific context/implicit state).
Checkout the SYNTAX.md file for a comprehensive deep dive on the rocker syntax.
Rocker has a growing list of frameworks that it has been seamlessly integrated with. If you want to link to a new framework added, please file an issue or submit a PR:
Static (plain text) for each Rocker template is (by default) stored internally as static byte arrays already converted into your target charset (e.g. UTF-8). When a template is rendered -- the static byte arrays are reused across all requests. Rocker renders to an optimized output stream that stores a composite (linked list) view of the reused byte arrays plus your dynamic content. Since templates consist mostly of static content rendered into the same charset over and over again, rather than allocating new memory, copying that content, and then converting it into your target charset for each request -- Rocker simply uses a pointer to it over and over again. This technique produces fast and memory efficient renders.
Let's say you have a template consisting of 9000 bytes of plain static text and 1000 bytes of dynamic content. Without this optimization, it would require ~100MB of memory to service 10000 requests (10000 bytes x 10000 requests). With this optimization, it would require ~10MB of memory to service 10000 requests (1000 bytes x 10000 requests). Besides lower memory, you also cut out 90MB of memory copies and 90MB of UTF-8 String->byte conversions. A pretty useful optimization.
Everything is compiled by your project's compiler along with your other Java source code. Any dynamic code in your template is ultimately converted into standard Java and compiled. No reflection used.
Version 0.10.0 introduced support for hot reloading templates during development. Hot reloading allows you to modify the template source code, save it, and have the changes active on the next request -- without having to restart your JVM. Rocker offers two different flavors of hot reloading for flexibility.
The major feature of Rocker templates is that your templates are compile-time checked for usage, arguments, logic, etc. by the Java compiler.
In version 0.10.0 the underlying structure of a template was modified where a template generates two underlying classes. Each template generates a model class (its interface) and an implementation class (its renderer). Your application will only interact directly with the model, therefore allowing Rocker to dynamically recompile and reload the implementation class.
The major benefit of flavor one is that your application code remains the same and is compile-time checked by the Java compiler, while the template content can be modified and automatically reloaded at runtime. Only in the case where you actually change the template arguments, will you need to restart your application.
If you prefer the convenience of fully dynamic templates, flavor two supports hot reloading of both the template model class (its interface) as well as the implementation class (its renderer). Your application will lose some of the compile-time checking and a small performance hit, but gain the convenience of everything being reloadable. The way your application will use templates is different as well.
import com.fizzed.rocker.Rocker
...
// dynamic interfaces, dynamic implementation
String rendered = Rocker.template("views/index.rocker.html")
.bind("val", "ValueA")
.render()
.toString();
The template path and arguments will be runtime-checked. Please note that each bindable value must match the name and type declared in your template.
In case your bindable map may contain more values that than the required ones a relaxed bind is available. The relaxed alternative will not fail rendering if an attribute is extra to the required list. For example:
@args (String name)
Hello ${name}!
Will render in relaxed mode as:
Map map = new HashMap();
map.put("name", "Joe");
map.put("age", 42);
Rocker.template("views/hello.rocker.html")
.relaxedBind(map)
.render();
// -> Hello Joe!
Support for hot reloading is added to your generated templates by default in version 0.10.0. If you'd like to disable support, set the configuration/system property rocker.optimize to true during your build. Since the code is present in your templates by default, you merely need to turn it on at runtime.
The rocker-compiler dependency needs to be added to your build. This dependency only needs to be present during development and can be removed in production. In Maven, this means you'll want to add the dependency in the provided scope.
com.fizzed
rocker-compiler
2.2.1
provided
Activate hot reloading at runtime. You can activate hot reloading either with a system property or programmatically. For activating hot reloading with a system property in maven.
mvn -Drocker.reloading=true ...rest of args...
Alternatively, you can activate hot reloading programmatically.
import com.fizzed.rocker.runtime.RockerRuntime
...
RockerRuntime.getInstance().setReloading(true);
There is a simple example demonstrating hot reload in action. This project uses Blaze to help script tasks. Run the following
java -jar blaze.jar hot_reload
Point your browser to http://localhost:8080
Then modify & save rocker-test-reload/src/test/java/views/index.rocker.html and refresh your browser.
Rocker consists of two components - the parser/generator and the runtime. To use Rocker in your project, add the runtime dependency to your application, then enable the parser in your build tool followed by creating your first template.
Rocker is published to Maven central. To add as a dependency in
No open issues yet, or sync has not completed.