Change configuration on demand without restarting container - spring

Spring MVC + Java 8 + Tomcat 8 stack
I am maintaining my configuration in yaml and flattening the properties using Spring's PropertyPlaceholderConfigurer and maintaining the configuration in a bean.
Today, it has a inherent problem as I am required to restart the server whenever there is a change to the YML files.
I believe there are ways to refresh the bean without restart, but my main concern is how to do in fail safe manner.
Lets assume, there was a request and that time the config was A, and then we refresh the configuration so now its B, but if any subsequent user request was dependent on the configuration, then it will blow up.

Add this configuration to your servlet-context.xml to catch property changes on the fly:
<context:property-placeholder
location="file:${A_CONFIG_LOCATION}/configuration.properties" />
<beans:bean id="propertiesLoader"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="cacheSeconds" value="1" />
<beans:property name="basenames">
<beans:list>
<beans:value>file:${A_CONFIG_LOCATION}/configuration
</beans:value>
</beans:list>
</beans:property>
</beans:bean>
And then you can read property values like this:
#Component
public class PropertiesReader {
private String value = "some_default_value";
#Autowired
MessageSource propertiesLoader;
public String getValue() {
value = propertiesLoader.getMessage("configuration.value", null, null);
return value;
}
}

Related

Spring Batch BeanWrapperFieldExtractor for large number of fields

I am in process of writing a Spring Batch application that reads a CSV file, does some transforming and writes a modified CSV to be sent to another batch process.
My writer configuration looks like this:
<beans:property name="lineAggregator">
<beans:bean class="org.springframework.batch.item.file.transform.FormatterLineAggregator">
<beans:property name="fieldExtractor">
<beans:bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<beans:property name="names" value="column1, column2, column3, column4 ------ 322 fields " />
</beans:bean>
</beans:property>
<beans:property name="format" value="%-8s%-12s%-11s%-16s" ----322 fields />
</beans:bean>
</beans:property>
I have to write around 322 fields. I am unable to get any FormatterLineAggregator to do my work. If I write the format like
<property name="format" value="%s;%s;%s;%s;%s;%s;%s;%f;%f;%s;%f;%f;%td.%tm.%​tY;%td.%tm.%<‌​tY;%s;%td.%tm.%&‌​lt;tY;%s;%s;%s;%s;%t‌​d.%tm.%tY" /> ,
its getting really messy and its tough to make sure all fields are correct.
I thought of 3 different solutions:
Either go with the approach above.
Write a CustomEditorFieldsExtractor but don't know what to write in the class and how to format the fields (preferred).
Use a "non-standard" BeanIO framework jar but I fear my client won't agree to this solution.
Can someone please provide some inputs. Appreciate your help!
You can proceed with solution #2 in this way:
Externalize how to format every property of you bean class (eg, in XML or text file)
Write a custom LineAggregator and make it works coupled with directive at point 1
class Aggregator implements LineAggregator<T> {
private Map<String, String> propertyFormat;
public String aggregate(T item) {
final StringBuilder sb = new StringBuilder();
for(final String property : propertyFormat.keySet()) {
final String format = propertyFormat.get(property);
final Object propertyValue = /* Extract property from item using Spring beans */;
sb.append(String.format(format, propertyValue));
}
return sb.toString();
}
}

Spring resolve view name dynamically at runtime based on language

I have a web application where page is displayed in English language. There is a button on header section, clicking on it will change the page to Dutch language. (I will store it in cookie). For this I have to load different JSP, CSS, IMAGE folder. Like, I have below files in folder:
helloPortal.jsp
helloPortal_DU.jsp
I don't want to change each and every Controller class to check for language and set ModelView name accordingly. Instead I will say ModelAndView("helloPortal") and want Spring to do it from InternalResourceViewResolver or some other mechanism.
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean> here
I need another ViewResolver where based on language it Suffix _DU.jsp to file name
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value="_DU.jsp" />
</beans:bean>
I googled for this kind of solution but can't find precise solution. InternalResourceViewResolver is for properties file I believe. Is there a way based on cookie/locale change viewName dynamically.
Please let me know if I haven't explained my issue properly.
You could create custom view resolver. The resolver could check the cookies to figure out it needs to resolve to a Dutch version of the view, modify the view name appropriately and then delegate to normal view resolver that you are currently using.
Implementation could look something like this (I haven't tested it, but it should work):
public class DelegatingLocalizedViewResolver implements ViewResolver {
#Autowired
private HttpServletRequest httpServletRequest;
private ViewResolver delegateViewResolver;
#Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (isDutchLanguage()) {
return delegateViewResolver.resolveViewName(viewName + "_DU", locale);
}
return delegateViewResolver.resolveViewName(viewName, locale);
}
private boolean isDutchLanguage() {
Cookie[] cookies = httpServletRequest.getCookies();
// ... do cookie logic to figure out if the request is dutch
return true;
}
public void setDelegateViewResolver(ViewResolver delegateViewResolver) {
this.delegateViewResolver = delegateViewResolver;
}
}
XML config would then look something like this:
<beans:bean id="internalResourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean class="org.example.DelegatingLocalizedViewResolver">
<beans:property name="delegateViewResolver" ref="internalResourceViewResolver" />
</beans:bean>
PS. You might look into Spring's internationalization / localization support and leverage it instead of storing language in the cookie yourself (especially if you plan to support more languages in the future). Once Spring's localization support is enabled, you will get the country/language information in Locale object in the ViewResolver.

Grails initialization

In my Grails app, I need access to configuration exposed by a Java class similar to the below
public class Config {
private Properties properties = new Properties();
static load(String path) {
File configFile = new File(path);
FileReader fileReader = new FileReader(configFile);
properties.load(fileReader);
}
String getProperty(String name) {
properties.getProperty(name);
}
}
I trigger the initialisation of this class in the first line of Bootstrap.groovy by calling Config.load("/conf.properties"). However, the initialization of various Spring beans needs properties that are exposed by Config, but by the time Bootstrap.groovy is executed, Spring initialization has already completed.
So I need to find a way to call Config.load() before construction of the Spring beans, is this possible? I guess there might be an event handler available in /script/_Events.groovy that I could invoke it from, but I'm not sure which handlers are available.
Unfortunately, changing the source code of Config.java isn't an option, and neither is eliminating my usage of this class.
You could try declaring a suitable bean in web-app/WEB-INF/applicationContext.xml, which is the definition of the root web application context as opposed to the GrailsApplication's internal context.
<bean id="initConfig" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="com.example.Config" />
<property name="targetMethod" value="load" />
<property name="arguments">
<list><value>/conf.properties</value></list>
</property>
</bean>
and modify the grailsApplication bean to depend on that:
<bean id="grailsApplication" depends-on="initConfig" class="...">

Spring MVC custom errors and internationalization

In my web application, I handle errors with annotations. Everything works fine and I can use custom messages via the "message" parameter.
#Digits(fraction = 0, integer = 3, message="my custom error message...")
private String price;
Now I'm trying to internationalize this message with a .properties files, but I certainly miss something and I can't get it to work.
My spring config :
<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basenames" value="classpath:i18n/messages, classpath:i18n/errors" />
<beans:property name="defaultEncoding" value="UTF-8" />
</beans:bean>
<beans:bean name="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<beans:property name="validationMessageSource">
<beans:ref bean="messageSource" />
</beans:property>
</beans:bean>
<beans:bean id="localeResolver" class="org.springframework.web.servlet.i18n.FixedLocaleResolver">
<beans:property name="defaultLocale" value="fr" />
</beans:bean>
My new bean :
#Digits(fraction = 0, integer = 3)
private String price;
My "errors_fr.properties" file. I've already tried everything :
Digits.myBean.myNestedBean.price = my custom error message...
Digits.myNestedBean.price = my custom error message...
javax.validation.constraints.Digits.myNestedBean.price = my custom error message...
I always get the same generic message from spring, it's like as spring doesn't detect my .properties file. By the way, the message keys above can be found in the BindingResult object when debugging.
What am I missing here ?
Notice that I already have internationalized messages in my jsp's (in the "messages_fr.properties" file) and they work fine.
I had a similar problem in my application, and I hope that this can help you.
As discussed in this thread, http://forum.springsource.org/showthread.php?73240-Roo-JSR-303-Validations-and-Localization, you need to:
define the error messages referred by the annotation in file ValidationMessages.properties
in your annotation, refer to the error message key enclosed in curly brackets:
#Digits(fraction = 0, integer = 3, message="{message.key}")
Hope this helps.

spring 3 my converter is not used

...but registered
Using Spring 3
I have two converters registered as follows:
<beans:bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<beans:property name="converters">
<beans:list>
<beans:bean class="mypackage.CalendarToStringConverter" />
<beans:bean class="mypackage.StringToCalendarConverter" />
</beans:list>
</beans:property>
</beans:bean>
The converters look like this:
public class StringToCalendarConverter implements Converter< String, Calendar > {
public Calendar convert( String value ) {
return Calendar.getInstance();
}
}
public class CalendarToStringConverter implements Converter< Calendar, String > {
public String convert( Calendar arg0 ) {
return "23.10.1985";
}
}
The problem is that they are not used during conversion in post and get requests.
What am I doing wrong?
What do I havt to do to get this working?
THX!
Are you using <mvc:annotation-driven> and if so, are you pointing to conversionService in the conversion-service attribute?
Here's the converters configuration that works for me. The differences you might try changing:
I pass in a set instead of a list. (setConverters takes a Set parameter)
I use FormattingConversionServiceFactoryBean instead of ConversionServiceFactoryBean. (Should not matter)
My converters are defined as top level beans and referenced. (Also should not matter)
Hopefully one of this will fix your problem.
<util:set id="converters" >
<ref bean="userDao" />
<ref bean="orderDao" />
<util:set>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters" ref="converters"/>
</bean>

Resources