I have application aimed to transform API responses into human-readable text. For that purpose I call data from 3rd parties, build the model from it and run template against context through org.thymeleaf.TemplateEngine
.
Example:
public Optional<String> process(String template, Map<String, Object> model) {
Context thymeleafContext = new Context();
thymeleafContext.setVariables(model);
try {
return Optional.of(templateEngine.process(template, thymeleafContext););
} catch (TemplateProcessingException e) {
log.error(e.getMessage());
}
return Optional.empty();
}
For that purpose I have a number of plaintext Thymeleaf templates org.thymeleaf.templatemode.TemplateMode#TEXT
(keep note this) placed into separate .txt
files.
Let's assume I have template like this: Born year: [[${bornYear}}]]
. Now I need to cast the value of bornYear
variable into specific pattern. For that purpose I have utility method like this
public class Utils {
private Utils() {}
public static String cast(String source, String pattern) {
return "<implement me>";
}
}
and I assume I can execute this utility method in the template. However I can not make it possible, at least I get exception on template processing.
What I tried:
Born year: [[${Utils.cast(bornYear, 'dd-MM-yyyy')}]]
.Born year: [[${T(com.example.Utils).cast(bornYear, 'dd-MM-yyyy')}]]
. This leads to org.thymeleaf.exceptions.TemplateProcessingException: Instantiation of new objects and access to static classes or parameters is forbidden in this context
.Utils
as Spring Bean and adjust template in a way Born year: [[${utils.cast(bornYear, 'dd-MM-yyyy')}]]
or Born year: [[${@utils.cast(bornYear, 'dd-MM-yyyy')}]]
. This leads to Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1057E: No bean resolver registered in the context to resolve access to bean 'Utils'
.TemplateResolver configuration:
@Bean
public ClassLoaderTemplateResolver templateResolve() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".txt");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setOrder(1);
templateResolver.setCheckExistence(true);
return templateResolver;
}
My questions are:
TemplateMode.TEXT
?Thank you in advance!
The way I would do this is...
Change utils to be a regular object:
public class Utils {
public String cast(String source, String pattern) {
return "<implement me>";
}
}
Always put a utils
onto your model:
public Optional<String> process(String template, Map<String, Object> model) {
Context thymeleafContext = new Context();
model.put("utils", new Utils());
thymeleafContext.setVariables(model);
In your template, use it like this:
Born year: [[${utils.cast(bornYear, 'dd-MM-yyyy')}]]
Another way you can do this, when instantiating your context add a ThymeleafEvaluationContext
like this. You will have to get access to your Spring ApplicationContext
as well.
public Optional<String> process(String template, Map<String, Object> model) {
Context thymeleafContext = new Context();
context.setVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME, new ThymeleafEvaluationContext(applicationContext, null));
thymeleafContext.setVariables(model);
Then if you've annotated your utils class with @Component
and made sure it is in a package that is being scanned by your configuration:
@Component
public class Utils {
public String cast(String source, String pattern) {
return "<implement me>";
}
}
You will be able to access Beans
in your templates like this:
Born year: [[${@utils.cast(bornYear, 'dd-MM-yyyy')}]]