I keep looking for the answer on how to reset/change the Locale of a Java (Spring) web application once it's set via the Locale.setDefault(new Locale ("en", "US")). Can someone please help me because it's frustrating seeing that after I set the Locale in my web app, I can't change it by simply calling Locale.setDefault(new Locale ("newLang", "newCountry")).
Is the locale being cached on the server?
The Locale.setDefault is a global thing. If you have two users that need to use differnt locales that won't work out.
You should probably put the locale in the HttpServletRequest.getSession().
After we did the global setDefault it even switched the language and logging of our application server.
The Locale.setDefault(...) has no restrictions as to when or how many times it can be called. The change can be prevented by the security manager, but I assume it's not your case, since you don't mention any exceptions (just in case, though, verify if you don't hide SecurityException in a try-catch block).
Other reason why you might observe such behaviour, is that perhaps your application obtains the default locale only once, caches it and uses forever?
public String arabic_lng() {
// Add event code here...
FacesContext ha_faces= FacesContext.getCurrentInstance();
Locale ar = new Locale("ar","SA");
Locale.setDefault(new Locale ("ar","SA"));
System.out.println(ar.getDisplayName(ar));
ha_faces.getViewRoot().setLocale(ar);
return null ;
}
public String eng_lng() {
// Add event code here...
FacesContext ha_faces= FacesContext.getCurrentInstance();
Locale en = new Locale("en", "US");
System.out.println(en.getDisplayName(en));
ha_faces.getViewRoot().setLocale(en);
Locale.setDefault(new Locale ("en", "US"));
return null ;
}
Related
I'm working with the CachingSessionFactory and I was wondering what the property `setTestSession(boolean testSession) does and when to use it?
The docs are not telling a lot about what it does: https://docs.spring.io/spring-integration/api/org/springframework/integration/file/remote/session/CachingSessionFactory.html#setTestSession(boolean)
See SFTP docs: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#sftp-session-caching
Starting with version 5.1, the CachingSessionFactory has a new property testSession. When true, the session will be tested by performing a REALPATH command for an empty path to ensure it is still active; if not, it will be removed from the cache; a new session is created if no active sessions are in the cache.
The logic there in the cache is like this:
public boolean isStale(Session<F> session) {
return CachingSessionFactory.this.testSession ? !session.test() : !session.isOpen();
}
See SftpSession for implementation details.
Is it possible to extend the visibility time out of a message that is in flight.
See:
http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html.
Section: Changing a Message's Visibility Timeout.
http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/sqs/AmazonSQSClient.html#changeMessageVisibility-com.amazonaws.services.sqs.model.ChangeMessageVisibilityRequest-
In summary I want to be able to extend the first set visibility timeout for a given message that is in flight.
Example if 15secs have passed I then want to extend the timeout by another 20secs. Better example in java docs above.
From my understanding in the links above you can do this on the amazon side.
Below are my current settings;
SqsMessageDrivenChannelAdapter adapter =
new SqsMessageDrivenChannelAdapter(queue);
adapter.setMessageDeletionPolicy(SqsMessageDeletionPolicy.ON_SUCCESS);
adapter.setMaxNumberOfMessages(1);
adapter.setSendTimeout(2000);
adapter.setVisibilityTimeout(200);
adapter.setWaitTimeOut(20);
Is it possible to extend this timeout?
Spring Cloud AWS supports this starting with Version 2.0. Injecting a Visiblity parameter in your SQS listener method does the trick:
#SqsListener(value = "my-sqs-queue")
void onMessageReceived(#Payload String payload, Visibility visibility) {
...
var extension = visibility.extend(20);
...
}
Note, that extend will work asynchronously and will return a Future. So if you want to be sure further down the processing, that the visibility of the message is really extended at the AWS side of things, either block on the Future using extension.get() or query the Future with extension.isDone()
OK. Looks like I see your point.
We can change visibility for particular message using API:
AmazonSQS.changeMessageVisibility(String queueUrl, String receiptHandle, Integer visibilityTimeout)
For this purpose in downstream flow you have to get access to (inject) AmazonSQS bean and extract special headers from the Message:
#Autowired
AmazonSQS amazonSqs;
#Autowired
ResourceIdResolver resourceIdResolver;
...
MessageHeaders headers = message.getHeaders();
DestinationResolver destinationResolver = new DynamicQueueUrlDestinationResolver(this.amazonSqs, this.resourceIdResolver);
String queueUrl = destinationResolver.resolveDestination(headers.get(AwsHeaders.QUEUE));
String receiptHandle = headers.get(AwsHeaders.RECEIPT_HANDLE);
amazonSqs.changeMessageVisibility(queueUrl, receiptHandle, YOUR_DESIRED_VISIBILITY_TIMEOUT);
But eh, I agree that we should provide something on the matter as out-of-the-box feature. That may be even something similar to QueueMessageAcknowledgment as a new header. Or even just one more changeMessageVisibility method to this one.
Please, raise a GH issue for Spring Cloud AWS project on the matter with link to this SO topic.
In my current project we need to implement a way for texters to manage the wicket messages/internationalization via upload of property files.
Also see this question: Administrating internationalized wicket applications
As suggested there, I've implemented a custom IStringResourceLoader and added it at the beginning of the StringResourceLoader list to override any properties already in place:
getResourceSettings().getStringResourceLoaders().add(0, new CustomStringResourceLoader());
This however is not enough, because updates can happen and need to be loaded at runtime. StringResources are cached by wicket and updated only when the ResourceWatcher is triggered.
I found where Wicket adds the string resources to the watcher: the PropertiesFactory in the settings. The method to add a resource to the watcher is addToWatcher(...). However this method is protected and also the whole setup suggests this is used for development purposes and not for production.
I managed to use this method by extending PropertiesFactory and effectively creating a custom version to add to settings:
getResourceSettings().setPropertiesFactory(new CustomPropertiesFactory(getResourceSettings()));
getResourceSettings().setResourcePollFrequency(Duration.seconds(1));
So my Question is: I feel this is quite the circuitious solution. Is there another way to watch for changing properties files?
My solution to the problem:
getResourceSettings().getStringResourceLoaders().add(0, new CustomResourceLoader());
getResourceSettings().getResourceFinders().add(new Path("/pathToResources"));
getResourceSettings().setResourcePollFrequency(Duration.seconds(1));
This inserts my CustomResourceLoader at the beginning of the list so all properties are first checked there.
The added Path tells the PropertiesFactory to look for resources in a given arbitrary directory outside of wicket.
I needed custom names for my resource files as well, I realized this in the CustomResourceLoader:
public String loadStringResource(final Class<?> clazz, final String key, final Locale locale, final String style, final String variation) {
final String myResourceFilename = createCustomResourceFileName(locale);
final IPropertiesFactory pF = Application.get().getResourceSettings().getPropertiesFactory();
final org.apache.wicket.resource.Properties props = pF.load(clazz, myResourceFilename);
...
}
When using the PropertiesFactory to load the files, it adds them to the internal IModificationWatcher automatically.
It turns out that part of the problem was, that the resource files are in a non-standard encoding. This can be fixed by adding a special IPropertyLoader to the PropertiesFactory in the settings:
((PropertiesFactory) getResourceSettings().getPropertiesFactory()).getPropertiesLoaders().add(0,
new UtfPropertiesFilePropertiesLoader("properties", "your-favorite-encoding"));
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");
}
We have a site which will be used for two different clients. During first request the user will be asked to choose a client. Based on that text,labels and site content should be displayed.
Is it possible to have two messages file in Play framework and during session startup the messages file would be decided
As of my research we can have more than a file for each Locale, the messages will be get based on locale in the request.
No, it is not supported at the moment.
You can easily do that either in a plugin(Look at MessagesPlugin ) or even using a bootstrap job with the #onApplicationStartup annotation
// From MessagesPlugin.java
//default languange messages
VirtualFile appDM = Play.getVirtualFile("conf/messages");
if(appDM != null && appDM.exists()) {
Messages.defaults.putAll(read(appDM));
}
static Properties read(VirtualFile vf) {
if (vf != null) {
return IO.readUtf8Properties(vf.inputstream());
}
return null;
}
You can wrote you own PlayPlugin and handle implement play.PlayPlugin.getMessage(String, Object, Object...). Then you could choose the right file. The class play.i18n.Messages can be used as inspiration how to implement the method.
Solved this problem with below solution,
Created a class MessagesPlugIn which extends play.i18n.MessagesPlugin
Created a class Messages as like play.i18n.Messages
Had a static Map messaagesByClientID in Messages.java
Overridden onApplicationStart() in MessagesPlugIn
Loaded the Properties in messaagesByClientID as locales loaded in play.i18n.MessagesPlugin
Had a method get() in Messages.java, retrieve the property from messaagesByClientID based ClientId in the session. If the property is not available call get() in play.i18n.Messages
7.Created a Custom tag il8nTag and its used in HTML templates. il8nTag will invoke the methos in Messages.get().
Create your own Module based on play.api.i18n.I18nModule, but bound to your own implementation of MessagesApi, based on Play's DefaultMessagesApi (here is the part defining the files to load)
Then in your application.conf, disable Play's play.api.i18n.I18nModule and enable your own module.