Ignore locale on thymeleaf #date.format() - spring-boot

I want to keep date format to fix standard regardless of locale. But however it is by default taking current locale and setting format based on locale.
th:text="${#dates.format(myDate, 'dd-MMM-yyyy')}"
I am always expecting format be like
09-Sep-2015
but with CA locale I am getting 09-de set.-2015
Is there a way to fix this.
UPDATE
This question is not duplicate of This question. My problem is related to locale formatting.

Not sure you are using Maven or Gradle. Add thymeleaf-extras-java8time as your dependency.
and instead of #dates use #temporal and specify locale as parameters as below.
th:text="${#temporals.format(myDate, 'dd-MMM-yyyy','en')}"
But make sure your myDate is in java.time.* format

The #temporals.format function is the correct one to use. However, the third "locale" argument must be a java.util.Locale object, not a string.
The following work:
#temporals.format(myDate, 'dd-MM-yyyy', new java.util.Locale('en'))
#temporals.format(myDate, 'dd-MM-yyyy', #java.util.Locale#ENGLISH)
Note that this is true even if you're working with Kotlin Spring Boot. The syntax in the Thymeleaf template isn't Java, it's an OGNL Expression.
https://commons.apache.org/proper/commons-ognl/language-guide.html
I'll quote the useful syntax used here:
#variable
Context variable reference
#class#method(args)
Static method reference
#class#field
Static field reference
new class(args)
Constructor call
Edit: one other option is to specify the Locale in the Thymeleaf context, if you just want to override the default system Locale. I've included a Kotlin snippet of how that might work:
val context = Context() // org.thymeleaf.Context
context.locale = Locale.ENGLISH
context.setVariable("x", 0)
templateEngine.process("classpath:template.html", context)

Related

Gradle - Get the default value (convention) of a Property

I want to achieve a very basic thing (in any normal language): I would like to get the default value (convention) of a Property. Gradle docs in chapter Lazy configuration, describes how to apply the convention to a Property:
def property = objects.property(String)
// Set a convention
property.convention("convention 1")
println("value = " + property.get())
// Can replace the convention
property.convention("convention 2")
println("value = " + property.get())
property.set("value")
// Once a value is set, the convention is ignored
property.convention("ignored convention")
println("value = " + property.get())
The problem is, that you cannot query the property to get information what is the convention (the default value) when you set a value. It seems that the only way it's to clear (nullify) the value:
property.value(null).get()
But this is just stupid because you are doing unnecessary actions, while the convention is somewhere there?
Does anyone know how to get it without clearing the value?
The answer (for 2022) is: No, you can't get the default value (convention) of a Property.
I'll try to answer why set overrides the convention once it's called. The convention is the default value, once the property has a value by calling set the convention is ignored because the property has a value. That makes sense because that's what a default value should mean. If I have a value use that otherwise use this default value. Convention and set seem to follow that pattern. I don't quite follow why this is surprising to how you want to use gradle, but just as an outside observer I think gradle is doing what is expected.
You can test if a property exists using
if( property.isPresent() ) {
// do whatever
}
I bet that isPresent() is going to say false when the convention would be returned by get(), but if you call set then isPresent() is going to return true.
Then there are other methods to help like:
def val = property.getOrElse( "SomeDefault" )
def maybe = property.getOrNull()
I do think Gradle doesn't always do a good job of making the API very accessible from their home page and docs (ie groovydoc) for answering more complex questions:
https://docs.gradle.org/current/javadoc/org/gradle/api/provider/Property.html
These methods are the super interface Provider and hence why it may not be obvious they are available if you don't look up the chain:
https://docs.gradle.org/current/javadoc/org/gradle/api/provider/Provider.html

SpelParseException: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'

I am trying to condtionally create a component using #ConditionalOnExpression("not ${service.synchronous} && not ${service.disabled}").
I based this on Spring Boot SpEL ConditionalOnExpression check multiple properties, which provides a multi-property conditional as follows: #ConditionalOnExpression("${properties.first.property.enable:true} && ${properties.second.property.startServer:false}")
However, I keep getting:
Caused by: org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
Those properties are always set in my .properties file so I did not provide a default value with the colon notation. What am I doing wrong?
You will need to provide the default values for your properties like in the example you followed, so update the expression to be:
#ConditionalOnExpression("not ${service.synchronous:false} && not ${service.disabled:true}")
In most such cases the properties your app is reading are not what you expect them to be.
Set a breakpoint on all constructors of SpelParseException. In the debugger you will see the expression that is parsed, that will give show you exactly which properties you are really using.
Maybe you have to go search a little in the stack until you find the right location where you can see the expression.
My mistake was that I had not imported the test properties file in a Spring test.
After I added #TestPropertySource("classpath:/application.properties") to the test class, the properties from the properties file were used.

ConstraintViolationException - extract field name which caused exception

I'm using hibernate-validator with a JAX-RS service to validate query parameters using #NotNull:
#GET
public Response doSomething(#NotNull #QueryParam("myParam") String myParam) {...}
This works as expected and throws a ConstraintViolationException if myParam is null. I'd like to extract the param name which is associated to the violation (e.g. myParam), and return that in the response message to the client but there does not appear to be an obvious way of extracting this from the exception. Can someone provide some insight?
As of BeanValidation 1.1 there is a ParameterNameProvider contract which makes parameter name extraction configurable. As mentioned in the other answer, with Java 8 you can get the parameter names in the byte code provided you compile with the -parameters flag. Use the ReflectionParameterNameProvider in this case. However, even with Java 7 you can get parameter names, for example by using the ParanamerParameterNameProvider. This parameter name provider is based on Paranamer and there are several ways to set it up.
This only works if you're using Java 8, as prior to Java 8 the actual parameter name was lost at compile time. Its now retained, assuming you compile and run at Java 8. See also http://docs.jboss.org/hibernate/validator/5.2/reference/en-US/html_single/#_java_8_support

Grails define custom error message for command object

I am writing a Grails (2.3.3 currently) application and have created a validateable command object similar to the following:
#Validateable
class MyCustomCommand {
String name
static constraints = {
name blank: false
}
}
In my i18n/messages.properties file I defined the following properties to override the default error messages.
MyCustomCommand.name.blank=Name must be provided.
MyCustomCommand.name.null=Name must be provided.
Which per the Grails documentation should be of the format [Class Name].[Property Name].[Constraint Code] as I have done. When I run my application if I leave the value blank I still get the default message for a null property.
I also tried following the example of the default messages and defining them a follows, but still get the default message.
MyCustomCommand.name.blank.message=Name must be provided.
MyCustomCommand.name.null.message=Name must be provided.
I am assuming that I am missing something simple here, but have yet to stumble upon what. Any suggestions on what I am doing incorrectly?
It is simple indeed. Message should look like:
myCustomCommand.name.blank=Name must be provided.
myCustomCommand.name.nullable=Name must be provided.
//className.propertyName.blank (camelCase with first letter of class name lower)
So, as I anticipated it was something simple. I was using the defaults as an example which used null where as what I really needed was nullable. Which does make sense as that matches the constraint name.
Therefore the correct version is:
myCustomCommand.name.blank=Name must be provided.
myCustomCommand.name.nullable=Name must be provided.

How to reuse Spring or Java's locale lookup code?

I am doing a Spring web application.
I need to support a few locales such as ko (Korean), ru (Russian), en (English), etc.
I am able to catch what locale is from a browser via ways such as RequestContextUtils.getLocale(request) or LocaleChangeInterceptor.
However, the browser's locale may not be what my web app supports. I have to resolve it to the closet or default locale.
Basically, I need to know how to get the resolved locale given the browser's locale AND a few locale values such as ko, ru, and en.
My understanding is that Spring has such locale resolution code because it is able to find right resource bundles given a browser's locale. I am hoping to reuse Spring's code for locale resolution, but not sure how to do it. Please note that this question has nothing to do with finding the brwoser's locale or displaying proper messages.
EDIT
Based on my tracing Spring's code, it appears that Spring depends on JDK to find the exact or closest locale. I just found out this and think this is what I am looking for:
Resource Bundle Lookup Order
https://sites.google.com/site/openjdklocale/design-notes/resource-bundle-lookup-order
Please note that I don't need to find right resource bundle. I just need to get the locale the existing JDK code returns given a locale in question and a few known locales. So I am hoping to reuse existing JDK's lookup code. Any idea?
I am using JDK 7.
Thanks for any help and input!
Regards.
Short answer
Have you checked the official documentation (chapter 17.8 Using locales)? You need to configure LocaleResolver and possibly a LocaleChangeInterceptor (or write your own).
Longer description about how Spring works
Note, that resolving client's locale is different task from getting a correct resource bundle.
Spring uses LocaleResolver to get or set the current locale. There are several implementations for different strategies to LocaleResolver:
FixedLocaleResolver - will always resolve locale to predefined value (not capable of setting different locale)
SessionLocaleResolver - stores and resolves locale to value store on session under special key
AcceptHeaderLocaleResolver - this is the resolver which actually tries to get locale from the browser (not capable of setting different locale)
CookieLocaleResolver - stores and resolves locale to value stored in a browser cooke
LocaleResolver is used to populate LocaleContextHolder (btw. that is the class you should be getting locale from).
There is a second mechanism LocaleChangeInterceptor, which is able to set locale via your selected LocaleResolver based on user request parameter.
Now this infrastructure is unrelated to your resource bundles (messages.properties, messages_en.properties, ...) and the mechanism used to resolve your messages. The following examples will show why.
Example scenarios
Lets assume your resource bundles are:
messages.properties - with ru messages (default messages)
messages_ko.properties - with ko messages
Lets assume you have configured SessionLocaleResolver with default locale ru
And lets assume you have configured LocaleChangeInterceptor
SCENARIO I - First requets:
User makes first request to the application
As soon as the request reaches Spring's DispatcherServlet it queries LocaleResolver to get locale for the request
No locale is set on the session, so the locale is resolved to ru (default)
...handler stuff...
Now you are rendering the webpage and you want to use <spring:message> tag...
The tag tries to resolve translation code using preconfigured MessageSource (ResourceBundleMessageSource) with request locale (this is the one resolved by your resolver).
Message source tries to load translation code from messages_ru.properties which does not exist, so it moves to more general file messages.properties (which "by accident" contains your default language - ru)
User get his page in russian language
SCENARIO II - User clicks link to change his language to ko:
Second request is made with query parameter locale=ko
DispatcherServlet resolves request locale to ru (this is what your locale resolver returns)
Before the request is handed over to your handler it goes through the LocaleChangeInterceptor handler interceptor.
LocaleChangeInterceptor detects locale query parameter and calls setLocale method on your LocaleResolver, which leads to changing request locale and storing new locale on the session for future requests.
...handler stuff...
...view stuff...
Now <spring:message> is calling MessageSource with ko locale.
Message source tries to load messages_ko.properties and succeeds.
User get his page in korean language
SCENARIO III - User tries to change to invalid locale:
User makes request with query parameter locale=en.
...dispatcher stuff... (locale is resolved to ko from the session)
Handler interceptor changes the locale to en (this will be stored on the session as well)
...handler stuff...
...view stuff...
Now <spring:message> is calling MessageSource with en locale.
Message source tries to load messages_en.properties which does not exist so it moves to a more general file messages.properties and messages are translated to ru, even thou the request locale is set to en.
User get his page in rusian language
Summary
Now the last example is probably what bothers you - there is no check whether the locale user selects is supported or not. If you don't want to allow user to switch to unsupported locale, then you need to either subclass some LocaleResolver or write your own LocaleChangeInterceptor.
Complete Reuse
To reuse the JDK logic, you could create a property file within the classpath for each of the known locales (such as test_fr_CA.properties, test_fr.properties, test_en_US.properties, test_en.properties, test.properties). Don't forget the root locale (test.properties) if you want to be able to match to it. Then simply create a resource bundle for the locale in question and inspect it to see the actual locale used.
ResourceBundle rb = ResourceBundle.getBundle("test", Locale.FRENCH);
System.out.println("Locale used is:"+rb.getLocale().toString());
The files can be created dynamically and cleaned up after the test.
High Level Code Replication, Low Level Reuse
You could replicate the high level code in java.util.ResourceBundle.getBundleImpl(...). This is basically going through looking for a match (using your own matching logic like equal toString() representations) in a candidate list of locales reusing java.util.ResourceBundle.Control.getCandidateLocales(...) on the locale in question. If there is no match you get the next fallback locale for the locale in question by reusing java.util.ResourceBundle.Control.getFallbackLocale(...) For each fallback locale you try to match a locale in it's candidate list repeating the fallback in a loop until there are no fallback locales left. Note that the root locale will be the last candidate in each candidate list but it should be skipped unless you have exhausted all fallback locales.
This method does not require creating files. You use a non-existent baseName in the getCandidateLocales(...) and get FallbackLocale(...) calls and compare each candidate locale to your list of known locales looking for a match.
A simple example of this would be like the following:
ResourceBundle.Control rbControl = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_PROPERTIES);
Locale localeInQuestion = Locale.CHINA;
List<Locale> knownLocales = Arrays.asList(new Locale[] {Locale.CANADA_FRENCH, Locale.FRENCH, Locale.US, Locale.UK, Locale.ENGLISH, Locale.ROOT});
String nonExistentBaseName = "bogus";
Locale matchingLocale = null;
Boolean reachedRootLocaleMatch = false;
outerloop:
for (Locale targetLocale = localeInQuestion;
targetLocale != null;
targetLocale = rbControl.getFallbackLocale(nonExistentBaseName, targetLocale)) {
List<Locale> candidateLocales = rbControl.getCandidateLocales(nonExistentBaseName, targetLocale);
for (Iterator iterator = candidateLocales.iterator(); iterator.hasNext();) {
Locale currentCandidateLocale = (Locale) iterator.next();
if (knownLocales.contains(currentCandidateLocale)) {
if (currentCandidateLocale.equals(Locale.ROOT)) {
reachedRootLocaleMatch = true;
}
else {
matchingLocale = currentCandidateLocale;
break outerloop;
}
}
}
}
if (matchingLocale == null && reachedRootLocaleMatch) {
matchingLocale = Locale.ROOT;
}
if (matchingLocale != null) {
System.out.println("The matching locale is: "+matchingLocale.toString());
}
else {
System.out.println("There was no matching locale");
}

Resources