Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues using Spring's DomainClassConverter in Spring MVC

I am trying to use Spring's DomainClassConverter feature with my Spring MVC project. (I have only very basic knowledge of Spring MVC and Spring, apologies in advance for any naive question here).

From the API docs:

The DomainClassConverter allows you to use domain types in your Spring MVC controller 
method signatures directly, so that you don't have to manually lookup the instances via 
the repository: (PS: Example 1.20)

What I understood from the above is that I don't have to write a finder method and the Spring supplies the User object. So these are the steps I did:

Included the below line of XML in applicationcontext.xml.

<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
    <list>
        <bean class="com.rl.userservice.controller.UserConverter"/>
    </list>
</property>

Included this dependency in my pom.xml per the Spring Data REST doc:

<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-webmvc</artifactId>
        <version>2.0.0.BUILD-SNAPSHOT</version>
    </dependency>
</dependencies>

My controller looks like the below:

@Controller
@RequestMapping(value = "/api/newuser")
public class NewUserServiceController {
      @Autowired
  NewUserRepository newUserRepository;

  @RequestMapping("/{id}")
    public String showUserForm(@PathVariable("id") NewUser newUser, Model model) {

      model.addAttribute("newUser", newUser);
      return "userForm";
    } 
}

Repository is like this:

@Repository
public interface NewUserRepository extends JpaRepository<NewUser, Integer> {
}

This is my converter service:

final class UserConverter implements Converter<Integer, NewUser> {
    NewUserRepository newUserRepository;

    public NewUser convert(Integer username) {
        return newUserRepository.findOne(username);
    } 
}

When I run the program tomcat starts successful, but when accessing the URL localhost:8080/userservice/api/newuser/1 I get the below exception:

type Exception report
    message
    description The server encountered an internal error () that prevented it from fulfilling this request.
    exception
    org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type
'com.mpp.userservice.domain.NewUser'; nested exception is
java.lang.IllegalStateException: Cannot convert value of type  
[java.lang.String] to required type
[com.mpp.userservice.domain.NewUser]: no matching editors or
conversion strategy found
      org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:71)
      org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45)
      org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:595)
      org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:101)
      org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
      org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
      org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
      org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
      org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
      org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
      org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
      org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
      org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
      org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

    root cause
    java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type
[com.mpp.userservice.domain.NewUser]: no matching editors or
conversion strategy found
      org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:264)
      org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:93)
      org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:61)
      org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45)
      org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:595)
      org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:101)
      org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
      org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
      org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
      org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
      org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
      org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
      org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
      org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
      org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
      org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

    note The full stack trace of the root cause is available in the Apache Tomcat/6.0.29 logs.
    mpp.

Though not the best code, here's my controller:

public @ResponseBody ResponseEntity<ModelMap> getUserTypeJSON(@PathVariable("userID" String userID, HttpServletResponse response)  { 
    UserType UserType = UserTypeRepository.findOne(id);
    model.addAttribute("Name",UserType.getName());  
    ...
} 

There is an example here that I referenced, but this is using custom converter but does not seem to be using the domain converter service. Please advise. Is this the way to go if I want to reduce boilerplate code of writing CRUD operations? What is the real benefit of this DomainClassConverter when I can get the data in in the other way?

Updated per Oliver Gierke suggestion - still does not work, same error

The document describes:

<mvc:annotation-driven conversion-service="conversionService" />
<bean class="org.springframework.data.repository.support.DomainClassConverter">
  <constructor-arg ref="conversionService" />
</bean>

So I updated my applicationcontext.xml as below, but the same issue:

    <mvc:annotation-driven conversion-service="conversionService"/>

  <bean class="org.springframework.data.repository.support.DomainClassConverter">
    <constructor-arg ref="conversionService" />
  </bean>

    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
          <bean class="com.rl.userservice.controller.UserConverter"/>
        </list>
    </property>
  </bean>

Still the same issue.

Update: DomainClassConverter works with Java Config, but not the XML way (at least experimented with a lot of combinations suggested here and else where on the internet). Just for the others who might be interested and get some useful info here's the code used.

pom.xml (Might require clean-up)

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-rest-webmvc</artifactId>
    <version>2.0.0.M1</version>
</dependency>

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.4.3.RELEASE</version>
</dependency>

The controller file (Might require clean-up)

@RequestMapping("/domain/{id}")
  public @ResponseBody ResponseEntity<ModelMap> showDomainUserForm(@PathVariable("id") User userMatch, HttpServletResponse response) {

  // some code omitted…  

    ModelMap model = new ModelMap();        
    model.addAttribute("DOMAIN-MAP","Domain Controller Service");
    model.addAttribute("Name",userMatch.getName());
    model.addAttribute("Phone",userMatch.getPhone());                                           

    // some code omitted…


  } 

The Java Config file assembled using examples from resource1 and resource2. (Might require clean-up)

package com.rl.userservice.controller;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
@ComponentScan
@EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport{
     @Bean
     public RequestMappingHandlerMapping requestMappingHandlerMapping() {
      RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
      handlerMapping.setUseSuffixPatternMatch(false);
      handlerMapping.setUseTrailingSlashMatch(false);
      return handlerMapping;
     } 


     @Bean
     public DomainClassConverter<?> domainClassConverter() {
         return new DomainClassConverter<FormattingConversionService>(mvcConversionService());
     }

     @Override
     public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
         configurer.enable();
     }

}

Add the below bean definition in the applicationContext.xml

<bean class="com.rl.userservice.controller.WebConfig"/>
like image 882
oneworld Avatar asked Dec 20 '13 02:12

oneworld


1 Answers

A URL is a String, so {id} is a String too. Therefore your service must be able to convert a String to NewUser, not an Integer as yours does.

like image 71
a better oliver Avatar answered Oct 10 '22 15:10

a better oliver