Having to use deprecated classes with Spring MVC - spring

Have been trial Spring Web MVC (4.2.5) and have his a number of issues trying to use a DispatcherServlet and
<mvc:annotation-driven />
Have setup a simple #Controller class and wanted to use the POJO to JSON mapping. The docu said that if Jackson was detected on the class path it would be used automatically, however this didn't work for me and I was forced to use the 'deprecated' AnnotationMethodHandlerAdapter
<bean name="mappingJackson2HttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" >
<property name="messageConverters" ref="mappingJackson2HttpMessageConverter"/>
</bean>
which then worked fine.
Equally, tried to create a #ControllerAdvice class for handling all exceptions, but only got an #ExceptionHandler method working on the same controller class, and that was only when I added the (again) deprecated AnnotationMethodHandlerExceptionResolver to the context.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" />
Having to instantiate two deprecated classes suggests I am doing something wrong, especially when all the tutorials seem to suggest this should all 'just work', but I cannot see what (and indeed nosing through the Spring source I cannot see how the default and recommended handlers would work anyway)
There are no errors, the annotation simply aren't detected. The fill context xml is
please find the entire context XML below (is very simple)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven enable-matrix-variables="true"/>
<bean name="mappingJackson2HttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" >
<property name="messageConverters" ref="mappingJackson2HttpMessageConverter"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" />
<context:component-scan base-package="com.domain.datastore.dao"/>
<context:component-scan base-package="com.domain.service"/>
<context:component-scan base-package="com.domain.uiapi"/>
</beans>
An example controller is
#RestController("/place/*")
public class PlaceController {
private PlaceService placeService;
#Autowired
public PlaceController(PlaceService placeService) {
this.placeService = placeService;
}
#RequestMapping(path="/{id}", method = RequestMethod.GET)
public #ResponseBody Place getPlace(#PathVariable("id") long id, Model model) {
return placeService.getPlace(id);
}
}
and the cross-cutting exception handler is
#ControllerAdvice
public class GlobalExceptionController {
public GlobalExceptionController() {
System.out.println("GlobalExceptionController");
}
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(NotFoundException.class)
public ModelAndView handleCustomException(NotFoundException ex) {
return null;
}
}

The issue was that Spring MVC was matching the path in
#RestController("/place/*")
And as such passing the instance of PlaceController around as the handler. The ExceptionHandlerExceptionResolver expects a HandlerMethod and so was unable to process the exception.
As such dropping the path from the class annotation and putting the full path in the method got it all working and I dropped all the deprecated beans.
#RestController
public class PlaceController {
#RequestMapping(path="/place/{id}", method = RequestMethod.GET)
public #ResponseBody Place getPlace(#PathVariable("id") long id, Model model)
What I am not sure is if this is a bug. Shouldn't it be possible to put the 'base' path in the RestController annotation and the subpath in the RequestMapping?

As far as I can understand you don't want to use a deprecated class. AnnotationMethodHandlerAdapter is indeed Deprecated. As doc suggest you should use RequestMappingHandlerAdapter instead.
See here for the details.
And instead of AnnotationMethodHandlerExceptionResolver you can use ExceptionHandlerExceptionResolver.

Related

Spring MVC validation ignored

I have Spring MVC app on Tomcat. I'm trying to add validation in it.
For some reason my validation is ignored and not checked.
Here is my POJO
import org.hibernate.validator.constraints.Range;
public class Goal {
#Range(min = 1, max = 15)
private int minutes;
public int getMinutes() {
return minutes;
}
public void setMinutes(int minutes) {
this.minutes = minutes;
}
}
And controller
import org.springframework.validation.BindingResult;
import javax.validation.Valid;
#Controller
#SessionAttributes("goal")
public class GoalController {
#RequestMapping(value = "addGoal", method = RequestMethod.POST)
public String updGoal(#ModelAttribute("goal") #Valid Goal goal, BindingResult result) {
System.out.println(goal.getMinutes());
System.out.println(result.hasErrors());
if(result.hasErrors()) {
return "addGoal";
}
return "redirect:addMinutes.html";
}
}
But even if I try to put any negative values as "minutes" there no errors produced.
Added configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.xlab.ice.mvc.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
<mvc:resources mapping="/pdfs/**" location="pdf"/>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="messages"/>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" p:defaultLocale="en"/>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
</mvc:interceptors>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0"/>
</beans>
Can you please also confirm that you have a JSR 303 implementation in your classpath - say hibernate-validator jar files.
You need <mvc:annotation-driven /> to enable jsr-303 validation, This is needed for the #Valid annotation to actually do anything.
try adding <mvc:annotation-driven/> in servlet context XML if not done.
schemaLocation the mvc entry should contain these two:
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
Solution is simple.
Validation libraries must be placed into classpath.
DISCLAIMER : In the absence of complete relevant controller code and the suspicion that Validator library might be missing in classpath, here is a small test to just check the configuration. You can use this very quickly to find if all configurations are in place with just removing the session attribute part.
Let's give it a try. To simplify testing with a REST Client and also incomplete code I have not used SessionAttributes. Also there is Hibernate validator library on my runtime classpath. Also I have used ResponseBody annotation to let the Spring automatically convert the String to response text without making use of alternate view technology (like jsp).
import org.hibernate.validator.constraints.Range;
public class Goal {
#Range(min = 1, max = 15)
private int minutes;
public int getMinutes() {
return minutes;
}
public void setMinutes(int minutes) {
this.minutes = minutes;
}
}
Then my controller
#Controller
public class GoalController {
#RequestMapping(value = "/addGoal", method = RequestMethod.POST)
public #ResponseBody String updGoal(#ModelAttribute("goal") #Valid Goal goal, BindingResult result) {
System.out.println(goal.getMinutes());
System.out.println(result.hasErrors());
if(result.hasErrors()) {
return "Errors !";
}
return "No Errors !";
}
}
Hitting the application with a rest client on http://localhost:8080/testApp/addGoal with POST parameters as minutes=1 gives me response as No Errors ! while with minutes=-1 gives me Errors !

Spring MVC Controller with JSON and JSP

Can the same method in a controller be used for both JSP and other MIME types (like XML and JSON)?
I know of the following ways to resolve views in Spring MVC.
Return a String with the view name and add attributes to the Model or ModelMap
Return a ModelAndView with the view name and model
Return an Object with a #ResponseBody annotation
I use 1 or 2 when I am dealing with JSP and 3 when I want to return JSON or XML.
I know I can use two methods and with the #RequestMapping(headers="accept=application/xml") or #produces annotations to define which MIME types they handle, but is it possible to do this in just one method?
The controller logic is pretty simple and it seems like unnecessary duplication to have two different methods mapped which return the same exact model, or is this just simply the way it's done?
Yes, this is straight forward in Spring MVC 3.x...
You basically write your controller methods for just normal JSP page views and then you configure a ContentNegotiatingViewResolver bean in your Dispatcher servlet config., which looks at the requested mime-type (or file extension) and returns the appropriate output type.
Follow the instructions here: Spring 3 MVC ContentNegotiatingViewResolver Example
I had the very same requirement recently, and below is my code. validateTicket returns jsp name and sendForgotPassword mail returns json. My spring version is 4.0.0.RELEASE. Of course, if I need to return complex json then I'd definitely register Jackson converter - http://docs.spring.io/spring/docs/4.0.x/javadoc-api/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.html
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="foo.bar" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
#Controller
#RequestMapping("/forgot-password")
public class ForgotPasswordController {
#RequestMapping(value="/reset-password", method = RequestMethod.GET)
public String validateTicket(#RequestParam String ticket, #RequestParam String emailAddress) {
return "resetPassword";
}
#RequestMapping(value="/send-mail", method = RequestMethod.POST, produces="application/json")
public #ResponseBody String sendForgotPasswordMail(#RequestParam String emailAddress) throws LoginException {
return "{\"success\":\"true\"}";
}
}

Inject property to spring bean using annotation

As explained here and here it is quite clear how to do it but still can't seem to make it work.
I simply like to use the #Value annotation in order to inject a property to a spring bean. I created a basic spring MVC project with one controller and one bean.
Here is my application context:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:component-scan base-package="me.co.fatsecret" />
<!-- Properties -->
<bean id="props"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:fatProperties.properties" />
</bean>
</beans>
I have one bean called Configuration:
package me.co.fatsecret;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class Configuration {
/*--- Members ---*/
#Value("${api_key}")
protected String API_KEY;
#Value("${api_secret}")
protected String API_SECRET;
#Value("${api_url}")
protected String API_URL;
/*--- Constructors ---*/
public Configuration() {
}
/*--- Getters & Setters ---*/
public String getAPI_KEY() {
return API_KEY;
}
public void setAPI_KEY(String aPI_KEY) {
API_KEY = aPI_KEY;
}
public String getAPI_SECRET() {
return API_SECRET;
}
public void setAPI_SECRET(String aPI_SECRET) {
API_SECRET = aPI_SECRET;
}
public String getAPI_URL() {
return API_URL;
}
public void setAPI_URL(String aPI_URL) {
API_URL = aPI_URL;
}
}
Now I have only one controller, injected with this Configuration class and as I call this controller I see that the values in the Configuration class are not populated right.
My properties file is located under the resources folder (src/main/resources) and is a part of my classpath (done by default since this is a maven project). Here it is:
api_url=http://platform.fatsecret.com/js?
api_key=SomeKey
api_secret=SomeSecret
The file name is fatProperties.properties.
As I debug my server when calling the controller I see that the content of the Configuration class is:
${api_key}
${api_secret}
${api_url}
This is the actual value of the Strings, wich means that the vales from the properties file are not getting injected for some reason.
Am I missing something here?
UPDATE1: I replaced the PropertyPlaceholderConfigurer bean with:
<context:property-placeholder location="classpath:fatProperties.properties"/>
Getting the same result
Ok, got it!
I'm using a spring MVC project, which means I have a separated context for my web layer (the controllers). The "Configuration" bean which hods the properties using the #Value annotation is injected to a controller. My property-placeholder is defined within my root-context hence it cannot be seen from my controller. To resolve the issue I simply added the property-placeholder definition to my DispatcherServlet context and it works like a charm :)
Add this to your application context file:
<context:property-placeholder location="classpath:fatProperties.properties" />
Try
#Value("#{props['api_key']}")
private String apiKey;

Spring and auto-wiring: NullPointerException

I'm trying to get a grip on auto-wiring in Spring, but I can't seem to properly instantiate the bean (a DocumentBuilder). I have created a custom JSP tag as such:
public class MyTag extends SimpleTagSupport {
#Autowired
private DocumentBuilder documentBuilder;
public void setBuilder(DocumentBuilder builder) {
this.documentBuilder = builder;
}
#Override
public void doTag() throws IOException {
// documentBuilder is null in here!
}
}
This is the servlet configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Scan for HTTP/REST controllers -->
<context:component-scan base-package="the.right.package" />
<context:annotation-config/>
<bean id="documentBuilderFactory"
class="javax.xml.parsers.DocumentBuilderFactory"
factory-method="newInstance">
<property name="validating" value="false" />
<property name="ignoringElementContentWhitespace" value="true" />
</bean>
<bean id="documentBuilder" class="javax.xml.parsers.DocumentBuilder"
factory-bean="documentBuilderFactory"
factory-method="newDocumentBuilder">
</bean>
</beans>
Any ideas?
You can only inject in spring beans! But Jsp-Tags are no Spring Beans, so the Autowird annotation will be completely ignored, and therefore the field is null.
There are two solution:
use the #Configurable Support. -- But that requires real AspectJ. (I have never tried it for Tags, but I guess it will work for tags like for every other normal class). #see Spring Reference: Chapter 7.8.1 Using AspectJ to dependency inject domain objects with Spring
Extend your tag from the abstract Spring class RequestContextAwareTag. This provides access to the WebApplicationContext via getRequestContext().getWebApplicationContext(). Then you can use the WebApplicationContext to obtain the required beans programmatic.
Try to modify the code like this
public class MyTag extends SimpleTagSupport {
private DocumentBuilder documentBuilder;
#Autowired
public void setBuilder(DocumentBuilder builder) {
this.documentBuilder = builder;
}
#Override
public void doTag() throws IOException {
// documentBuilder is null in here!
}
}
You can use #Autowired if you mark your tag class as Spring bean. But it's stupid, because simple tags not caching by container. Each request creates own tag instance, but wiring happend only conteiner starts.

implement AOP for Controllers in Spring 3

How do I implement AOP with an annotated Controller?
I've search and found two previous posts regarding the problem, but can't seem to get the solutions to work.
posted solution 1
posted solution 2
Here's what I have:
Dispatch Servlet:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.foo.controller"/>
<bean id="fooAspect" class="com.foo.aop.FooAspect" />
<aop:aspectj-autoproxy>
<aop:include name="fooAspect" />
</aop:aspectj-autoproxy>
</beans>
Controller:
#Controller
public class FooController {
#RequestMapping(value="/index.htm", method=RequestMethod.GET)
public String showIndex(Model model){
return "index";
}
}
Aspect:
#Aspect
public class FooAspect {
#Pointcut("#target(org.springframework.stereotype.Controller)")
public void controllerPointcutter() {}
#Pointcut("execution(* *(..))")
public void methodPointcutter() {}
#Before("controllerPointcutter()")
public void beforeMethodInController(JoinPoint jp){
System.out.println("### before controller call...");
}
#AfterReturning("controllerPointcutter() && methodPointcutter() ")
public void afterMethodInController(JoinPoin jp) {
System.out.println("### after returning...");
}
#Before("methodPointcutter()")
public void beforeAnyMethod(JoinPoint jp){
System.out.println("### before any call...");
}
}
The beforeAnyMethod() works for methods NOT in a controller; I cannot get anything to execute on calls to controllers. Am I missing something?
In order to put an aspect on a HandlerMethod in a class annotated with #Controller in Spring 3.1 you need to have the proxy-target-class="true" attribute on the aspectj-autoproxy element. You also need to have the CGLIB and the ASM libraries as a dependency in your WAR/EAR file. You can either specify your annotated aspect class as a bean and use the aop:include as stated above or you can leave the aop:include out and add a filter similar to this in your component scan element:
<context:component-scan>
<context:include-filter type="aspectj"
expression="com.your.aspect.class.Here"/>
</context:component-scan>
I do not know if this is a requirement as a result of Spring 3.1 only but I know that without this, you will not be able to put an aspect on your controller HandlerMethod. You would get an error similar to:
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
HandlerMethod details:
Controller [$Proxy82]
Method [public void com.test.TestController.testMethod(java.security.Principal,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException]
Resolved arguments:
[0] [null]
[1] [type=com.ibm.ws.webcontainer.srt.SRTServletResponse] [value=com.ibm.ws.webcontainer.srt.SRTServletResponse#dcd0dcd]
This is not required if your aspect is on a method in a class that is not a controller
I am going to state an alternative solution (sorry not a direct answer) but what you wanting to do is probably best done via interceptors and filters.

Resources