As of release 0.9.0 mustache.java is now Java 8 only. For Java 6/7 support use 0.8.x.
There are no external dependencies and the compiler library is 95k.
Mustache.java is a derivative of mustache.js.
There is a Google Group for support and questions: http://groups.google.com/group/mustachejava
Travis CI: https://travis-ci.org/spullara/mustache.java
Largest production deployment of Mustache.java:
- Twitter (the web site, email, syndicated widgets, etc)
Thanks to YourKit for many performance improvements:
YourKit is kindly supporting the mustache.java open source project with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products:
Request for contributions:
- Real world benchmarks that matter - currently benchmarking based on Twitter templates
- Documentation
- Bug reports / fixes
- API feedback
- Optimizations
Documentation:
- Javadocs
- Mustache.js manual
- Passes all of the
mustachespecification tests modulo whitespace differences - Biggest difference between mustache.js and mustache.java is optional concurrent evaluation
- Data is provided by objects in an array of scopes and are accessed via non-private fields, methods or maps
- Any
Iterablecan be used for list-like behaviors - Returning a
Callableallows for concurrent evaluation if anExecutorServiceis configured - Template inheritance is supported by this implementation, see mustache/spec#38 (eg.
{{<super}}{{$content}}...{{/content}}{{/super}}) - Additional functions/lambdas (eg.
{{#func1}}...{{/func1}}) are implemented usingFunctionfrom Java 8 (post-substitution) - Use
TemplateFunctionif you want mustache.java to reparse the results of your function/lambda (pre-substitution) - Both default and manually configured classpath based and file system based template roots are supported
- A compiled and invokedynamic version is available. Performance improvements are often application specific.
- The
handlebarserver will render templates + json data for quick mockups of templates by designers - Completely pluggable system for overriding almost all the behavior in the compilation and rendering process
- You can pull out sample data from live systems using the
CapturingMustacheVisitorfor mocks and tests - The DecoratedCollection can provide first / last / index for elements in a collection
- The
invertcall can take text and a template and solve for the data
Performance:
- See the
com.github.mustachejavabenchmarkspackage in thecompilermodule - Compiles 4000+ timeline.html templates per second per core
- Renders 3000+ of 50 tweet timelines per second per core on 2011 Macbook Pro / MacPro hardware
- New codegen module generates code for guards and mustaches
- The
indymodule uses the codegen module and invokedynamic to compile templates down to bytecode
Build suggestions:
- Don't build, use Maven dependencies
- If you must build but not test:
- git clone https://github.com/spullara/mustache.java.git
- set your JAVA_HOME to a JDK 8 JDK
- mvn -DskipTests clean install
- If you must build and test but not benchmark:
- CI=1 mvn clean install -pl :compiler -am
- If you must build, test and benchmark:
- mvn clean install
Maven dependency information (ie. for most common cases you will just need the compiler module):
Java 8+:
<dependency> <groupId>com.github.spullara.mustache.java</groupId> <artifactId>compiler</artifactId> <version>0.9.2</version> </dependency>
Java 6/7:
<dependency> <groupId>com.github.spullara.mustache.java</groupId> <artifactId>compiler</artifactId> <version>0.8.18</version> </dependency>
Example template file:
{{#items}}
Name: {{name}}
Price: {{price}}
{{#features}}
Feature: {{description}}
{{/features}}
{{/items}}
Might be powered by some backing code:
public class Context { List<Item> items() { return Arrays.asList( new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))), new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly."))) ); } static class Item { Item(String name, String price, List<Feature> features) { this.name = name; this.price = price; this.features = features; } String name, price; List<Feature> features; } static class Feature { Feature(String description) { this.description = description; } String description; } }
And would result in:
Name: Item 1
Price: $19.99
Feature: New!
Feature: Awesome!
Name: Item 2
Price: $29.99
Feature: Old.
Feature: Ugly.
Evaluation of the template proceeds serially. For instance, if you have blocking code within one of your callbacks, the system will pause while executing them:
static class Feature { Feature(String description) { this.description = description; } String description() throws InterruptedException { Thread.sleep(1000); return description; } }
If you change description to return a Callable instead it will automatically be executed in a separate
thread if you have provided an ExecutorService when you created your MustacheFactory.
Callable<String> description() throws InterruptedException { return new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(1000); return description; } }; }
This enables scheduled tasks, streaming behavior and asynchronous i/o. Check out the example module in order
to see a complete end-to-end example:
package mustachejava; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.util.Arrays; import java.util.List; public class Example { List<Item> items() { return Arrays.asList( new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))), new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly."))) ); } static class Item { Item(String name, String price, List<Feature> features) { this.name = name; this.price = price; this.features = features; } String name, price; List<Feature> features; } static class Feature { Feature(String description) { this.description = description; } String description; } public static void main(String[] args) throws IOException { MustacheFactory mf = new DefaultMustacheFactory(); Mustache mustache = mf.compile("template.mustache"); mustache.execute(new PrintWriter(System.out), new Example()).flush(); } }
An alternative approach for providing variables would be to use a Map object, like:
public static void main(String[] args) throws IOException { HashMap<String, Object> scopes = new HashMap<String, Object>(); scopes.put("name", "Mustache"); scopes.put("feature", new Feature("Perfect!")); Writer writer = new OutputStreamWriter(System.out); MustacheFactory mf = new DefaultMustacheFactory(); Mustache mustache = mf.compile(new StringReader("{{name}}, {{feature.description}}!"), "example"); mustache.execute(writer, scopes); writer.flush(); }