I have a problem with Locale's. I'm trying to do a simple thing where I can choose english or german.
So I have two property files called
messages_en and
messages_de.
So far these files have a single line:
contactbook = Contact Book
and
contactbook = Adressbuch
respectively.
Now in my JSP view I have this :
<spring:message code="contactbook"/>
The idea is that the message in the view changes depending on which locale we're using. Now the locale itself should be changed with this line:
English|German
If I remove the everything works great but of course it's not locale specified. So the rest of the view is good.
In my dispatcher-servlet.xml file I have this:
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="language" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
Again, without it everything works well. There's something wrong with my understanding here. The localeChangeInterceptor stops any change done to the locale and the ControllerClassNameHandlerMapping does what? I keep on looking at my controller and wondering should I add something to it but it seems to me that all these lines in the dispatcher should be enough.
The exception I get is
No message found under code 'contactbook' for locale 'en'
Despite a myriad of examples out there I keep on failing to understand or resolve the problem. Any help would be greatly appreciated.
You need configure a ReloadableResourceBundleMessageSource.
look at java doc Here.
Example.
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!--classpath of your messages-->
<property name="basename" value="classpath:/i18n/messages" />
<property name="useCodeAsDefaultMessage" value="true" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
If you need working example of JavaConfig SpringMVC app I can help you with my code. I've been developing the same kind of simple app. Here is configuration of SpringMVC:
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/views/");
resolver.setSuffix(".jsp");
resolver.setContentType("text/html; charset=UTF-8");
return resolver;
}
#Bean(name = "messageSource")
public ReloadableResourceBundleMessageSource getReloadableResourceBundle(){
ReloadableResourceBundleMessageSource resourceBundle = new ReloadableResourceBundleMessageSource();
resourceBundle.setBasename("classpath:messages");
resourceBundle.setDefaultEncoding("UTF-8");
return resourceBundle;
}
#Bean(name = "localeResolver")
public SessionLocaleResolver getSessionLocaleResolver(){
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.ENGLISH);
return resolver;
}
#Bean(name = "localeChangeInterceptor")
public LocaleChangeInterceptor getLocaleChangeInterceptor(){
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("language");
return interceptor;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/css*").addResourceLocations("/resources/css/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getLocaleChangeInterceptor());
}
}
Also if it won't solve your problem and your config will be working fine, just try to put line spacer before the very first message in every .message file in your message bundle. (It helped me once when I've been getting the same exception you got and I don't know why, just saying ;P )
Related
I understand org.springframework.beans.factory.config.PropertyResourceConfigurer implements org.springframework.core.Ordered. See javadoc for setOrder method.
I am not sure how one can use the setOrder method for PropertyResourceConfigurer though.
Can I use setOrder for one PropertyResourceConfigurer to override properties from another PropertyResourceConfigurer? If setOrder is not meant to be used that way, then can someone please give me a practical use case for this method in the context of PropertyResourceConfigurer?
edit 1: Here are my PropertySourcesPlaceholderconfigurers:
#Configuration
public class PropertyConfigurerConfiguration {
static class defaultConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setOrder(35);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
#Profile(Profiles.DEV)
static class devConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setOrder(25);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/dev/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
...
And here is how I activate the profile: spring.profiles.active="dev"
edit 2:
Reading one of your comments about the bean IDs, I modified my code as follows:
#Profile(Profiles.DEV)
static class devConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer devPropertySourcesPlaceholderConfigurer() throws IOException {//Now called devProperXXX
and it appears it now works...
Can someone please explain to me why changing bean IDs would change the behavior? Is this documented somewhere? If so where?
Hopefully, I understood this correctly. Short answer is "yes".
If you have two beans defined like
<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<value>
p1=v1
p2=v2
</value>
</property>
<property name="order" value="25" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<bean id="mappings2"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<value>
p1=v111
p3=v3
</value>
</property>
<property name="order" value="35" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
"p1" property's value will be "v1" because the order of its PropertyPlaceholderConfigurer is lower (and thus of higher precedence).
A scenario where I can see this useful is with .jars that have their own configurers and their own .properties files. If these jars are searched for .xml configuration files (in classpath*:*.xml kind of style) I think one can override a property from one of the xml files by defining another propertyplaceholderconfigurer with a lower order value (by default the order is Integer.MAX_VALUE).
In my springbatch+quartz setup, I am reading a CSV File using FlatFileItemReader. I want to set the cursor for the reader to start the next jobinstance with the given parameters for reader. Is it possible?
<bean id="cvsFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<!-- Read a csv file -->
<property name="resource" value="classpath:cvs/input/report.csv" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="id,impressions" />
</bean>
</property>
<property name="fieldSetMapper">
<bean
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="report" />
</bean>
</property>
</bean>
</property>
</bean>
The idea is to continue reading the file where last failure occured in the next execution. I am putting an integer 'writecursor' for each line written in my customWriter.
public void write(List<? extends Report> items) throws Exception {
System.out.println("writer..." + items.size() + " > ");
for(Report item : items){
System.out.println("writing item id: " + item.getId());
System.out.println(item);
}
//getting stepExecution by implementing StepExecutionListener
this.stepExecution.getExecutionContext().putInt("writecursor", ++writecursor);
}
Now, in the customItemReadListener, I want to get the update writecursor value and then skip the lines from the top to start reading from writecursor
public class CustomItemReaderListener implements ItemReadListener<Report>, StepExecutionListener {
ApplicationContext context = ApplicationContextUtils.getApplicationContext();
private StepExecution stepExecution;
#Override
public void beforeRead() {
//Skip lines somehow
}
Another thing I saw as a possible solution is to set linestoskip dynamically in itemreader. There is a thread here http://thisisurl.com/dynamic-value-for-linestoskip-in-itemreader but not answered yet. And here,
http://forum.spring.io/forum/spring-projects/batch/100950-accessing-job-execution-context-from-itemwriter
Use FlatFileItemReader.linesToSkip property setted injecting job Parameter value.
<bean id="myReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="linesToSkip" value="file:#{jobParameters['cursor']}" />
</bean>
A more easy way for implementing the lines to skip is by the following:
create a reader flat file reader in the xml, autowire the reader to the beforeStep of step execution listener as shown below
public class CustomStepListener implements StepExecutionListener {
#Autowired
#Qualifier("cvsFileItemReader")
FlatFileItemReader cvsFileItmRdr;
#Override
public void beforeStep(StepExecution stepExecution) {
System.out.println("StepExecutionListener -------- beforeStep");
cvsFileItmRdr.setLinesToSkip(4);
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
System.out.println("StepExecutionListener - afterStep");
return null;
}
}
it is working fine for me.....
I have two lists generated with a FactoryBean and I would like to initialize a bean with the merged version of this two. Is there a way doing this?
<bean id="listA" class="XFactoryBean" >
...
</bean>
<bean id="listB" class="YFactoryBean">
...
</bean>
<bean >
<property name="AwithB" ...>
</bean>
There is a solution that works with static lists (http://vikdor.blogspot.hu/2012/10/using-collection-merging-in-spring-to.html), but does not work with those generated lists.
Java #Configuration FTW:
#Configuration
public class Config {
#Resource
private List listA;
#Resource
private List listB;
#Bean
public List AwithB() {
List mergedList = new ArrayList(listA);
listB.addAll(listB);
return mergedList;
}
}
Much less boilerplate.
There is a sublte bug with the ListMergerFactoryBean solution above.
Config:
<util:list id="listA" value-type="java.lang.String">
<value>fooA</value>
<value>barA</value>
</util:list>
<util:list id="listB" value-type="java.lang.String">
<value>fooB</value>
<value>barB</value>
</util:list>
<util:list id="listC" value-type="java.lang.String">
<value>fooC</value>
<value>barC</value>
</util:list>
<bean id="AwithB" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listB" />
</list>
</property>
</bean>
<bean id="AwithC" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listC" />
</list>
</property>
</bean>
With this config you might be surprised when bean AwithB has the expected content of AwithC. ListA is a singleton and the getObject method alters it, therefore alters it for all users of ListMergerFactoryBean, even though the bean factory is not a singleton. The fix is to not re-use the first list in the listOfLists:
#Override
public List getObject() throws Exception {
List mergedList = new ArrayList();
for (List list : listOfLists) {
mergedList.addAll(list);
}
return mergedList;
}
After checking the SpEL-related solutions (how to extend a list in spring config) and extending the ListFactoryBean (http://ericlefevre.net/wordpress/2008/04/02/merging-lists-in-a-spring-configuration-file/) I came up with the following solution:
config:
<bean id="AwithB" class="com.util.ListMergerFactoryBean">
<property name="listOfLists">
<list>
<ref bean="listA" />
<ref bean="listB" />
</list>
</property>
</bean>
java:
public class ListMergerFactoryBean implements FactoryBean<List> {
private List<List> listOfLists;
#Override
public List getObject() throws Exception {
List mergedList = new ArrayList();
for (List list : listOfLists) {
mergedList.addAll(list);
}
return mergedList;
}
#Override
public Class<?> getObjectType() {
return (new ArrayList()).getClass();
}
#Override
public boolean isSingleton() {
return false;
}
public void setListOfLists(List<List> listOfLists) {
this.listOfLists = listOfLists;
}
}
UPDATE: I have eliminated a bug using Aron Bartle's solution. Thanks!
I am using Spring 3.1.1 with Freemarker.
After I have successded to use the new concept of java based configuraion of the new spring 3.1.
Now I try to use Freemarker with it.
However,I got that exception:
javax.servlet.ServletException: Could not resolve view with name
'/welcome' in servlet with name 'appServlet'
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1139)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:927)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
Here is the WebConfig:
package com.springway.config;
#Configuration
#ComponentScan("com.springway")
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Inject
private Environment environment;
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJacksonHttpMessageConverter());
}
#Bean
public ViewResolver viewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(FreeMarkerView.class);
return viewResolver;
}
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath(
"/WEB-INF/freemarker.xml");
return configurer;
}
freemarker.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- freemarker config -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="freemarkerSettings">
<props>
<prop key="number_format">0.######</prop>
</props>
</property>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape"/>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
<!--
View resolvers can also be configured with ResourceBundles or XML files. If you need
different view resolving based on Locale, you have to use the resource bundle resolver.
-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
</bean>
</beans>
The controller :
#RequestMapping(value="/", method=RequestMethod.GET)
public ModelAndView home(Principal user) {
return new ModelAndView("/welcome.ftl");
}
I am struggling the same problem as yours. And I tried to turn on logging level "DEBUG" for "org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer". I found following message:
java.io.FileNotFoundException: ServletContext resource [/WEB-INF/freemarker/] cannot be resolved to URL because it does not exist
at org.springframework.web.context.support.ServletContextResource.getURL(ServletContextResource.java:154) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.web.context.support.ServletContextResource.getFile(ServletContextResource.java:169) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.ui.freemarker.FreeMarkerConfigurationFactory.getTemplateLoaderForPath(FreeMarkerConfigurationFactory.java:351) [spring-context-support-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.ui.freemarker.FreeMarkerConfigurationFactory.createConfiguration(FreeMarkerConfigurationFactory.java:304) [spring-context-support-3.1.1.RELEASE.jar:3.1.1.RELEASE]
.....
And a wired log from Jetty:
WARN org.eclipse.jetty.util.log - /freemarker/
I don't know what going on! So that I replaced the value of "templateLoaderPath" with another name, like "/WEB-INF/tmpl-freemarker/", then everything is workable!!
Next, I tried to use ServerContext.getResource() to test "/WEB-INF/freemarker/html.ftl". Here is the result:
Resource under /WEB-INF/freemarker/: null
My code of testing:
#Inject
private ServletContext servletContext;
try {
logger.info("Resource under /WEB-INF/freemarker/: {}", servletContext.getResource("/WEB-INF/freemarker/html.ftl"));
} catch(Exception e) {
logger.info("Exception while getResource", e);
}
It seems ServletContext can't find out the resource under "/WEB-INF/freemarker/" correctly either. But I'm not sure the object of ServletContext of Spring is as same as one on normal environment of Java Servlet.
I'm using FreeMarker 2.3.19, Springframework 3.1.1.RELEASE, and jetty-maven-plugin 7.4.5.v20110725 of Maven.
#Configuration
#EnableWebMvc
#ImportResource("classpath:/security-integration.xml")
#ComponentScan("com.springway")
public class WebConfiguration extends WebMvcConfigurerAdapter {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public FreeMarkerViewResolver viewResolver() {
FreeMarkerViewResolver freeMarkerViewResolver = new FreeMarkerViewResolver();
freeMarkerViewResolver.setCache(true);
freeMarkerViewResolver.setPrefix("");
freeMarkerViewResolver.setSuffix(".ftl");
return freeMarkerViewResolver;
}
#Bean
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/freemarker/");
Properties settings = new Properties();
settings.setProperty("number_format", "0.######");
freeMarkerConfigurer.setFreemarkerSettings(settings);
Map variables = new java.util.HashMap<String, Object>();
variables.put("xml_escape", new XmlEscape());
freeMarkerConfigurer.setFreemarkerVariables(variables);
return freeMarkerConfigurer;
}
}
You might have done wrong import
import org.springframework.web.portlet.ModelAndView;
instead of do this
import org.springframework.web.servlet.ModelAndView;
What is a good way to inject some file resource into Spring bean ?
Now i autowire ServletContext and use like below. Is more elegant way to do that in Spring MVC ?
#Controller
public class SomeController {
#Autowired
private ServletContext servletContext;
#RequestMapping("/texts")
public ModelAndView texts() {
InputStream in = servletContext.getResourceAsStream("/WEB-INF/file.txt");
// ...
}
}
Something like this:
#Controller
public class SomeController {
private Resource resource;
public void setResource(Resource resource) {
this.resource = resource;
}
#RequestMapping("/texts")
public ModelAndView texts() {
InputStream in = resource.getInputStream();
// ...
in.close();
}
}
In your bean definition:
<bean id="..." class="x.y.SomeController">
<property name="resource" value="/WEB-INF/file.txt"/>
</bean>
This will create a ServletContextResource using the /WEB-INF/file.txt path, and inject that into your controller.
Note you can't use component-scanning to detect your controller using this technique, you need an explicit bean definition.
Or just use the #Value annotation.
For single file:
#Value("classpath:conf/about.xml")
private Resource about;
For multiple files:
#Value("classpath*:conf/about.*")
private Resource[] abouts;
What do you intend to use the resource for? In you example you don't do anything with it.
From it's name, however, it looks like you are trying to load internationalisation / localisation messages - for which you can you a MessageSource.
If you define some beans (possibly in a separate messages-context.xml) similar to this:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>WEB-INF/messages/messages</value>
</list>
</property>
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en_GB" />
</bean>
Spring will load your resource bundle when you application starts. You can then autowire the MessageSource into your controller and use it to get localised messages:
#Controller
public class SomeController {
#Autowired
private MessageSource messageSource;
#RequestMapping("/texts")
public ModelAndView texts(Locale locale) {
String localisedMessage = messageSource.getMessage("my.message.key", new Object[]{}, locale)
/* do something with localised message here */
return new ModelAndView("texts");
}
}
NB. adding Locale as a parameter to your controller method will cause Spring to magically wire it in - that's all you need to do.
You can also then access the messages in your resource bundle in your JSPs using:
<spring:message code="my.message.key" />
Which is my preferred way to do it - just seems cleaner.