- Introduction
- Settings Builder
- Base Directory
- HTML Escaping
- Include Depth
- Issue Handling
- Complete Configuration Example
JHP is configured using the Settings class, which uses a builder pattern for easy and readable configuration.
Create settings using the builder:
Settings settings = Settings.builder()
.base("/path/to/templates")
.escape(true)
.maxIncludeDepth(15)
.issueHandleMode(IssueHandleMode.COMMENT)
.build();The base directory is the root path for all template files.
Settings settings = Settings.builder()
.base("/var/www/templates")
.build();Settings settings = Settings.builder()
.base("src/main/resources/templates")
.build();If not specified, the base directory defaults to the current directory (.):
Settings settings = Settings.builder().build();
// base = "."The base directory acts as a security boundary. Templates cannot include files outside this directory:
Settings settings = Settings.builder()
.base("/var/www/templates")
.build();Template:
{% include '../../../etc/passwd' %}
<!-- Error: Path not in base directory -->Control whether output is HTML-escaped by default.
Settings settings = Settings.builder()
.escape(true) // Default
.build();Template:
{{ userInput }}
<!-- <script> becomes <script> -->Settings settings = Settings.builder()
.escape(false)
.build();Template:
{{ htmlContent }}
<!-- HTML is rendered as-is -->The default escaping mode is true:
System.out.println(Settings.defaultEscapeMode); // trueEven with escaping enabled, you can output raw HTML using triple braces:
{{ escaped }} <!-- HTML-escaped -->
{{{ notEscaped }}} <!-- Raw HTML -->Limit the maximum depth of nested includes to prevent infinite recursion.
Settings settings = Settings.builder()
.maxIncludeDepth(10)
.build();The default maximum include depth is 15:
System.out.println(Settings.defaultMaxIncludeDepth); // 15Limiting include depth prevents:
- Infinite include loops
- Stack overflow errors
- Performance issues
Settings settings = Settings.builder()
.maxIncludeDepth(3)
.build();Template Structure:
main.jhp
└─ includes header.jhp (depth 1)
└─ includes logo.jhp (depth 2)
└─ includes image.jhp (depth 3)
└─ includes watermark.jhp (depth 4) ❌ Error!
Configure how JHP handles runtime issues like missing variables or include errors.
JHP provides four modes for handling issues:
Renders issues as HTML comments:
Settings settings = Settings.builder()
.issueHandleMode(IssueHandleMode.COMMENT)
.build();Output:
<!-- MISSING_VARIABLE: Variable 'username' not found -->Throws exceptions for issues:
Settings settings = Settings.builder()
.issueHandleMode(IssueHandleMode.THROW)
.build();Result:
// Throws RuntimeExceptionUse this mode in development or when you want strict error handling.
Renders detailed debug information:
Settings settings = Settings.builder()
.issueHandleMode(IssueHandleMode.DEBUG)
.build();Output:
<details style="...">
<summary>Template Engine Debug — Issue: MISSING_VARIABLE</summary>
<table>
<tr><td>Issue Type</td><td>MISSING_VARIABLE</td></tr>
<tr><td>Message</td><td>Variable 'username' not found</td></tr>
<tr><td>Timestamp</td><td>2025-10-05T00:18:03Z</td></tr>
<tr><td>Thread</td><td>main</td></tr>
<tr><td>Include Stack</td><td>...</td></tr>
</table>
</details>Perfect for development and debugging.
Silently ignores issues:
Settings settings = Settings.builder()
.issueHandleMode(IssueHandleMode.IGNORE)
.build();Output:
<!-- Nothing rendered for errors -->Use with caution - errors are completely silent.
The default issue handle mode is COMMENT:
System.out.println(Settings.defaultIssueHandleMode); // COMMENTJHP can handle various issue types:
MISSING_VARIABLE- Variable not found in contextFUNCTION_ERROR- Function execution errorINCLUDE_ERROR- Include file errorINCLUDE_CYCLE- Circular include detectedINCLUDE_MAX_DEPTH- Max include depth exceededINCLUDE_NOT_IN_BASE_DIR- Include path outside base directoryMISSING_INCLUDE- Include file not foundUNKNOWN_ELEMENT- Unknown template elementFUNCTION_CALL_ERROR- Invalid function call
Development:
.issueHandleMode(IssueHandleMode.DEBUG)Testing:
.issueHandleMode(IssueHandleMode.THROW)Production:
.issueHandleMode(IssueHandleMode.COMMENT)
// or
.issueHandleMode(IssueHandleMode.IGNORE)Settings devSettings = Settings.builder()
.base("src/main/resources/templates")
.escape(true)
.maxIncludeDepth(20)
.issueHandleMode(IssueHandleMode.DEBUG)
.build();
FunctionLibrary lib = new FunctionLibrary();
JhpEngine devEngine = new JhpEngine(devSettings, lib);Settings prodSettings = Settings.builder()
.base("/var/www/app/templates")
.escape(true)
.maxIncludeDepth(10)
.issueHandleMode(IssueHandleMode.COMMENT)
.build();
FunctionLibrary lib = new FunctionLibrary();
JhpEngine prodEngine = new JhpEngine(prodSettings, lib);Settings testSettings = Settings.builder()
.base("src/test/resources/templates")
.escape(true)
.maxIncludeDepth(5)
.issueHandleMode(IssueHandleMode.THROW)
.build();
FunctionLibrary lib = new FunctionLibrary();
JhpEngine testEngine = new JhpEngine(testSettings, lib);public class TemplateConfig {
public static Settings getSettings() {
String env = System.getenv("APP_ENV");
if ("production".equals(env)) {
return productionSettings();
} else if ("test".equals(env)) {
return testSettings();
} else {
return developmentSettings();
}
}
private static Settings productionSettings() {
return Settings.builder()
.base("/var/www/templates")
.escape(true)
.issueHandleMode(IssueHandleMode.COMMENT)
.build();
}
private static Settings developmentSettings() {
return Settings.builder()
.base("src/main/resources/templates")
.escape(true)
.issueHandleMode(IssueHandleMode.DEBUG)
.build();
}
private static Settings testSettings() {
return Settings.builder()
.base("src/test/resources/templates")
.escape(true)
.issueHandleMode(IssueHandleMode.THROW)
.build();
}
}public class TemplateEngine {
private static final JhpEngine instance;
static {
Settings settings = TemplateConfig.getSettings();
FunctionLibrary lib = new FunctionLibrary();
// Register custom functions
registerCustomFunctions(lib);
instance = new JhpEngine(settings, lib);
}
public static JhpEngine getInstance() {
return instance;
}
private static void registerCustomFunctions(FunctionLibrary lib) {
// Register your custom functions here
}
}Unless you have a specific reason, always keep HTML escaping enabled:
.escape(true) // Prevent XSS attacks.maxIncludeDepth(10) // Enough for most use casesSettings objects are immutable after creation. To change settings, create a new Settings instance:
Settings settings1 = Settings.builder().base("/path1").build();
Settings settings2 = Settings.builder().base("/path2").build();
JhpEngine engine1 = new JhpEngine(settings1, lib);
JhpEngine engine2 = new JhpEngine(settings2, lib);- Explore Advanced Usage
- Review API Reference
- Check out Examples