Spring's #RequestBody providing empty string on POST - java

I have an application with Spring 3.0.5.RELEASE trying to get the full content of a post using #RequestBody. The method is called, but the string passed is always empty. I have checked, by placing breakpoints, that the StringHttpMessageConverter is called, but the inner HttpInputMessage is empty.
I've seen this issue with both Jetty and Tomcat, so I'm discarding it's a problem with the container.
Here is my sample controller:
#Controller
#RequestMapping("/")
public class SubscriptionController {
#RequestMapping(value = "/requestbody", method = RequestMethod.POST)
public ModelAndView mycustomAction(#RequestBody String body) {
// body is always empty
Logger.getLogger(this.getClass()).debug("REQUEST BODY '" + body + "'");
return new ModelAndView("empty");
}
}
My application context is defined as follows:
<?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/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">
<!-- Enable auto detection of controllers -->
<context:component-scan base-package="com.big.viajerotelcel.controller" />
<!--
use annotation driven mvc and one single validator with JSR-303
standard
-->
<mvc:annotation-driven />
<!--
Message source for this context, loaded from localized "messages_xx"
files
-->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames" value="classpath:i18n/messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!-- Declare the Interceptor -->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="locale" />
</mvc:interceptors>
<!-- Declare the Resolver -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
<!-- will load Tiles definitions! -->
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/general.xml</value>
</list>
</property>
</bean>
<!-- Tiles view resolver -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>
<!-- Configure the multipart resolver -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--
one of the properties available; the maximum file size in bytes (5MB)
-->
<property name="maxUploadSize" value="5120000" />
</bean>
<!-- Adding these lines has no effect, the StringHttpMessageConverter is called either way -->
<!-- <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>-->
<!-- -->
<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">-->
<!-- <property name="messageConverters">-->
<!-- <list>-->
<!-- <ref bean="stringHttpMessageConverter"/>-->
<!-- </list>-->
<!-- </property>-->
<!-- </bean>-->
</beans>
I'm testing this using curl as follows:
curl -d asd=123 -d qwe=456 http://localhost:8080/requestbody
Any ideas or help is more than welcomed!

Here is a code snippet of ServletServerHttpRequest, which extends HttpInputMessage. I am pretty positive this is the implementation that you are using in your code:
public InputStream getBody() throws IOException {
return this.servletRequest.getInputStream();
}
In other words, the request body is meant to be read as the input stream of the HttpServletRequest object.
The request's input stream is not valid in several situations, but I can't find the correct documentation for it at the moment. For example, if you call request.getParameter() on a post request, tomcat has to read the input stream in order to interpret the parameters, thus afterwards when you read the input stream, it is empty because it has reached the end already.
Perhaps you are invoking getParameter somewhere in an interceptor or perhaps a filter defined in web.xml. Another option is that Spring is doing that for you, for example, if your controller has some other method with complex #RequestMappings (such as reading param values, or header values).
I have two suggestions for you:
Add a servlet filter (before spring gets a chance to act), and wrap the request with your own wrapper (just extend HttpServletRequestWrapper). This way you can put breakpoints or log messages at some methods of the request object and see who's calling them.
Use a pojo object parameter, and setup the bindings. It seems like a much cleaner way to read post data.

How are you POSTing messages to this URL? Are you positive that the HTTP request contains what you think it does? I suggest removing any web browsers from the picture and drop down to something low-level like curl which lets you send any type of HTTP message yourself.

Had a similar problem - the string received by spring controller was always empty. Tinkered with my spring config but with no result. Finally the problem was that the client was actually was not sending anything body!(due to some typo of mine)
If found with a similar error, its worth checking once if the client's payload is actually non-empty.

Another reason that your XML may not be getting marshalled into your JAXB object is related to the namespaces in the XML.
Versions of java after 1.8.101 are more strict about parsing namespaced XML.
See JAXB doesn't unmarshall after updating java from 1.8.0_77 to 1.8.0_121
In my case I was seeing a request body with all nulls and no exception being thrown to indicate that the XML parsing had failed.

Related

Cache interceptor call is ignored

I am working on cache implementation (exstremescale)for maven multi module project, where i have added below maven dependency
<dependency>
<groupId>com.ibm.extremescale</groupId>
<artifactId>ogclient</artifactId>
<version>8.6.0.20150901-215917</version>
</dependency>
Added caching annotation on
#Override
#Cacheable(value = "productDetails", key = "#productId + #orgId")
public Product productRead(final String productId, final String productKey, final String orgId, final CRApplicationEnum sourceSystem) throws IntegrationException {
cache-manager.xml
<?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:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager" primary="true">
<property name="caches">
<set>
<bean class="com.ibm.websphere.objectgrid.spring.ObjectGridCache"
p:name="eventDetails" p:map-name="${iev.eventDetails.mapName}"
p:object-grid-client-ref="wxsGridClient" />
<bean class="com.ibm.websphere.objectgrid.spring.ObjectGridCache"
p:name="eventValidationDetails" p:map-name="${iev.eventValidationDetails.mapName}"
p:object-grid-client-ref="wxsGridClient" />
<bean class="com.ibm.websphere.objectgrid.spring.ObjectGridCache"
p:name="productDetails" p:map-name="${ipr.productDetails.mapName}"
p:object-grid-client-ref="wxsGridClient" />
</set>
</property>
</bean>
<bean id="wxsCSDomain"
class="com.ibm.websphere.objectgrid.spring.ObjectGridCatalogServiceDomainBean"
p:catalog-service-endpoints="${xscale.catalogServiceEndpoint}" />
<bean id="wxsGridClient"
class="com.ibm.websphere.objectgrid.spring.ObjectGridClientBean"
p:catalog-service-domain-ref="wxsCSDomain" p:objectGridName="${wxs.objectGridName}" />
Caching is working for only one maven module of the project, i can see the cache interceptor call and for rest of the maven module it is ignoring the #cacheable annotation(it is not going to the interceptor).
We dont have PostConstructor or Self invokation
We are using atomikos as transaction manager and CXF -interceptors which will be executed before coming to caching methods.
Please help me on this
Your comment about JdkDynamixAopProxy and looking at the code makes me think that the method you have annotated with #Cacheable is in a concrete class. And for the annotation on a concrete class to exhibit proper behavior; you need to enable the cglib proxying in your application.
This can be done by adding proxy target class parameter to your cache annotation driven tag.
<cache:annotation-driven proxy-target-class="true"/>
If you dont want to enable class based proxying for your overall application; you can specify the behavior for a particular class by annotating it with this annotation:
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
Calling methods in the same class bypasses the dynamic proxy and any cross cutting concern like caching, transaction etc which is part of the dynamic proxies logic is also bypassed. So could your problem be Spring cache #Cacheable method ignored when called from within the same class ?
If so, the fix is to use AspectJ compile time or load time weaving.

Restlet Framework and Spring - Global filter

I'm having a problem with RESTlet Framework configuration with Spring. I want to have one global filter for all requests and responses. I guess I can use Filter class and it's methods beforeHandle, afterHandle and setNext like this:
Filter.beforeHandle() -> Router -> Filter.afterHandle()
The problem is, that I'm using Spring configured RESTlet and I don't know if the regular Filter will work correctly with SpringRouter from org.restlet.ext.spring package. My current restlet configuration is as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="root" class="org.restlet.ext.spring.SpringRouter">
<property name="attachments">
<map>
<entry key="/login">
<bean class="org.restlet.ext.spring.SpringFinder">
<lookup-method name="create"
bean="loginResource" />
</bean>
</entry>
</map>
</property>
</bean>
</beans>
I'm thinking about adding a bean with id root and class that extends class Filter, and pass to it as property next a bean with id router (which currently is called root). What do you think about this solution?
Mixing Restlet classes from the Spring extension with other one shouldn't be an issue. Spring* classes only provide additional configurability.

Spring MongoDB and Apache Shiro

I am attempting to use Apache Shiro with Spring and MongoDB. I am using Spring Data Repositories which are autowired. I have created my own custom realm for Shiro which uses a Spring Data repository to talk to Mongo:
public class PlatformRealm extends AuthorizingRealm {
#Autowired(required = true)
protected UserRepository userRepository = null;
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
...
}
}
The problem I'm seeing is the userRepository isn't being autowired. I get the following line in my console output referring to the PlatformRealm:
INFO org.springframework.web.context.support.XmlWebApplicationContext - Bean 'platformRealm' of type [class com.resonance.platform.core.security.PlatformRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
This is because of the Apache Shiro ShiroFilterFactoryBean. What is happening is this bean and all of its dependencies are being loaded up immediately when the container is started. It doesn't wait for my persistence beans to be initialized prior to resolving dependencies. This causes the repository reference to be null.
The following bean configurations are loaded via the contextConfigLocation parameter:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/web-platform-persistence.xml,
/WEB-INF/web-platform-services.xml
</param-value>
</context-param>
Services bean configuration:
<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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-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/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="userSession"
class="com.resonance.platform.web.core.services.ShiroUserSessionService" />
<!-- Shiro (Security) -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/" />
<!-- The 'filters' property is not necessary since any declared javax.servlet.Filter
bean -->
<!-- defined will be automatically acquired and available via its beanName
in chain -->
<!-- definitions, but you can perform instance overrides or name aliases
here if you like: -->
<!-- <property name="filters"> <util:map> <entry key="anAlias" value-ref="someFilter"/>
</util:map> </property> -->
<property name="filterChainDefinitions">
<value>
# some example chain definitions:
/admin/** = passThruFilter, roles[admin]
/** = passThruFilter
</value>
</property>
</bean>
<bean id="passThruFilter"
class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter" />
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property
instead. -->
<property name="realm" ref="platformRealm" />
<!-- By default the servlet container sessions will be used. Uncomment
this line to use shiro's native sessions (see the JavaDoc for more): -->
<!-- <property name="sessionMode" value="native"/> -->
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Define the Shiro Realm implementation you want to use to connect to
your back-end -->
<!-- security datasource: -->
<bean id="platformRealm" class="com.resonance.platform.core.security.PlatformRealm" />
Persistence bean config:
<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"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<mongo:mongo id="mongo" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongo" />
<constructor-arg value="platform" />
<property name="writeConcern">
<util:constant static-field="com.mongodb.WriteConcern.SAFE" ></util:constant>
</property>
</bean>
<mongo:repositories base-package="com.resonance.platform.core.data.repositories" />
User Repository:
package com.resonance.platform.core.data.repositories;
import org.bson.types.ObjectId;
import org.springframework.data.repository.CrudRepository;
import com.resonance.platform.core.entities.User;
/**
* A repository used to manage User entities.
* #author Kyle
*/
public interface UserRepository extends CrudRepository<User, ObjectId> {
/**
* Gets a user by the specified login.
* #param login
* #return
*/
User getByLogin(String login);
}
My question is, how can I get the userRepository dependency to resolved properly? I understand that the ShiroFilterFactoryBean has to be initialized before the other dependencies and whatnot, but there must be a way to get the userRepository dependency to be resolved.
EDIT: Added User Repository code.
I am running into the same problem described here.
I am noticing two spring factories.
from the dispacher-servlet.xml which loads #Service #Repository classes due to component-scan defined at an base package level so I can #Autowire Service class into Controller.
from application context doesn't seem to #Autowire classes marked as #Service because they are not loaded.
If I understand you right you should be able to create a subclass of ShiroFilterFactoryBean which implements org.springframework.beans.factory.InitializingBean. In InitializingBean.afterPropertiesSet() you would then add some code that gets the UserRepository and sets it to that field. Not the most elegant solution, but this looks like an exceptional case.
I've had this problem too. It has something to do with the order of bean initialization in the Spring container. The workaround is not to autowire the repository but have your realm implement ApplicationContextAware and get the needed beans straight from the context. It's not elegant, but it'll work.
I am not too sure if this is helpful, but you may check this question from me for an alternative solution.
But, the core issue probably still stays open.
Concrete problem explanation taken from ShiroFilterFactoryBean-and-a-spring-data-mongodb-realm:
The problem is that spring-data-mongodb requires a spring
ApplicationEventMulticaster to have been initialised before it can be
used.
ShiroFilterFactoryBean is a beanPostProcessor, and as such, during
initialisation, spring attempts to configure its realms(and hence my
realm and spring data mongo based userDao). it fails because
ApplicationEventMulticaster has not yet been created.
After I've tried several suggested ways to solve this problem, like the InitializingBean, ApplicationContextAware or BeanPostProcessor interfaces (each resulting in a premature invocation, hence before initializing my necessary service/repository stuff), I came up with the following solution:
Let spring create your shiro context without any automatic bean resolution to your services/repositories.
Let spring create your service/repository context, including mongodb
Create a simple class which will take care of your shiro-service coupling and configure it accordingly in your spring config. This class will be invoked after your shiro and service context has been successful set up.
To (1), sth. like this:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="filterChainDefinitions">
<value>
<!-- Your definitions -->
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"
p:realm-ref="myShiroRealm" />
<bean id="myShiroRealm" class="com.acme.MyShiroRealm"
<!--no bean refs here-->
/>
To (2), sth. like this:
<bean id="myService" class="com.acme.MyService"
c:myRepository-ref="myRepository" />
...
<!-- Ask Spring Data to scan our repositories -->
<mongo:repositories base-package="com.acme.repository.impl.mongodb" />
To (3):
public class ShiroRealmServiceBridge {
public static void postInject( MyShiroServerRealm realm, MyService service ) {
realm.setService( service );
}
}
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass"><value>com.acme.ShiroRealmServiceBridge</value></property>
<property name="targetMethod"><value>postInject</value></property>
<property name="arguments">
<list>
<ref bean="myShiroRealm" />
<ref bean="myService" />
</list>
</property>
Advantages:
It works xD
No additional burden/dependencies on your shiro stuff
Complete spring configuration and setup, resulting in a consistent state after initialization
Disadvantage:
One time overhead setup
May result in an inconsistent state, which will complain at runtime rather than at startup, if you forget or bump the glue-configuration
The ShiroFilterFactoryBean implements the BeanPostProcessor and, since it has dependencies on the security manager w/ its own dependencies on data stores, data access objects, etc. it can cause a whole slew of Bean X of type Y is not eligible for getting processed by all BeanPostProcessors messages.
The worst part is that it seems to be just a way to see the Filter implementations that Spring is instantiating in order to track and possibly inject properties into AuthorizationFilters.
Frankly I don't need that headache just for filter tracking, so I created a custom version that did not include the BeanPostProcessor. I'm now forced to manually wire in Filter implementations to the beans "filters" property, but at least I don't have to deal with that error and the questionable status of my security manager and related beans.

Spring REST 3 to Support XML and JSON

If we develop REST using Spring MVC, it will support XML and JSON data. I have wrote ContentNegotiationViewResorver in my spring config bean app-servlet.xml
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
p:order="1">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<bean class="org.springframework.oxm.xstream.XStreamMarshaller"
p:autodetectAnnotations="true" />
</property>
</bean>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
And my spring REST Controller is:
#Controller
#RequestMapping("/rest/customers")
class CustomerRestController {
protected Log log = LogFactory.getLog(CustomerRestController.class);
#RequestMapping(method = POST)
#ResponseStatus(CREATED)
public void createCustomer(#RequestBody Customer customer,
HttpServletResponse response) {
log.info(">>>" + customer.getName());
response.setHeader("Location", String.format("/rest/customers/%s",
customer.getNumber()));
}
#RequestMapping(value = "/{id}", method = GET)
#ResponseBody
public Customer showCustomer(#PathVariable String id) {
Customer c = new Customer("0001", "teddy", "bean");
return c;
}
#RequestMapping(value = "/{id}", method = PUT)
#ResponseStatus(OK)
public void updateCustomer(#RequestBody Customer customer) {
log.info("customer: " + customer.getName());
}
I set #XStreamAlias("customer") annotation in my customer domain class.
But when I try access http://localhost:8080/rest/customers/teddy.xml it always response JSON data.
I set #XmlRootElement(name="customer") annotation in my customer domain class.
But when I try access http://localhost:8080/rest/customers/teddy.json it always response XML data.
Is there some thing wrong ?
I think "xml" content type should be mapped to "text/xml" not to "application/xml". Also, to force content type resolvers based on extension, you can try to set the "favorPathExtension" property of "ContentNegotiatingViewResolver" to true(though it should have been true by default!)
EDIT: I have now added a working sample at this GIT location - git://github.com/bijukunjummen/mvc-samples.git, if you bring up the endpoint, using mvn tomcat:run, the json is served at http://localhost:8080/mvc-samples/rest/customers/teddy.json and xml at http://localhost:8080/mvc-samples/rest/customers/teddy.xml. This uses JAXB2 not XStream, as I am familiar with JAXB. One thing I noticed was that when my JAXB annotations were not correct in Customer class, Spring was serving out JSON and not XML the way you saw it(You can replicate it by removing the XMLRootElement annotation from Customer class), once I fixed up my annotations, I got back XML as expected. So it could be that there is something wrong with your XStream configuration.
EDIT 2: You are right!! I did not notice, once I got back xml, I assumed that json is working now. I see the problem, in AnnotationMethodHandlerAdapter, the handling for #ResponseBody is a little strange, it completely ignores the ViewResolvers, and uses the registered MessageConverters instead completely bypassing the ContentNegotiatingViewResolver, one workaround for now is to use #ModelAttribute annotation for response, instead of #ResponseBody, this way the view Resolvers are getting called. Try now using the project at git#github.com:bijukunjummen/mvc-samples.git and see if it works for you. This could be a Spring bug, you may try and bring it up in the Spring forum and see what they recommend.
What Accept headers are sent to your server?
Make sure the content type you would like to request is in this list.
Spring 3.1 solves the problem you mention using the new produces element on the #RequestMapping annotation. This allows you to control the HttpMessageConverter that Spring applies to your object.
I wrote a blog post about it:
http://springinpractice.com/2012/02/22/supporting-xml-and-json-web-service-endpoints-in-spring-3-1-using-responsebody/
I had the same problem. I assume you're using Spring 3 and you've used <mvc:annotation-driven/>. I'm not entirely sure, but I think this creates some conflict based on the message converters that the mvc namespace configures.
Using the oxm namespace worked for me:
#XmlRootElement(name="person")
class Person {
private String firstName;
private String lastName;
}
#Controller
#RequestMapping("person")
class PersonController {
#RequestMapping("list")
public #ResponseBody Person getPerson() {
Person p = new Person();
p.setFirstName("hello");
p.setLastName("world");
return p;
}
}
Content Configuration (mvc and internal view resolver are in another context):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-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/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<oxm:jaxb2-marshaller id="jaxbMarshaller">
<oxm:class-to-be-bound name="package.Person" />
</oxm:jaxb2-marshaller>
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="defaultContentType" value="text/html" />
<property name="ignoreAcceptHeader" value="true" />
<property name="favorPathExtension" value="true" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller" ref="jaxbMarshaller" />
</bean>
</list>
</property>
</bean>
</beans>
This example uses JAXB, so you'd need jaxb-api and jaxb-impl on the classpath.
Also, just a tip, you don't need the app-servlet.xml. In your web.xml, set the config to null and let the Context Listener load them for you:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/mvc-context.xml, /WEB-INF/spring/content-negotiation-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value/>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Well I got a solution but I don't know if it's the right way in your method show customer:
#RequestMapping(value = "/{id}", method = GET)
#ResponseBody
public Customer showCustomer(#PathVariable String id) {
Customer c = new Customer("0001", "teddy", "bean");
return c;
}
In this part, we are using MVC of spring and in the controller we should be return a view, so I removed the annotation #ResponseBody and I return a String with the name of the view because in our XML we added a ContentNegotiatingViewResolver and when we have ResponseBody the contentnegociationviewresolver is ignored because is waiting for a view but we returned the object so the method should be like that:
#RequestMapping(value = "/{id}", method = GET)
public String showCustomer(#PathVariable String id, ModelMap model) {
Customer c = new Customer("0001", "teddy", "bean");
model.addAttribute("customer",c);
return "myView";
}
well that works for me, if you have problems you can add to your app-servlet.xml
this bean, but I don't think that you have to add this.
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
I got the answers from mkyong.com
Accessing the controller using a browswer will send a typical browser Accept header. It will not match any view resolver and default to the first one (application/xml) or it matches because application/xml is in the Accept list.
I can recommend using RestClient http://code.google.com/p/rest-client/ to have complete control over what Accept header (if one at all) you want to send.
I don't recommend using text/xml as the default character set is US-ASCII and not UTF-8. This might create funky encoding problems down the road. You can always specify the encoding but appliation/xml has a UTF-8 default encoding.

Spring MVC annotation error "No adapter for handler - Does your handler implement a supported interface like Controller?"

I have an existing Spring MVC apps written in 2.5.
I wanted to use the new annotation controller. I somewhat see that it is really flexible and would answer my other needs.
My problem is, it seems I cannot mix them both.
<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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan
base-package="com.test.web" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<!-- Controller Mappings Here -->
<bean id="homeController" class="com.test.web.HomeController">
<property name="cacheSeconds" value="120" />
</bean>
//other plain old spring mvc controller
When I ran my app and hit the home page, I get below error:
javax.servlet.ServletException: No adapter for handler [com.test.web.HomeController#cca07b]: Does your handler implement a supported interface like Controller?
I am not sure but I think something is conflicting. This is a fairly large Spring MVC apps and I don't want to change those modules that were working already using the old Spring Base Controller.
My goal is to only use the annotation controller on my new enhancement.
You don't need to declare DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter. The context has these registered by default, along with adapters for old-style controllers.
When you declare them explicitly like this, the default ones are removed, and the context will only support the ones you declare.

Categories

Resources