Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MAJOR FEAT] Refactoring of the Message Resolution API #421

Closed
danielfernandez opened this issue Oct 20, 2015 · 1 comment
Closed

[MAJOR FEAT] Refactoring of the Message Resolution API #421

danielfernandez opened this issue Oct 20, 2015 · 1 comment

Comments

@danielfernandez
Copy link
Member

The refactorings undergone by the Template Resolution API (#419) and Context API (#420) offered the possibility of performing an additional refactoring, this time on the Message Resolution API (package org.thymeleaf.messageresolver).

The Message Resolution API in Thymeleaf 2.1 suffered from two main issues:

  • No possibility to specify externalized messages for inserted fragments. When an A.html template inserted a fragment from another B.html template, externalized messages could only be resolved from A.properties and never B.properties. Note this affected non-Spring enabled applications only, given Spring-enabled applications normally delegate message resolution entirely to Spring via the Spring MessageSources.
  • No possibility to customize what should be shown when a message was not found in any of the message resolvers, something the new API knows as absent message representation.

Both were unresolvable problems in 2.1, as it was the very architecture of the message resolution API that prevented any of these features from being implemented.

Thymeleaf 3.0 however modifies the ITemplateResolver interface, so that it looks like this:

public interface IMessageResolver {

    public String getName();

    public Integer getOrder();

    public String resolveMessage(
            final ITemplateContext context, final Class<?> origin, 
            final String key, final Object[] messageParameters);

    public String createAbsentMessageRepresentation(
            final ITemplateContext context, final Class<?> origin, 
            final String key, final Object[] messageParameters);

}

So message resolvers now have the opportunity to define not only how a specific message should be resolved, but also what representation should be used for signaling that a message is not found in any of the existing resource bundles.

Also, note that message resolution can be done based on two different pieces of information:

  • Context, by using the ITemplateContext passed as parameter. Normally, this is used for obtaining the name of the template being processed and using that name for finding the suitable resource bundle.
  • Origin, by using the Class<?> passed as a parameter, which enables IMessageResolver implementations to perform message lookups specific to processors, pre-/post-processors, or any other template processing structure, without the result depending on the template being processed.

Implementations of IMessageResolver will choose to use both mechanism, one of them, or maybe none.

Message Resolvers will be queried for messages according to their order property. If all the configured message resolvers return null, then the same order will be used for querying them for an absent message representation.

Thymeleaf 3.0, just like 2.1, offers two implementations of IMessageResolver:

  • StandardMessageResolver, used by default in non-Spring-enabled applications, which will look for .properties files with the same names as the templates it resolves.
  • SpringMessageResolver, used by default in Spring-enabled applications, which will simply delegate to the Spring configured MessageSource beans, so that Spring is left in complete control of the application's resource bundles.

Improvements on StandardMessageResolver

The org.thymeleaf.messageresolver.StandardMessageResolver class is the basic implementation of IMessageResolver, and it has been improved in Thymeleaf 3.0 so that it allows a greater flexibility for message resolution.

It works like this:

  • First, for template-based message resolution, it will examine the whole stack of nested template resolutions (so that files for fragments are taken into account, see [MAJOR FEAT] Refactoring of the Context API #420) and will try to find files with the same name as the template, ending in .properties (e.g. main.html -> main.properties). This will be done starting from the first-level template and going towards the most specific fragment template, so that the templates that insert a fragment have the possibility to overwrite the messages specified for that fragment by default if needed.
  • Note these message files will be resolved as relative resources using the same template resolution mechanism used for the templates themselves (see [MAJOR FEAT] Refactoring of the Template Resolution API #419). So if a template file was retrieved using an HTTP URL, equivalent HTTP URLs will be called for trying to access that template's resources.
  • For each of these templates, several resource bundle files will be examined. For example, a message in template /WEB-INF/templates/home.html for locale gl_ES-gheada (gl = language, ES = country, gheada = variant) would be looked for in .properties files in the following sequence:
    • /WEB-INF/templates/home_gl_ES-gheada.properties
    • /WEB-INF/templates/home_gl_ES.properties
    • /WEB-INF/templates/home_gl.properties
    • /WEB-INF/templates/home.properties
  • If no message is found using this mechanism, origin-based message resolution will be attempted. This will allow the resolution of messages from .properties files living in the classpath (and only in the classpath) in files corresponding with the names of the classes being used as origin. For example, a processor called my.company.processor.SomeDataProcessor using its own class as origin will be able to resolve messages from a my/company/processor/SomeDataProcessor_gl_ES.properties file in the classpath.
  • Also, if a message is not found for the exact class, resolution will be tried for each of the superclasses this my.company.processor.SomeDataProcessor class extends, until a suitable message is found, or no more superclasses (except java.lang.Object) exist.
  • Finally, resolution will be tried using the default messages specified directly at the StandardMessageResolver object by means of its setDefaultMessages(Properties) and addDefaultMessage(String, String) methods.

Finally, note that resource bundles obtained using the template-based resolution mechanism will be cached only if the template they refer to was resolved as cacheable by its Template Resolver. Resource bundles obtained using the origin-based resolution will, in contrast, be always cached.

Improvements on SpringMessageResolver

The org.thymeleaf.spring3|spring4.messageresolver.SpringMessageResolver class has also been slightly improved in Thymeleaf 3.0, so that now after delegating to Spring's MessageSource mechanisms the resolution of externalized messages it will also try an origin-based resolution operation completely analogous to the one performed by StandardMessageResolver, as explained above.

This should allow processors, pre-/post-processors, etc. to be packaged in extension libraries as .jar files along with their .properties resource bundles, and make them usable both in Spring-enabled and non-Spring-enabled applications.

@danielfernandez
Copy link
Member Author

Already in 3.0.0-SNAPSHOT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant