I'm testing a dummy template as follows:
.get("/test", ctx -> ctx.render("templates/test.mustache", new HashMap<>()));
Editing the template (test.mustache) requires restarting the app (or maybe rebuilding the app, but stop + run in Eclipse anyway).
It's a major use case to be able to work with a template without a need of restarting the Javalin app for every change. I even tried to add .enableStaticFiles("src/resources/templates", Location.EXTERNAL), but no luck.
Is this a feature? Works differently in SparkJava by the way.
Thanks!
You probably have to configure mustache to load templates from the file system rather than the classpath. I don't know how it is configured in Spark, but you could just look at the implementation there and copy the relevant parts.
Template files are not static files, so they're not effected by Loaction.EXTERNAL.
Edit: Accidentally closed the issue (mobile).
Found a tip from here https://github.com/spullara/mustache.java/issues/57
MustacheFactory mf = new DefaultMustacheFactory(templates);
mf.compile(the_template);
JavalinMustache.configure(mf);
Gotta be done for every request, so obviously for development purposes only, but I'm fine with this now. Some sort of reload-on-modify -option would be cool, it was so super easy to take the templates in use.
Happy you found a solution @valtterip!
Gotta be done for every request, so obviously for development purposes only, but I'm fine with this now.
Instead of doing it "manually" per request, you could register a new FileRenderer:
JavalinRenderer.register((filePath, model) -> {
MustacheFactory mustacheFactory = new DefaultMustacheFactory("./");
StringWriter stringWriter = new StringWriter();
mustacheFactory.compile(filePath).execute(stringWriter, model).close();
return stringWriter.toString();
)}, ".mustache")
Then you could use Javalin like normal, with calls to ctx.render(...).
Some sort of reload-on-modify -option would be cool
I don't think anything template-specific should be included in Javalin, other than a minimal example implementation that can be overridden by the user.
My solution is to use WatchService to reload the MustacheFactory. This is to be used only during development:
final Path root = FileSystems.getDefault().getPath("src/main/resources");
WatchService watchService = FileSystems.getDefault().newWatchService();
// register for all subfolders
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
logger.info("Watching folder " + dir);
dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
return FileVisitResult.CONTINUE;
}
});
new Thread(() -> {
try {
boolean valid = true;
while (valid) {
boolean reloadMustache = false;
WatchKey wk = watchService.take();
for (WatchEvent<?> event : wk.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind.name().equals("OVERFLOW")) {
continue;
}
Path changed = (Path) event.context();
logger.info("File changed: " + changed);
// add any other system that needs reloading here
reloadMustache = reloadMustache || changed.toString().endsWith((".mustache"));
}
// reload assets as needed
if (reloadMustache) {
logger.info("Reloading all Mustache views");
JavalinMustache.configure(new DefaultMustacheFactory(new File("src/main/resources")));
}
// reset the key
valid = wk.reset();
if (!valid) {
logger.warn("Key has been unregistered! Exiting.");
}
}
} catch (InterruptedException e) {
logger.warn("Interrupted. Exiting.");
}
logger.info("Stopped monitoring src/main/resources");
}).start();
I don't think this belongs in Javalin, tbh. Hot reloading is something that can affect multiple aspects of the application, and is best left for an external system.
I don't think this belongs in Javalin, tbh.
I agree, but thank you for documenting your solution here. I've added the info tag.
Most helpful comment
Happy you found a solution @valtterip!
Instead of doing it "manually" per request, you could register a new
FileRenderer:Then you could use Javalin like normal, with calls to
ctx.render(...).I don't think anything template-specific should be included in Javalin, other than a minimal example implementation that can be overridden by the user.