Creating a custom view resolver in Spring MVC: Property 'url' is required - java

I'm trying create a simple view resolver that returns hello world regardless of what view you want (as a starting point).
I have this so far:
public class MyViewResolver extends AbstractTemplateView {
#Override
protected void renderMergedTemplateModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
doRender(model, request, response);
}
protected void doRender(Map<String,Object> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
PrintWriter writer = response.getWriter();
writer.write("hi from my resolver!");
}
}
Now I am getting this error:
2012-03-29 16:51:28.855:WARN:/:unavailable
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'viewResolver' defined in ServletContext resource [/WEB-INF/application-context.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'url' is required
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1455)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
I have implemented whatever the AbstractTemplateView required, not sure what url property it is asking for?
Also, where is the name of the view that is passed to this viewresolver?
Update
So I added:
#Override
public boolean isUrlRequired() {
return false;
}
And now I am just getting an error like:
HTTP ERROR 404
Problem accessing /home/index. Reason:
NOT_FOUND
My application-context.xml has:
<bean id="viewResolver" class="com.example.MyViewResolver">
</bean>
What am I missing something?

You extend (indirectly) AbstractUrlBasedViewResolver, so it's logical that a URL is required to resolve the view. However, if in your case it is not required, you can override the isUrlRequired() method and return false

You have mixed up two concepts in Spring MVC. In Spring MVC you need both a view resolver and a view.
In your question you want two things:
A view that returns "hello world".
A view resolver that passes all
returned view names to the above view.
To create the view:
Extend: org.springframework.web.servlet.View
Your implementation can then write "hello world" to the http response.
To create the view resolver:
Extend: org.springframework.web.servlet.ViewResolver
Your implementation should always te return your previously created view.
You can see that the viewresolver base class is where you have acceess to the returned view name. See: https://fisheye.springsource.org/browse/spring-framework/spring-webmvc/src/main/java/org/springframework/web/servlet/ViewResolver.java
This is the most basic way to answer your question. Spring offers many implementations of these classes for more specialised purposes that may suit your use case.
Hope this helps. Let me know if you need any more details.

You have started out wrong: instead of a ViewResolver, you are implementing a View. See Spring's documentation on this, there are many base classes you can start from. As for the view name, it will be quite obvious once you get on the right track since the method View resolveViewName(String viewName, Locale locale) is the only one in the ViewResolver interface.
But, judging from your confusion of these concepts, maybe your real question is "help me make a Hello World in Spring MVC". In that case I should really direct you to the samples that come with the Spring MVC distribution, but here's something for a quick start. First of all, you don't need to implement a ViewResolver or even a View. The most basic thing you need is a Controller, which can directly produce the response:
public class MyController implements Controller
{
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
{
response.getWriter().println("Hello, world!");
return null;
}
}
Next, you need a HandlerMapping, this is the simplest variant:
<bean id="handlerMapping"
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
Finally, declare your controller and set its name to the URL you want it to serve:
<bean name="/home/index" class="MyController" />

If you dig into the nested exception you will see
Caused by: java.lang.IllegalArgumentException: Property 'url' is required
at org.springframework.web.servlet.view.AbstractUrlBasedView.afterPropertiesSet(AbstractUrlBasedView.java:67)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$6.run(AbstractAutowireCapableBeanFactory.java:1504)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1502)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
... 37 more
So Exception came at AbstractUrlBasedView.java: line 67, if you see the spring source file #fisheye.springsource.com you will find the following code
public void afterPropertiesSet() throws Exception {
if (isUrlRequired() && getUrl() == null) {
throw new IllegalArgumentException("Property 'url' is required");
}
}
/**
* Return whether the 'url' property is required.
* <p>The default implementation returns <code>true</code.
* This can be overridden in subclasses.
*/
protected boolean isUrlRequired() {
return true;
}
So it is checking for if isUrlRequired and getURL is null it will show the above exception, so by changing the isUrlRequired to false you can avoid the 1st exception

Related

How to resolve ambiguous mapping with Spring Rest Controllers?

I have looked at the following posts
1) Error creating bean with name 'requestMappingHandlerAdapter'
2)Spring Boot Ambiguous mapping. Cannot map method
3) Spring mvc Ambiguous mapping found. Cannot map controller bean method
4) Spring MVC Ambiguous mapping. Cannot map
But I have not been able to figure out how to resolve my issue. I am creating a Spring Boot web application in which I am trying to map the following endpoints /quiz/score/{quizId} and /quiz/questions/{quizId} endpoints to two separate methods.
My functions are as follows
#RequestMapping(name="/quiz/questions/{quizId}", method=RequestMethod.GET)
public ResponseEntity<QuizQuestion> questions(#PathVariable String quizId) {
QuizQuestion question = this.quizService.fetchQuestion(quizId);
if (question == null) {
return new ResponseEntity<QuizQuestion>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<QuizQuestion>(question, HttpStatus.OK);
}
and
#RequestMapping(name="/quiz/score/{id}", method=RequestMethod.GET)
public Score getScore(#PathVariable("id") String quizId) {
return this.quizService.getScore(quizId);
}
I am getting the following error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map '/myapplication' method
public com.project.myapplication.Score com.project.myapplication.QuizController.getScore(java.lang.String)
to {[],methods=[GET]}: There is already '/myapplication' bean method
public org.springframework.http.ResponseEntity<com.project.myapplication.QuizQuestion> com.project.myapplication.QuizController.questions(java.lang.String) mapped.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
. . . . . . . .. .
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map '/myapplication' method
public com.project.myapplication.Score com.project.myapplication.QuizController.getScore(java.lang.String)
to {[],methods=[GET]}: There is already '/myapplication' bean method
public org.springframework.http.ResponseEntity<com.project.myapplication.QuizQuestion> com.project.myapplication.QuizController.questions(java.lang.String) mapped.
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576) ~[spring-webmvc-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at
I know that two methods have the same signature, but they have two unique endpoints. How can I resolve this issue?
Your problem is that you've specified your endpoints like this:
#RequestMapping(name="/quiz/score/{id}", method=RequestMethod.GET)
public Score getScore(#PathVariable("id") String quizId) {
return this.quizService.getScore(quizId);
}
But they should be like this:
#RequestMapping(value="/quiz/score/{id}", method=RequestMethod.GET)
public Score getScore(#PathVariable("id") String quizId) {
return this.quizService.getScore(quizId);
}
Note the value instead of name.
For further clarification, you can check RequestMapping javadoc, which explains the different parameters. name parameter just gives a name for your mapping. The value parameter is the key one.
Use value in place of name or you can use method Specific annotation
#GetMApping("/name")
#PostMApping("/name")
#PutMApping("/name")
#DeleteMApping("/name")

Spring custom converter gets overwritten

This is my first post, so please excuse me if I miss to follow any convention of posting a good question.
I am coding a RESTful service using Spring REST. The signature of the service is as below.
#RestController
#RequestMapping(value = "/{param1}/{param2}/myservice", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyService {
#RequestMapping(value = "/{param3}", method = GET)
public MyResponseObj getMyResponseDetails(MyRequestObject request){
//Service logic code
}
}
In above example, MyRequestObject contains a custom date object with a specific format. For the reference of this question, the name of the custom date object of MyDate in MyRequestObject is myDate.
My objective here is to convert the string value of myDate coming as a query parameter with name myDate. I have purposefully kept the query parameter name the same as of the inner object name within MyRequestObject class, so that Spring can implicitly assign the value to the myDate attribute of the MyRequestObject instance of the method argument. To make this auto assignment, I ensured to keep a constructor for MyDate with a String parameter. But, Spring does not do this auto-assignment for myDate value.
So, after reading several posts on this forum, I created a custom converter to convert a String object to MyDate object. Below is my code for the same.
#Component
public class StringToMyDateConverter implements Converter<String, MyDate> {
public MyDate convert(String s){
//MyDate.parseData(String) is a custom method to convert a String to MyDate object
return MyDate.parseData(s);
}
}
Then, I had added below configuration into my Spring config.xml file to add this custom converter class into Spring's default conversion service.
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.my.service.rest.util.conversion.StringToMyDateConverter"/>
</set>
</property>
</bean>
During application startup while debug, I have found that my custom converter code is getting executed and the Spring's default converter service registers my custom converter. However, I have also noticed that the Spring's internal code to initialize FormattingConversionServiceFactoryBean is getting executed multiple times during start up of the service. And that eventually resulted into overwriting of the converter service listing, which did not have my custom converter that got registered before.
So, when I invoke the REST service URL mentioned below, I get below mentioned error.
URL
http://localhost:7880/project/json/value1/value2/myservice/value3?myDate=2017-09-12
Exception
org.springframework.validation.BindException:
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'request' on field 'myDate': rejected value [2017-09-12]; codes [typeMismatch.request.myDate,typeMismatch.myDate,typeMismatch.com.my.service.xml.datatype.MyDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [request.myDate,myDate]; arguments []; default message [myDate]]; default message [Failed to convert property value of type [java.lang.String] to required type [com.my.service.xml.datatype.MyDate] for property 'myDate'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.my.service.xml.datatype.MyDate] for property 'myDate': no matching editors or conversion strategy found]
It is important to mention here that my project has a complex module structure having multiple Spring configurations.
I think, because of that Spring is initializing the default conversion service multiple times. Though, I am not able to determine the order of configuration invocations so that I can prevent Spring overwriting my customer configuration added in the list. I have also tried the annotation based configuration class as listed below to achieve this result. But that also resulted in same issue.
#Configuration
public class ConversionServiceProvider {
#Autowired
private StringToMyDateConverter stringToMyDateConverter;
#Bean
public ConversionService getConversionService() {
ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
bean.setConverters(getConverters());
bean.afterPropertiesSet();
ConversionService object = bean.getObject();
return object;
}
private Set<Converter<?, ?>> getConverters() {
Set<Converter<?, ?>> converters = new HashSet<>();
converters.add(stringToMyDateConverter);
// add here more custom converters, either as spring bean references or directly instantiated
return converters;
}
}
Can you please suggest me anything that I can try here to solve this issue? Please excuse me for the lengthy question, but I think that would help to understand the situation and problem better.
Thanks!!

Understanding view resolution in spring mvc

I'm trying to understand how spring mvc generates a markup. For instance, consider the simple controller:
#Controller
public class HelloController{
#RequestMapping("/hello")
public String hello(){
return "hello";
}
}
and say, that we're applying UrlBasedViewResolver defined in the dispatcher-servlet.xml as follows:
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="prefix" value="/WEB-INF/views/">
<property name="suffix" value="jsp">
</bean>
What the javadoc of the resolver does is says that we have three methods that return the instance of the View interface.
The first one is
protected AbstractUrlBasedView buildView(String viewName),
the second is
protected View createView(String viewName, Locale locale)
and the third is
protected View loadView(String viewName, Locale locale).
As long as the View interface has the method render(Map<String,?> model, HttpServletRequest request, HttpServletResponse response) I'd assume that once the instance of View has been created we call this method to render the markup to the client. But I'm not sure if it actually works that way.
In general, my question is what method takes the a jsp-page and return the instance of View to be rendered to the client.
In general, my question is what method takes the a jsp-page and return the instance of View to be rendered to the client.
In the case of UrlBasedViewResolver, that would be the createView method, which for a JSP will return an InternalResourceView.
As far as the view resolution framework is concerned, the ViewResolver interface is the entry point, and has a method resolveViewName which takes the view name ("hello" in your example") and returns a View object, then calls render on that.
The buildView, createView and loadView methods are all internal specific to the UrlBasedViewResolver implementation of ViewResolver.

#ControllerAdvice Not Firing

I'm working on a Spring MVC/Webflow Application (version 3.2) and trying to get exception handling working where I can output a custom exception message to a logfile and error.jsp. The problem I'm having is that the Exception Handler is not getting fired. I've created the following class and annotated it "#ControllerAdvice" and put it into the same package as my controller that is throwing the exception:
#ControllerAdvice
public class MyCustomExceptionController {
#ExceptionHandler(MyCustomException.class)
public ModelAndView handleMyException(MyCustomException ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/error/error");
modelAndView.addObject("errorId", ex.getErrorId());
modelAndView.addObject("message", ex.getErrorMessage());
return modelAndView;
}
}
and added the following to the mvc-config File:
<mvc:annotation-driven/>
And included the following in my app-config File:
<context:component-scan base-package="package containing my controllers and MyCustomExceptionController">
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
Any ideas why this isn't working?
The <mvc:annotation-driven/> element implicitly registers a ExceptionHandlerExceptionResolver bean. This class has a initExceptionHandlerAdviceCache() method which scans beans in the context to find those whose class type is annotated with #ControllerAdvice.
It does this by first calling ControllerAdviceBean.findAnnotatedBeans(ApplicationContext). Internally, this method uses ApplicationContext#getBeanDefinitionNames(). The javadoc of this method states
Does not consider any hierarchy this factory may participate
To clarify what this means. When you declare a ContextLoaderListener in your deployment descriptor, it loads what we call a root or application ApplicationContext and makes it available in the ServletContext. When you then declare a DispatcherServlet, it creates its own servlet ApplicationContext and uses any ApplicationContext it finds in the ServletContext attributes loaded by the ContextLoaderListener as a parent to that context. The hierarchy looks like so
Root ApplicationContext // loaded by the ContextLoaderListener
|
Servlet ApplicationContext // loaded by the DispatcherServlet
Every ApplicationContext has access to beans in parent contexts, but not the other way around.
The method above chooses not to use the beans in parent contexts and so only has access to beans in the current ApplicationContext (BeanFactory really).
As such, if your
<context:component-scan .../>
is declared in a root ApplicationContext as I'll assume from the name app-config, but the
<mvc:annotation-driven />
is declared in the servlet ApplicationContext, again assuming from mvc-config, then the ExceptionHandlerExceptionResolver looking for #ControllerAdvice beans will not find any. It is looking for beans in the servlet context but they aren't there, they are in the root context.
In case anyone else runs into a problem like this - I found an error I had.
I only had one RequestMapping (http://localhost:8080/myapp/verify/verify)
In an InterceptorController, in the PreHandle method, I explicitly threw an exception -> throw new MyCustomException("error","error.jsp") to test my #ControllerAdvice Exception handling.
When I went to http://localhost:8080/myapp/ I would see the interceptor controller get called, my custom exception get thrown, but the #ControllerAdvice class with my #ExceptionHandler(MyCustomException.class) was never called.
I added a #RequestMapping(value="/") and it resolved my issues. Since I was attempting to go to a URI that had no #RequestMapping associated with it, i was getting a 'NoHandlerFoundException' which was shorting out my Exception from bubbling up.
In short, make sure the URI you're attempting to invoke has a #RequestMapping associated with it, or have a method in your ExceptionHandler class to deal with the NoHandlerFoundException.
Hope this helps.

Add attributes to the model of all controllers in Spring 3

Every single view in my Spring 3 app has a set of attributes they can rely on. So the first line of every controller is something like:
ControllerHelper.addDefaultModel(model, personManager, request);
In there I'll add
user object and full name retrieved from the database if person is logged in
set of variables which are typically set once (e.g. imagesHost)
set of languages a visitor can switch to
current language
some statistics (e.g. total # of people in our system)
This all allows each view to display the logged in user's name, easily reference an image location, a list of languages and some overall stats about the site.
So the question is, is the controller model object the best place to store all the data or is there a more convenient place which makes it just as easy for views to access this info?
And secondly, I'd REALLY love not to have to have the ControllerHelper line above as the first line in every controller. It's actually not always the first line, sometimes I first check if I need to redirect in that controller, because I don't want to waste resources filling the model for no reason. Maybe a filter or annotation or some Spring callback mechanism could make sure the ControllerHelper code is called after the controller is finished but right before the view is rendered, skipping this if a redirect was returned?
You could write an org.springframework.web.servlet.HandlerInterceptor. (or its convenience subclass HandlerInterceptorAdapter)
#See: Spring Reference chapter:
15.4.1 Intercepting requests - the HandlerInterceptor interface
It has the method:
void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception;
This method is invoked after the controller is done and before the view is rendered. So you can use it, to add some properties to the ModelMap
An example:
/**
* Add the current version under name {#link #VERSION_MODEL_ATTRIBUTE_NAME}
* to each model.
* #author Ralph
*/
public class VersionAddingHandlerInterceptor extends HandlerInterceptorAdapter {
/**
* The name under which the version is added to the model map.
*/
public static final String VERSION_MODEL_ATTRIBUTE_NAME =
"VersionAddingHandlerInterceptor_version";
/**
* it is my personal implmentation
* I wanted to demonstrate something usefull
*/
private VersionService versionService;
public VersionAddingHandlerInterceptor(final VersionService versionService) {
this.versionService = versionService;
}
#Override
public void postHandle(final HttpServletRequest request,
final HttpServletResponse response, final Object handler,
final ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
modelAndView.getModelMap().
addAttribute(VERSION_MODEL_ATTRIBUTE_NAME,
versionService.getVersion());
}
}
}
webmvc-config.xml
<mvc:interceptors>
<bean class="demo.VersionAddingHandlerInterceptor" autowire="constructor" />
</mvc:interceptors>
You can also use #ModelAttribute on methods e.g.
#ModelAttribute("version")
public String getVersion() {
return versionService.getVersion();
}
This will add it for all request mappings in a controller. If you put this in a super class then it could be available to all controllers that extend it.
You could use a Controller Class annotated with #ControllerAdvice
"#ControllerAdvice was introduced in 3.2 for #ExceptionHandler, #ModelAttribute, and #InitBinder methods shared across all or a subset of controllers."
for some info about it have a look at this part of the video recorded during SpringOne2GX 2014
http://www.youtube.com/watch?v=yxKJsgNYDQI&t=6m33s
like #blank answer this work for me:
#ControllerAdvice(annotations = RestController.class)
public class AnnotationAdvice {
#Autowired
UserServiceImpl userService;
#ModelAttribute("currentUser")
public User getCurrentUser() {
UserDetails userDetails = (UserDetails)
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return userService.findUserByEmail(userDetails.getUsername());
}
}
There is one issue that occurs with redirection when using #ModelAttribute or HandlerInterceptor approach. When the handler returns Redirect view, the model attributes added this way are appended as query parameters.
Another way to handle this situation is to create session-scoped bean, that can be autowired in base application controller, or explicitelly in every controller where access is needed.
Details about available scopes and usage can be found here.
if you need add some global variables that every view can resolve these variables,
why not define into a properties or map?
then use spring DI, refer to the view resolver bean.
it is very useful,such as static veriable, e.g. resUrl
<property name="viewResolvers">
<list>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="attributes" ref="env" />
<property name="exposeContextBeansAsAttributes" value="false" />
<property name="prefix" value="${webmvc.view.prefix}" />
<property name="suffix" value="${webmvc.view.suffix}" />
</bean>
</list>
</property>

Categories