I have the Enum:
public enum EmployeeErrorCode {
DELETE_SUCCESS,
//... Other enumerators
#Override
public String toString(){
ApplicationContext ctx = ContextLoader
.getCurrentWebApplicationContext();
MessageSource messageSource = (MessageSource) ctx
.getBean("messageSource"); //How to avoid this?
switch (this) {
case DELETE_SUCCESS:
return messageSource.getMessage("deleteEmployee.success",
null, LocaleContextHolder.getLocale());
//... Other cases
default:
return null;
}
}
}
In the toString nethod I specified the messages for any Enumerator, but I used getBean method to programmatically get the appropriate bean. How can I avoid that?
I tried to inject the bean via
#Autowired
MessageSource messageSource;
but it didn't work. In fact, messageSource was just null. Is there a way to do that corretly at all?
If MessageSource is a bean that opens a properties file, then for example if your properties file is called Messages.properties, then you can use
ResourceBundle bundle = ResourceBundle.getBundle("Messages", LocaleContextHolder.getLocale());
String message = bundle.getString("deleteEmployee.success");
EDIT: Another possible method is to inject the MessageSource into your enums (idea from my solution at Java Generics and Enum, loss of template parameters ), like so:
public enum EmployeeErrorCode {
DELETE_SUCCESS {
#Override
public String toString() {
return messageSource.getMessage("deleteEmployee.success", null, LocaleContextHolder.getLocale());
}
},
//... Other enumerators
private MessageSource messageSource;
static class EnumInitializer {
#Autowired
private MessageSource messageSource;
#PostConstruct
public void init() {
for(EmployeeErrorCode errorCode : EmployeeErrorCode.values() {
errorCode.messageSource = getMessageSource();
}
}
public MessageSource getMessageSource() {
return messageSource;
}
}
}
But I think the other one is a bit cleaner.
Related
private String message;
#Autowired
private CustomMessageSource messageSource;
#PostConstruct
public void postConstruct() {
message= messageSource.getMessage("warning-message");
logger.log(message);
}
#Component
public class CustomMessageSource extends ReloadableResourceBundleMessageSource {
#Autowired
private ApplicationContext ctx;
public CustomMessageSource() {
Locale.setDefault(Locale.ENGLISH);
}
public String getMessage(String key) {
return ctx.getMessage(key, new Object[] { }, getCurrentLocale());
}
}
in above code , when #PostConstruct is getting executed,I am getting error:
messagesource not initialized - call 'refresh' before accessing
messages via the context failing the applicationContext loading
warning-message is a key in messages.properties file.
On debugging I have found that even though messageSource object is created but probably messages have not loaded causing this error.
if instead of post construct,I use it directly in the execution methods,it works correctly.
Can some one please throw some light on this if this is even possible or may be I am doing anything wrong?
I want to use localization to localize the Swagger Documentation. But I can only provide compile time constants to Annotations. So I'm confused how to provide read messages from messages_**.properties and provide it to annotations.
Message Source:
#Configuration
public class CustomMessageSourceConfig {
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocalValidatorFactoryBean getValidator() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.ENGLISH);
return slr;
}
}
Reading messages from messages_**.properties:
#Component
public class MessagesByLocaleServiceImpl implements MessagesByLocaleService {
#Autowired
private MessageSource messageSource;
#Override
public String getMessage(String id) {
Locale locale = LocaleContextHolder.getLocale();
return StringEscapeUtils.unescapeJava(messageSource.getMessage(id, null, locale));
}
}
Here is how I'm reading messsages in Java Code:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot"))).build()
.apiInfo(apiInfo())
.tags(new Tag("Netmap Mode Service", messageSource.getMessage(MessageCodes.SWAGGER_WINDOWS_ONLY)));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title(messageSource.getMessage(MessageCodes.SWAGGER_TITLE))
.description(messageSource.getMessage(MessageCodes.SWAGGER_DESCRIPTION))
.contact(messageSource.getMessage(MessageCodes.SWAGGER_CONTACT)).build();
}
But how can I provide these messages to Swagger Annotations.
#ApiOperation(value = "Add Netmap mode ", notes = "**I want to read properties here**")
#ApiImplicitParams({
#ApiImplicitParam(value = SwaggerSinglePoint.DESC_MODE_NAME, dataType = CDSwaggerPrimitives.STRING, name = SwaggerSinglePoint.MODE_NAME, paramType = CDSwaggerPrimitives.PARAMA_TYPE_QUERY),
#ApiImplicitParam(value = SwaggerSinglePoint.DESC_MODE_BUFFER_SIZE, dataType = CDSwaggerPrimitives.INETEGER, name = SwaggerSinglePoint.BUFFER, paramType = CDSwaggerPrimitives.PARAMA_TYPE_QUERY)})
#RequestMapping(method = RequestMethod.POST, produces = CDConstants.JSON_RESPONSE_DATA_FORMAT, consumes = CDConstants.JSON_REQUEST_DATA_FORMAT)
#SuppressWarnings({ "squid:S3776", "squid:S1319", "unused" })
public String testController(#RequestBody(required = false) HashMap requestParamMap, HttpServletResponse response,
HttpServletRequest request) {
I want to read messages in these annotations. Any guidance or suggestions would be highly appreciated.
It's always better to decouple your documentation comments from your code (reading text from external property file rather than ineserting as plain text)
Use the placeholder like so,instead of
#ApiOperation(value = "Add Netmap mode " ,...)
use
#ApiOperation(value = ${message.addNetMode} ,...)
Here inside "messages_**.properties" file there should be key-value pair
message.addNetMode=Add Netmap mode
Also register the property file in your configuration on class level
#PropertySource("classpath:messages_**.properties")
**Note that values for some annotations might not be supported.Refer docs http://springfox.github.io/springfox/docs/current/#support-for-documentation-from-property-file-lookup
You can get values from application.properties by using SPEL i.e. ${}:
#Annotation(value="${request.reminder.mails.cron.expression}")
Note:- the props name should be complete name from application.properties.
How can I conditionally create a Bean given a situation where the String is null?
The following example would cause an error, but I would like to somehow prevent it from happening by only creating beans when the string being assessed is not empty.
public class MyAppContext
#Value("${this.string.is.null}")
private String nullString;
#SupressWarnings("SpringJavaAutowiringInspection")
#Bean
public MessageListenerContainer myQueue() {
bean.setDestinationName(nullString)
}
You could use the #ConditionalOnExpression annotation:
#Bean
#ConditionalOnExpression("'${this.string.is.null}'!=null")
public MessageListener myQueue() {
bean.setDestinationName(nullString)
}
Or create a custom condition:
public class MyPropNotNull implements Condition {
public MyPropNotNull() {}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.getProperty("this.string.is.null") != null;
}
}
#Bean
#Conditional(MyPropNotNull.class)
public MessageListener myQueue() {
bean.setDestinationName(nullString)
}
Update: if having a bean of this type is mandatory, don't forget to add a fallback bean. Example:
#ConditionalOnMissingBean
#Bean
public MessageListener useThisOneWhenTheOtherIsMissing() {
// this bean will be used when the other one is not available
// ...
}
try https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/condition/ConditionalOnProperty.html
#ConditionalOnProperty(value = "propertyName")
#Bean
public MessageListener myQueue() {
bean.setDestinationName(nullString)
}
I use the next code
#Value("${app.user.root}")
private String userRoot;
to get constant value from my application.properties file.
In my GetMapping method I need to redirect to the error page and to pass a String as parameter.
#GetMapping("/user/activate")
public String activate(String activation) {
Users u = usersService.activate(activation);
if (u != null) {
usersService.autoLogin(u);
return "redirect:/";
}
return "redirect:/error?message=Could not activate with this activation code, please contact support";
But I need to have different String values with different languages. So, I am using Spring i18n, but how can I get the value I need at runtime? I need something like this:
return "redirect:/error?message=${errorMessage}";
Thank you, hope you will help me.
First you have to create multiple properties file for multiple languages
messages_en.properties
messages_fr.properties
The configuration of i18n should be following
#Configuration
public class LanguageConfig extends WebMvcConfigurerAdapter {
#Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SmartLocaleResolver slr = new SmartLocaleResolver();
Locale locale = new Locale("en", "us");
slr.setDefaultLocale(locale); // Set default Locale as en_cos
return slr;
}
#Bean
public LocaleChangeInterceptor localeInterceptor() {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang");
return interceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeInterceptor());
}
class SmartLocaleResolver extends CookieLocaleResolver {
#Override
public Locale resolveLocale(HttpServletRequest request) {
String acceptLanguage = request.getHeader("Accept-Language");
if (acceptLanguage == null || acceptLanguage.trim().isEmpty()) {
return super.determineDefaultLocale(request);
}
return request.getLocale();
}
}
}
Now update your controller code and autowire org.springframework.context.MessageSource and then use it to get localized message.
#Autowired
private MessageSource messageSource;
Then you can get the localized message using following code.
String errorMessage = messageSource.getMessage("project.errorMessage", new Object[]{"John Doe"}, LocaleContextHolder.getLocale());
You can also use Locale object from controller method parameter instead of LocaleContextHolder.getLocale(), but it works just fine.
I followed some tutorials like this one:
http://www.concretepage.com/spring-4/spring-4-mvc-internationalization-i18n-and-localization-l10n-annotation-example
to get i18n work in my spring(boot) project.
It's working so far, but I don't like it to always autowire the MessageSource bean to my current class, just to get some Strings translated.
My idea is a easy wrapper class with a static call like
I18n.translate("some.identifier")
or
I18n.translate("some.identifier",param,param,param...).
But I can't inject the MessageSource into a class which is not handled by Spring, huh?
Any idea how to solve this?
You could (not sure if you should) do something like this:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component
public class StaticContextAccessor {
private static StaticContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> clazz) {
return instance.applicationContext.getBean(clazz);
}
}
and then use it like this:
SomeOtherwiseAutowiredClass someObject = StaticContextAccessor.getBean(SomeOtherwiseAutowiredClass.class);
I had the same Idea as I develop also with Vaadin and the solution which works for me is to use a StaticContextInitializer Bean. Therefore do the following:
First make your field for the Message class static and provide the getter and setter methods:
private static MessageSource messageSource;
private TextSource() {
}
public static String getText(String key, Locale locale) {
return messageSource.getMessage(key,null, ensureLocale(locale));
}
public static String getText(String key, Locale locale, Object[] parameter) {
return messageSource.getMessage(key, parameter, ensureLocale(locale));
}
private static Locale ensureLocale(Locale locale) {
if (locale == null)
locale = Locale.getDefault();
return locale;
}
Add the static setter method to the class:
public static void setMessageSource(MessageSource messageSource) {
TextSource.messageSource = messageSource;
}
Write your StaticInitializer Bean with a #PostConstruct annotation and inject the MessageSource with #Autowired annotation.
#Component
public class FrontendStaticContextInitializer {
#Autowired
private MessageSource messageSource;
#PostConstruct
public void initialize() {
TextSource.setMessageSource(messageSource);
}
}
After that you'll be able to call the class like that in your Views: TextSource.getText("login.textfield.placeholder.benutzername", getLocale())
You have two possibilities:
Non-static setter for static property/field;
Using org.springframework.beans.factory.config.MethodInvokingFactoryBean to invoke a static setter.
Examples you can find follow by link
How to make spring inject value into a static field
P.S. What is problem with autowire MessageSource into your beans?
Create a static message source wrapper, for example like https://github.com/chelu/jdal/blob/master/core/src/main/java/org/jdal/beans/StaticMessageSource.java
and declare it in bean configuration file:
<!-- Message Source -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="i18n/jdal,i18n/i18n" />
</bean>
<bean id="staticMessageSource" class="org.jdal.beans.StaticMessageSource">
<constructor-arg ref="messageSource" />
</bean>
Note that you can use depend-on if you need that the wrapper is instantiated before some other bean.
Just implement MessageSourceAware and set it to the static variable.
But, it will only work if spring.main.lazyInitialization is false.
#Component
public final class MessageUtils implements MessageSourceAware {
private static MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
MessageUtils.messageSource = messageSource;
}
public static String getMessage(String key, String... params) {
return messageSource.getMessage(key, params, new Locale("PT", "br"));
}
}
If spring.main.lazyInitialization is true, will need to force the injection:
public class App {
#Autowired
private MessageUtils messageUtils;
public static void main(String[] args) {
SpringApplication.run(App .class, args);
}
}
Spring will automatically inject, see class: ApplicationContextAwareProcessor.invokeAwareInterfaces(Object bean)
here is a sample Utils, without DI concept, without Bean configuration:
import java.util.Locale;
import org.apache.commons.lang3.Validate;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
public class I18n {
private static MessageSource MESSAGE_SOURCE;
static {
I18nUtils.MESSAGE_SOURCE = messageSource();
}
public static String translate(Locale locale, String key, Object... args) {
Validate.notNull(locale, "locale require not null");
Validate.notEmpty(key, "key require not empty");
return MESSAGE_SOURCE.getMessage(key, args, locale);
}
private static MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.addBasenames("classpath:Messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}