I am trying to use Spring's ReloadableResourceBundleMessageSource for LocalValidatorFactoryBean so that when I update an error message it should reflect without requiring the server to be restarted. I am using Spring 4.1.4, hibernate-validator 4.3.2.Final.
Below are the code details -
context.xml -
<mvc:annotation-driven validator="validator" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:../conf/fileapplication</value> <!-- Messages here will override the below properties file-->
<value>/WEB-INF/application</value>
</list>
</property>
<property name="cacheSeconds" value="10"></property> <!-- Will check for refresh every 10 seconds -->
</bean>
<bean name="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource">
<ref bean="messageSource"/>
</property>
</bean>
Model -
import org.hibernate.validator.constraints.NotBlank;
public class InputForm {
#NotBlank ( message = "{required.string.blank}")
String requiredString;
Controller -
#RequestMapping(value = "/check/string", method = RequestMethod.POST)
public String checkString(
#ModelAttribute("formModel") #Valid InputForm inputForm ,
BindingResult result, Model model, HttpServletResponse response,
HttpServletRequest request) {
if (result.hasErrors()) {
model.addAttribute("formModel", inputForm);
return "userInput";
}
// Do some backend validation with String
result.reject("string.not.valid",
"String is Invalid");
model.addAttribute("formModel", inputForm);
return "userInput";
}
application.properties (in /WEB_INF/ folder)
required.string.blank=Please enter the required string.
string.not.valid=Please enter a valid string.
fileapplication.properties (in /conf/ folder. Will override above file)
required.string.blank=You did not enter the required string. #Does not reflect when I change here
string.not.valid=You did not enter a valid string. #Reflects when I change here
Now the problem I am facing is, when I update "string.not.valid" in fileapplication.properties it reflects at runtime and I see the updated message. But when I update "required.string.blank" in fileapplication.properties it does not reflect at runtime.
Note that the overriding part is working fine for both messages upon application start up. But the "reloading" part is not working fine for "required.string.blank".
This is what I figured out based on my research - We need to create our own MessageInterpolator and add it as dependency to the validator instead of message source. Because when we add a messageSource as dependency, it is cached by default by the validator and any message reloads spring does won't take effect in the validator's cached instance of messageSource.
Below are the details:
In context.xml, add the custom MessageInterpolator as dependency to LocalValidatorFactoryBean instead of messageSource:
<mvc:annotation-driven validator="validator" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:../conf/fileapplication</value> <!-- Messages here will override the below properties file-->
<value>/WEB-INF/application</value>
</list>
</property>
<property name="cacheSeconds" value="10"></property> <!-- Will check for refresh every 10 seconds -->
</bean>
<bean name="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="messageInterpolator">
<ref bean="messageInterpolator"/>
</property>
</bean>
<bean name="messageInterpolator"
class="com.my.org.support.MyCustomResourceBundleMessageInterpolator">
<constructor-arg ref="messageSource" />
</bean>
Create your custom MessageInterpolator by extending Hibernate's org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.
public class MyCustomResourceBundleMessageInterpolator extends
ResourceBundleMessageInterpolator {
public MyCustomResourceBundleMessageInterpolator(MessageSource messageSource)
{
// Passing false for the second argument
// in the super() constructor avoids the messages being cached.
super(new MessageSourceResourceBundleLocator(messageSource), false);
}
}
Model, Controller and properties file can be same as in the question.
Related
I'm trying to return JSON when using the #RestController. I'm using Spring 4.1.
Here's the exception I'm getting when calling listrestsites.html using a GET request.
I have the fasterxml Jackson core and databind jars in my build path.
Output of the accept from #requestheader = accept: application/json, text/javascript, /; q=0.01
Any help is appreciated. Thank you,
[DEBUG,ExceptionHandlerExceptionResolver] Resolving exception from
handler [public java.util.List
com.amci.spring3.controller.SitesRestController.listRestSites(java.lang.String)]:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not
find acceptable representation [DEBUG,DefaultListableBeanFactory]
Returning cached instance of singleton bean
'exceptionControllerAdvice' [DEBUG,ExceptionHandlerExceptionResolver]
Invoking #ExceptionHandler method: public
org.springframework.web.servlet.ModelAndView
Here's my Restcontroller class:
#RestController
public class SitesRestController {
#Autowired
private AssetService assetService;
#RequestMapping("/listrestsites.html")
public List<Asset> listRestSites(#RequestHeader(value="accept") String accept) {
System.out.println(getLogLevel());
System.out.println("accept: " + accept);
return assetService.findAssets();
}
}
Also, snippet from my spring.xml:
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
Please make sure that you have the following in your Spring xml file:
<context:annotation-config/>
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
and all items of your POJO should have getters/setters. Hope it helps
Credit answer to this question
I am using Spring MVC and Tiles.
When I try to use spring prefix "redirect:" for the redirection I get a servlet exception
- "Could not resolve view...".
Controller
RequestMapping("/SUPPORT.lz")
public String generateSupportView(HttpServletRequest request)
{
String url="http://"+request.getServerName()+":"+request.getServerPort()+"/APP";
AuthenticatedUser user = getUser();
/*
* Check if a user has the role for the page he is trying to access. If
* not redirect him to default page (home page).
*/
if (user.getRoles() != null && !user.getRoles().contains(new Role(0, Role.RoleType.SUPPORT)))
{
return "redirect:"+url;
}
return "support.tiles";
}
dispatcher-servlet.xml
<bean id="tilesViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver"
p:order="1"
p:viewNames="*.tiles"
p:viewClass="org.springframework.web.servlet.view.tiles2.TilesView"/>
<bean id="nontilesViewResolver"
class="org.springframework.web.servlet.view.XmlViewResolver"
p:order="2"
p:location="/WEB-INF/app-nontiles-views.xml"/>
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer"
p:definitions="/WEB-INF/app-tiles.xml"/>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="i18n"/>
Solution-
I added internal view resolver in disaptacher servlet.xml and issue got resolved.
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:order="3">
</bean>
I have a bean as follows:
<bean id="myBean" class="MyBeanClass">
<constructor-arg value="\WEB-INF\myfile.dat"/>
</bean>
In the bean's contructor, I need to build the file's full path. To do that, I have to first find the app's root path first.
Thanks and regards.
Update
Per Michael-O's suggestion, here is my solution (so easy).
Spring bean:
<bean id="myBean" class="MyBeanClass">
<constructor-arg value="/myfile.dat"/> <!--under WEB-INF/classes-->
</bean>
Java:
public MyBeanClass(String path) throws Exception {
ClassPathResource file = new ClassPathResource(path);
lookup = new LookupService(file.getFile().getPath(), LookupService.GEOIP_MEMORY_CACHE);
}
Michael, thanks!!!
Use Spring's Resource class in your bean and spring will do the rest for you.
After seeing #curious1's edit, there is a better solution to his answer. Please do not use that. Go with this one:
beans.xml:
<!-- START: Improvement 2 -->
<context:annotation-config />
<bean id="service" class="LookupService">
<constructor-arg value="classpath:/myfile.dat"/> <!--under WEB-INF/classes-->
<constructor-arg>
<util:constant static-field="LookupService.GEOIP_MEMORY_CACHE"/>
</constructor-arg>
</bean>
<!-- END: Improvement 2 -->
<!-- Spring autowires here -->
<bean id="myBean" class="MyBeanClass" />
<!-- START: Improvement 1 -->
<bean id="myBean" class="MyBeanClass" />
<constructor-arg value="classpath:/myfile.dat"/> <!--under WEB-INF/classes-->
</bean>
<!-- END: Improvement 1 -->
Java:
public MyBeanClass(Resource path) throws Exception {
lookup = new LookupService(path.getInputStream(), LookupService.GEOIP_MEMORY_CACHE);
}
This is source-agnostic, does not rely on files and is the Spring way.
Edit 2: Rethinking my code, it can be even better:
public class MyBeanClass {
#Autowired
LookupService service;
}
and configure LookupService in your beans.xml.
Maybe you should consider using:
getClass().getClassLoader().getResourceAsStream()
inside constructor. This will use your classpath, so you "WEB-INF\myfile.dat", will be visible. Next think is use resource directory to put all resources in one directory (default: under root directory in WAR file)
Hi I used Internal resource view resolver and my structure of jsp are like
jsp/adm, jsp/icon, jsp/iload, like
and my annotation based controller will return string value based on condition
my problem is jsp uder sub-folder is not resoled but it is under jsp folder is working
could any one please help me in this
he is my code:`
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
my controller code is
#RequestMapping("/icrl")
public String search() throws Exception
{
setMenuName(CommonConstants.ICRL);
return "pgiCRL";
}
#RequestMapping("/searchCodes")
public String searchCodes() throws Exception
{
String key=getSearchKey();
String query=getQuery();
Map<String, Object> searchKeys = new HashMap<String, Object>();
ArrayList<String> list=new ArrayList<String>();
if(query!=null||!query.isEmpty()){
searchKeys.put(CommonConstants.DIAGICD9, getDiaICD9());
searchKeys.put(CommonConstants.DIAGICD10, getDiaICD10());
searchKeys.put(CommonConstants.DIAGNOSIS, getDiagnosis());
searchKeys.put(CommonConstants.PROCEDURE, getProcedure());
searchKeys.put(CommonConstants.SURGICAL, getSurgical());
searchKeys.put(CommonConstants.SURGICAL9, getSurICD9());
searchKeys.put(CommonConstants.SURGICAL10, getSurICD10());
searchKeys.put(CommonConstants.REVENUE, getRevenue());
list= (ArrayList<String>) iCRLService.getSearchCodeList(query,searchKeys);
}
setSuggestions(list);
return CommonConstants.SUCCESS;
}
my view is depending on condition it may be success page and failure page so i need to fix the return value in controller because that is dynamic.
Thanks in advance
You need to define the subfolder name in the returning string value.
For Example, if your page "pgiCRL" is in admin subfolder then return "admin/pgiCRL".
I have a fairly simple task that I want to accomplish, but can't seem to find information for Spring MVC routing about it. I have a very simple controller that routes a path to a view:
#Controller
#RequestMapping(value = "/help")
public class HelpController {
private static final String HELP = "help";
#RequestMapping(method = RequestMethod.GET)
public String help(Model model, Locale locale) {
model.addAttribute("locale", locale);
return HELP;
}
}
I would like to throw a 404 if http://mysite.com/help.some.extension.is.entered, but Spring seems to resolve the example to /help. The javadoc says that the #RequestMapping annotation is just a servlet URI mapping, but I thought /help means it needs to be an exact match. Any clarification would be appreciated.
For Spring 4 it's pretty easy to solve:
<mvc:annotation-driven>
<mvc:path-matching suffix-pattern="false" />
</mvc:annotation-driven>
So you still can use mvc:annotation-driven for your config.
You can mention it in the #RequestMapping annotation
it is same as Servlet URL pattern only.
#Controller
public class HelpController {
private static final String HELP = "help";
#RequestMapping(value = "/help" method = RequestMethod.GET)
public String help(Model model, Locale locale) {
model.addAttribute("locale", locale);
return HELP;
}
#RequestMapping(value = "help/*" method = RequestMethod.GET)
public String helpWithExtraWords() {
return "error";
}
}
The best way I can think of is to configure your RequestMappingHandlerMapping explicitly to not consider suffixpaths, this way:
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useSuffixPatternMatch" value="false"></property>
</bean>
However, if you have configured your Spring MVC using mvc:annotation-driven, this will not work, you will have to expand out the entire handlerAdapter definition, which is not that difficult to do, along these lines(this is not complete, you can look through org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser for the entire definition):
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useSuffixPatternMatch" value="false"></property>
</bean>
With Spring 3.0.X You can use the useDefaultSuffixPattern property.
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
You will need to remove </mvc:annotation-driven>
Refer URL Pattern Restricting in SPRING MVC