or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-qute@3.30.x

docs

checked-templates.mdconfiguration.mdengine-advanced.mdindex.mdmessage-bundles.mdtemplate-basics.mdtemplate-extensions.mdtemplate-syntax.mdutilities.mdvalue-resolvers.md
tile.json

tessl/maven-io-quarkus--quarkus-qute

tessl install tessl/maven-io-quarkus--quarkus-qute@3.30.0

Offer templating support for web, email, etc in a build time, type-safe way

utilities.mddocs/

Qute Utility Classes

This document covers utility classes and helper types that simplify template usage, provide convenience methods, and enable advanced customization of Qute's behavior.

Qute Convenience Class

The Qute class provides quick and convenient access to a static engine instance and formatting methods for simple template rendering without explicit engine management.

package io.quarkus.qute;

/**
 * Provides quick access to a static engine instance and convenient formatting methods.
 * If no specific engine is set, a default engine is created lazily.
 */
public final class Qute {

    /**
     * Get or create the static engine instance.
     * Default engine includes:
     * - All default value resolvers and section helpers
     * - ReflectionValueResolver
     * - IndexedArgumentsParserHook
     * - HtmlEscaper for text/html and text/xml
     */
    public static Engine engine();

    /**
     * Set a specific engine instance for static access.
     * Clears the template cache when called.
     */
    public static void setEngine(Engine engine);

    /**
     * Format template with named data map.
     */
    public static String fmt(String template, Map<String, Object> data);

    /**
     * Format template with indexed data array.
     * Empty placeholders {} are replaced with {data[0]}, {data[1]}, etc.
     */
    public static String fmt(String template, Object... data);

    /**
     * Create a fluent Fmt builder for advanced formatting.
     */
    public static Fmt fmt(String template);

    /**
     * Enable template caching by default.
     */
    public static void enableCache();

    /**
     * Disable template caching by default.
     */
    public static void disableCache();

    /**
     * Clear the template cache.
     */
    public static void clearCache();
}

Basic Formatting

The simplest way to render templates is using Qute.fmt():

import io.quarkus.qute.Qute;
import java.util.Map;

// Indexed arguments (simple placeholders)
String result1 = Qute.fmt("Hello {}!", "World");
// => "Hello World!"

String result2 = Qute.fmt("Hello {} {}!", "John", "Doe");
// => "Hello John Doe!"

// Named arguments
String result3 = Qute.fmt("Hello {name}!", Map.of("name", "Alice"));
// => "Hello Alice!"

String result4 = Qute.fmt(
    "User: {username}, Email: {email}",
    Map.of("username", "bob", "email", "bob@example.com")
);
// => "User: bob, Email: bob@example.com"

Indexed Placeholder Syntax

Empty placeholders {} are automatically replaced with indexed array accessors:

// Template: "Hello {}!"
// Becomes: "Hello {data[0]}!"

Qute.fmt("Hello {}!", "World");  // Hello World!

// Template: "{} + {} = {}"
// Becomes: "{data[0]} + {data[1]} = {data[2]}"

Qute.fmt("{} + {} = {}", 2, 3, 5);  // 2 + 3 = 5

Fluent Fmt Builder

For advanced formatting with caching, content types, and attributes:

public static final class Fmt {

    /**
     * Enable template caching for this format operation.
     */
    public Fmt cache();

    /**
     * Disable template caching for this format operation.
     */
    public Fmt noCache();

    /**
     * Set content type (affects escaping behavior).
     */
    public Fmt contentType(String contentType);

    /**
     * Set template variant.
     */
    public Fmt variant(Variant variant);

    /**
     * Add template instance attribute.
     */
    public Fmt attribute(String key, Object value);

    /**
     * Set data as named map.
     */
    public Fmt dataMap(Map<String, Object> data);

    /**
     * Set data as indexed array.
     */
    public Fmt dataArray(Object... data);

    /**
     * Add single data item.
     */
    public Fmt data(String key, Object value);

    /**
     * Render the template.
     */
    public String render();
}

Fmt Builder Examples:

// HTML escaping
String html = Qute.fmt("<html>{header}</html>")
    .contentType("text/html")
    .data("header", "<h1>Title</h1>")
    .render();
// => "<html>&lt;h1&gt;Title&lt;/h1&gt;</html>"

// With caching enabled
String cached = Qute.fmt("Hello {name}!")
    .cache()
    .data("name", "World")
    .render();

// With attributes
String withAttrs = Qute.fmt("{msg:hello}")
    .attribute("locale", Locale.FRENCH)
    .render();

// Complex example
String complex = Qute.fmt("User: {user.name}, Status: {status}")
    .contentType("text/plain")
    .cache()
    .data("user", userObject)
    .data("status", "active")
    .attribute("timestamp", System.currentTimeMillis())
    .render();

IndexedArgumentsParserHook

The IndexedArgumentsParserHook enables the {} placeholder syntax. It's automatically included in the default Qute engine:

/**
 * Parser hook that replaces empty placeholders {} with indexed data accessors.
 */
public class IndexedArgumentsParserHook implements ParserHook {
    // Automatically transforms:
    // "Hello {}!" => "Hello {data[0]}!"
    // "{} + {} = {}" => "{data[0]} + {data[1]} = {data[2]}"
}

Cache Management

Control template caching globally or per-operation:

// Enable caching globally
Qute.enableCache();
String result = Qute.fmt("Hello {name}!", Map.of("name", "World"));

// Disable caching globally
Qute.disableCache();

// Clear the cache
Qute.clearCache();

// Per-operation caching control
String cached = Qute.fmt("Template")
    .cache()  // Enable for this operation only
    .render();

String uncached = Qute.fmt("Template")
    .noCache()  // Disable for this operation only
    .render();

Custom Engine

Replace the default static engine with a custom configuration:

Engine customEngine = Engine.builder()
    .addDefaults()
    .addValueResolver(new MyCustomResolver())
    .timeout(5000)
    .build();

Qute.setEngine(customEngine);

// Now all Qute.fmt() calls use the custom engine
String result = Qute.fmt("Hello {name}!", Map.of("name", "World"));

Escaper and Result Mappers

Escaper Class

The Escaper class provides character-based escaping using configurable replacement maps.

package io.quarkus.qute;

/**
 * Escapes character sequences using a map of character replacements.
 */
public final class Escaper {

    /**
     * Escape a character sequence.
     */
    public String escape(CharSequence value);

    /**
     * Create a new escaper builder.
     */
    public static Builder builder();

    public static class Builder {
        /**
         * Add character replacement.
         */
        public Builder add(char c, String replacement);

        /**
         * Build the escaper.
         */
        public Escaper build();
    }
}

Example:

import io.quarkus.qute.Escaper;

// Create custom escaper
Escaper xmlEscaper = Escaper.builder()
    .add('<', "&lt;")
    .add('>', "&gt;")
    .add('&', "&amp;")
    .add('"', "&quot;")
    .add('\'', "&apos;")
    .build();

String escaped = xmlEscaper.escape("<tag attr='value'>content</tag>");
// => "&lt;tag attr=&apos;value&apos;&gt;content&lt;/tag&gt;"

// CSV escaper example
Escaper csvEscaper = Escaper.builder()
    .add('"', "\"\"")
    .add(',', "\\,")
    .build();

String csvValue = csvEscaper.escape("Hello, \"World\"");
// => "Hello\\, \"\"World\"\""

HtmlEscaper

The HtmlEscaper automatically escapes HTML entities for text/html and text/xml content types.

package io.quarkus.qute;

/**
 * Escapes HTML entities for HTML and XML templates.
 * Replaces: ", ', &, <, >
 */
public class HtmlEscaper extends CharReplacementResultMapper {

    /**
     * Create HTML escaper for specified content types.
     */
    public HtmlEscaper(List<String> escapedContentTypes);
}

Replacements:

CharacterReplacement
"&quot;
'&#39;
&&amp;
<&lt;
>&gt;

Example:

Template template = engine.parse("<div>{content}</div>",
    Variant.forContentType("text/html"));

String result = template.data("content", "<script>alert('XSS')</script>").render();
// => "<div>&lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;</div>"

Registration:

Engine engine = Engine.builder()
    .addDefaults()
    .addResultMapper(new HtmlEscaper(List.of("text/html", "text/xml")))
    .build();

JsonEscaper

The JsonEscaper escapes strings for JSON content according to RFC 8259.

package io.quarkus.qute;

/**
 * Escapes strings for application/json content type.
 * Handles: ", \, control characters (U+0000 to U+001F), and special chars
 */
public final class JsonEscaper implements ResultMapper {

    /**
     * Escape a string for JSON output.
     */
    static String escape(String toEscape);
}

Replacements:

CharacterReplacement
"\"
\\\
/\/
\b (backspace)\b
\f (form feed)\f
\n (newline)\n
\r (return)\r
\t (tab)\t
Control chars\uXXXX

Example:

Template jsonTemplate = engine.parse("{\"message\": \"{msg}\"}",
    Variant.forContentType("application/json"));

String result = jsonTemplate.data("msg", "Hello\n\"World\"").render();
// => "{\"message\": \"Hello\\n\\\"World\\\"\"}"

Registration:

Engine engine = Engine.builder()
    .addDefaults()
    .addResultMapper(new JsonEscaper())
    .build();

RawString

The RawString class wraps a string to prevent automatic escaping.

package io.quarkus.qute;

/**
 * Wrapper that prevents escaping during template rendering.
 * Useful for pre-escaped HTML or trusted content.
 */
public final class RawString {

    /**
     * Create a raw string wrapper.
     */
    public RawString(String value);

    /**
     * Get the wrapped value.
     */
    public String getValue();
}

Example:

import io.quarkus.qute.RawString;

Template htmlTemplate = engine.parse("<div>{content}</div>",
    Variant.forContentType("text/html"));

// Normal escaping
String escaped = htmlTemplate.data("content", "<b>Bold</b>").render();
// => "<div>&lt;b&gt;Bold&lt;/b&gt;</div>"

// Prevent escaping with RawString
String raw = htmlTemplate.data("content", new RawString("<b>Bold</b>")).render();
// => "<div><b>Bold</b></div>"

Use Cases:

  • Pre-sanitized HTML from a trusted source
  • Content already escaped by another system
  • Programmatically generated HTML that should not be re-escaped
  • Template fragments rendered from other templates

Security Warning:

Only use RawString with trusted content. Using it with user input can lead to XSS vulnerabilities:

// UNSAFE - Do not do this with user input!
String userInput = getUserInput();
template.data("content", new RawString(userInput));  // XSS risk!

// SAFE - Let Qute escape user input
template.data("content", userInput);  // Automatically escaped

Template Instance Utilities

ForwardingTemplateInstance

The ForwardingTemplateInstance is an abstract base class for creating template instance decorators using the forwarding pattern.

package io.quarkus.qute;

/**
 * Abstract base class for template instance decorators.
 * Forwards all calls to a delegate instance.
 */
public abstract class ForwardingTemplateInstance implements TemplateInstance {

    /**
     * Return the delegate instance to forward calls to.
     */
    protected abstract TemplateInstance delegate();

    // All TemplateInstance methods forward to delegate()
}

Example: Logging Decorator

public class LoggingTemplateInstance extends ForwardingTemplateInstance {

    private final TemplateInstance delegate;

    public LoggingTemplateInstance(TemplateInstance delegate) {
        this.delegate = delegate;
    }

    @Override
    protected TemplateInstance delegate() {
        return delegate;
    }

    @Override
    public String render() {
        long start = System.currentTimeMillis();
        String result = super.render();
        long duration = System.currentTimeMillis() - start;
        System.out.println("Template rendered in " + duration + "ms");
        return result;
    }
}

// Usage
Template template = engine.getTemplate("mytemplate");
TemplateInstance logged = new LoggingTemplateInstance(template.instance());
String result = logged.data("name", "World").render();

Example: Caching Decorator

public class CachingTemplateInstance extends ForwardingTemplateInstance {

    private final TemplateInstance delegate;
    private final Map<String, String> cache = new ConcurrentHashMap<>();

    public CachingTemplateInstance(TemplateInstance delegate) {
        this.delegate = delegate;
    }

    @Override
    protected TemplateInstance delegate() {
        return delegate;
    }

    @Override
    public String render() {
        String cacheKey = computeCacheKey();
        return cache.computeIfAbsent(cacheKey, k -> super.render());
    }

    private String computeCacheKey() {
        // Compute cache key from template ID and data
        return delegate.getTemplate().getId() + ":" + dataHashCode();
    }
}

ResultsCollectingTemplateInstance

The ResultsCollectingTemplateInstance collects rendering results for analysis, testing, or caching.

package io.quarkus.qute;

/**
 * Collects all rendering results for the specified template instance.
 * Useful for testing, debugging, and result caching.
 */
public class ResultsCollectingTemplateInstance extends ForwardingTemplateInstance {

    /**
     * Create a results collector.
     *
     * @param delegate the template instance to wrap
     * @param resultConsumer consumer called with (instance, result) after rendering
     */
    public ResultsCollectingTemplateInstance(
        TemplateInstance delegate,
        BiConsumer<TemplateInstance, String> resultConsumer
    );
}

Example: Collect Rendering Results

import io.quarkus.qute.ResultsCollectingTemplateInstance;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// Create result collector
Map<String, String> results = new ConcurrentHashMap<>();

Template template = engine.getTemplate("user-profile");
TemplateInstance collector = new ResultsCollectingTemplateInstance(
    template.instance(),
    (instance, result) -> {
        String templateId = instance.getTemplate().getId();
        results.put(templateId, result);
    }
);

// Render template - result is collected
String output = collector.data("user", user).render();

// Access collected result
String collected = results.get("user-profile");

Example: Testing Template Output

public class TemplateTest {

    @Test
    public void testTemplateRendering() {
        List<String> renderedResults = new ArrayList<>();

        Template template = engine.getTemplate("test-template");
        TemplateInstance collecting = new ResultsCollectingTemplateInstance(
            template.instance(),
            (inst, result) -> renderedResults.add(result)
        );

        collecting.data("name", "Test").render();

        // Assert on collected results
        assertEquals(1, renderedResults.size());
        assertTrue(renderedResults.get(0).contains("Test"));
    }
}

Example: Async Result Collection

Map<String, String> asyncResults = new ConcurrentHashMap<>();

Template asyncTemplate = engine.getTemplate("async-template");
TemplateInstance asyncCollector = new ResultsCollectingTemplateInstance(
    asyncTemplate.instance(),
    (instance, result) -> {
        asyncResults.put(instance.getTemplate().getId(), result);
    }
);

// Works with async rendering
asyncCollector.data("data", data)
    .renderAsync()
    .thenAccept(result -> {
        // Result also collected via consumer
        String collected = asyncResults.get("async-template");
    });

Use Cases:

  • Testing: Collect template output for assertions
  • Caching: Store rendered results for reuse
  • Analytics: Track which templates are rendered and how often
  • Debugging: Capture intermediate rendering results
  • Monitoring: Measure template rendering performance

TemplateGlobalProvider

The TemplateGlobalProvider interface is implemented by generated classes that provide global variables to all templates.

package io.quarkus.qute;

/**
 * An implementation is generated for each class declaring template globals.
 * Combines TemplateInstance.Initializer and NamespaceResolver.
 */
public interface TemplateGlobalProvider
        extends TemplateInstance.Initializer, NamespaceResolver {
}

Purpose:

This interface is used internally by Quarkus to implement the @TemplateGlobal annotation. When you annotate a class or members with @TemplateGlobal, Quarkus generates an implementation of TemplateGlobalProvider that:

  1. Initializes template instances with global data
  2. Resolves global variable references as a namespace

Generated Implementation:

// User code
@TemplateGlobal
public class AppGlobals {
    public static final String APP_NAME = "MyApp";

    public static int currentYear() {
        return LocalDate.now().getYear();
    }
}

// Quarkus generates something like:
public class AppGlobals_GlobalProvider implements TemplateGlobalProvider {

    @Override
    public void accept(TemplateInstance instance) {
        // Initialize template with globals
        instance.data("APP_NAME", "MyApp");
        instance.data("currentYear", currentYear());
    }

    @Override
    public CompletionStage<Object> resolve(EvalContext context) {
        // Resolve global variable references
        return switch(context.getName()) {
            case "APP_NAME" -> CompletedStage.of("MyApp");
            case "currentYear" -> CompletedStage.of(currentYear());
            default -> Results.notFound(context);
        };
    }

    private static int currentYear() {
        return LocalDate.now().getYear();
    }
}

Manual Registration:

While typically handled by Quarkus, you can manually register global providers:

Engine engine = Engine.builder()
    .addDefaults()
    .addTemplateInstanceInitializer(new MyGlobalProvider())
    .addNamespaceResolver(new MyGlobalProvider())
    .build();

Template Usage:

<!DOCTYPE html>
<html>
<head>
    <title>{APP_NAME}</title>
</head>
<body>
    <footer>&copy; {currentYear} {APP_NAME}</footer>
</body>
</html>

See Also:

  • @TemplateGlobal annotation documentation
  • Template Globals in index.md

ContentTypes Utility

The ContentTypes class is a CDI singleton that determines MIME content types for template paths based on file extensions and configuration.

package io.quarkus.qute.runtime;

import jakarta.inject.Singleton;

/**
 * Utility for determining content types from template paths.
 * Considers file extensions, configuration mappings, and standard MIME types.
 */
@Singleton
public class ContentTypes {

    /**
     * Determine content type from a template path.
     *
     * @param templatePath path relative to template root, using '/' separator
     * @return MIME content type string
     */
    public String getContentType(String templatePath);
}

Content Type Resolution:

The getContentType() method resolves content types using this priority:

  1. Custom mappings from quarkus.qute.content-types configuration
  2. Built-in JSON handling - .json files return application/json
  3. Standard MIME types from URLConnection.getFileNameMap()
  4. Fallback to application/octet-stream if undetectable

CDI Injection:

import jakarta.inject.Inject;
import io.quarkus.qute.runtime.ContentTypes;

public class TemplateService {

    @Inject
    ContentTypes contentTypes;

    public String getTemplateContentType(String path) {
        return contentTypes.getContentType(path);
    }
}

Example Usage:

// Built-in mappings
contentTypes.getContentType("templates/page.html");    // → "text/html"
contentTypes.getContentType("templates/data.json");    // → "application/json"
contentTypes.getContentType("templates/page.xml");     // → "text/xml"
contentTypes.getContentType("templates/doc.txt");      // → "text/plain"

// Custom mapping via configuration
// application.properties: quarkus.qute.content-types.qute=text/html
contentTypes.getContentType("templates/page.qute");    // → "text/html"

Configuration:

Custom content type mappings can be defined in application.properties:

# Map custom suffixes to MIME types
quarkus.qute.content-types.qute=text/html
quarkus.qute.content-types.email=text/plain
quarkus.qute.content-types.svg=image/svg+xml

Use Cases:

  • Variant Selection: Determine content type for template variants
  • Response Headers: Set appropriate Content-Type headers for rendered output
  • Custom Escaping: Apply content-type-specific escaping rules
  • Template Routing: Route requests based on template content types

See Also:

Best Practices

Using Qute.fmt() Effectively

// Good: Simple one-liners
String msg = Qute.fmt("Hello {}!", name);

// Good: With caching for repeated use
String template = "User: {name}, Email: {email}";
Qute.enableCache();
for (User user : users) {
    String result = Qute.fmt(template, Map.of(
        "name", user.getName(),
        "email", user.getEmail()
    ));
}

// Bad: Complex templates (use proper Template instead)
String complex = Qute.fmt("""
    {#for item in items}
      {item.name}
    {/for}
    """, Map.of("items", items));  // Use Template for this!

RawString Security

// SAFE: Trusted content from your code
template.data("header", new RawString("<h1>Welcome</h1>"));

// SAFE: Content from sanitized source
String sanitized = sanitizer.sanitize(userInput);
template.data("content", new RawString(sanitized));

// UNSAFE: Direct user input
String userInput = request.getParameter("content");
template.data("content", new RawString(userInput));  // XSS vulnerability!

// SAFE: Let Qute escape user input
template.data("content", userInput);  // Automatically escaped

Custom Escaper Patterns

// CSV escaper
Escaper csvEscaper = Escaper.builder()
    .add('"', "\"\"")  // Double quotes
    .add('\n', "\\n")  // Newlines
    .add('\r', "\\r")  // Carriage returns
    .build();

// SQL escaper (for identifiers, not values!)
Escaper sqlEscaper = Escaper.builder()
    .add('\'', "''")  // Single quotes
    .build();

// XML attribute escaper
Escaper xmlAttrEscaper = Escaper.builder()
    .add('<', "&lt;")
    .add('>', "&gt;")
    .add('&', "&amp;")
    .add('"', "&quot;")
    .add('\'', "&apos;")
    .build();

ForwardingTemplateInstance Use Cases

// Use case 1: Add default data
public class DefaultDataInstance extends ForwardingTemplateInstance {
    private final TemplateInstance delegate;
    
    public DefaultDataInstance(TemplateInstance delegate) {
        this.delegate = delegate;
        // Add default data
        delegate.data("timestamp", System.currentTimeMillis());
        delegate.data("version", "1.0");
    }
    
    @Override
    protected TemplateInstance delegate() {
        return delegate;
    }
}

// Use case 2: Add security checks
public class SecureTemplateInstance extends ForwardingTemplateInstance {
    private final TemplateInstance delegate;
    private final SecurityContext securityContext;
    
    @Override
    public String render() {
        if (!securityContext.canRenderTemplate(delegate.getTemplate().getId())) {
            throw new SecurityException("Access denied");
        }
        return super.render();
    }
    
    @Override
    protected TemplateInstance delegate() {
        return delegate;
    }
}

// Use case 3: Add metrics
public class MetricsTemplateInstance extends ForwardingTemplateInstance {
    private final TemplateInstance delegate;
    private final MeterRegistry metrics;
    
    @Override
    public String render() {
        Timer.Sample sample = Timer.start(metrics);
        try {
            return super.render();
        } finally {
            sample.stop(metrics.timer("template.render",
                "template", delegate.getTemplate().getId()));
        }
    }
    
    @Override
    protected TemplateInstance delegate() {
        return delegate;
    }
}

Summary

Qute provides several utility classes for common operations:

ClassPurpose
QuteStatic engine access and convenient formatting
Qute.FmtFluent template formatting with caching
IndexedArgumentsParserHookEnables {} placeholder syntax
EscaperConfigurable character escaping
HtmlEscaperHTML entity escaping for HTML/XML
JsonEscaperJSON string escaping (RFC 8259)
RawStringPrevent automatic escaping
ForwardingTemplateInstanceBase class for instance decorators
ResultsCollectingTemplateInstanceCollect rendering results
TemplateGlobalProviderProvide global variables (generated)
ContentTypesDetermine MIME types from template paths

These utilities cover the most common template usage patterns and provide building blocks for custom extensions.