PUT method Spring MVC - java

I'm trying to use the following format for making a put request through the RESTtemplate.
#Autowired
RestTemplate template;
#RequestMapping(value = "/change", method = RequestMethod.PUT)
public ModelAndView change(Data data){
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.APPLICATION_XML);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
HttpEntity<Data> entity = new HttpEntity<Data>(data, headers);
String url="http://www...com";
try {
template.put(url, entity);
} catch (Exception e) {
System.out.println(e);
}
return new ModelAndView("redirect:/home");
}
I checked on the database and I realized that there is no change. Even the request is not written on the log file. When I'm debugging, I am not getting any error. Probably I'm not using correctly the put method. Can anyone suggest me how should I use the put method or what else should I try to perform a put request with the RestTemplate?
Also i try to use the exchange method instead of the put:
try {
ResponseEntity<Data> result = template.exchange(Url, HttpMethod.PUT, entity, Data.class);
} catch (Exception e) {
System.out.println(e);
}
But in this case i'm taking the following exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [website.Data] and content type [text/html;charset=utf-8].
As you can see from the headers i set the content type as application/xml and not text/html. I look at the headers and i can see that contained:
Accept: application/xml
I really can't understand. What else should i change? Any comments on the exception?
Configuration:
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"
p:basename="config/views" p:order="1" />
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/classes/config/xml-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
<!--It is used for redirect-->
<bean id="urlBasedViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value=""/>
<property name="suffix" value=""/>
<property name="order" value="2" />
</bean>
<context:annotation-config />
<!--<context:annotation-config />-->
<context:component-scan base-package="data.controller" />
<context:component-scan base-package="data.service" />
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
</list>
</property>
</bean>

I guess the client of your application is a web-page (then HTML). This article explain what to do for your webapp to be ready for the future browsers compatibility (if they decide later to support e.g PUT, DELETE operations).
In summary for our project we just declared these line in the web.xml:
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/app/*</url-pattern>
</filter-mapping>
The article talk about javascript to add (probably for version prior to Spring 3.0 M1), but we found this solution better as it is just a configuration.

Related

Getting Spring MVC to work with JodaModule

This is a little frustrating... I had this working before in my previous projects, but unable to get it working in my new project after debugging for several hours.
Let's assume I have a simple Rest controller that returns Joda's LocalDate:-
#RestController
#RequestMapping(value = "/api")
public final class ApiController {
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<LocalDate> main() {
return new ResponseEntity<LocalDate>(LocalDate.now(), HttpStatus.OK);
}
}
By default, when I call http://app/api, I get [2015,10,13]. What I really want is 2015-10-13.
To solve this in my previous project, I got it working with this configuration in spring-servlet.xml:-
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<context:component-scan base-package="test.controller"/>
<mvc:annotation-driven/>
<mvc:resources location="/resources/" mapping="/resources/**"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="objectMapper"
class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
p:indentOutput="true"
p:simpleDateFormat="yyyy-MM-dd'T'HH:mm:ss.SSSZ">
</bean>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
p:targetObject-ref="objectMapper"
p:targetMethod="registerModule">
<property name="arguments">
<list>
<bean class="com.fasterxml.jackson.datatype.joda.JodaModule"/>
</list>
</property>
</bean>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
But, when I do the same thing in my new project, I'm getting [2015,10,13] again instead of 2015-10-13.
I did upgrade some dependencies and I also make sure there's no additional ObjectMapper being loaded.
Here's my current dependency tree... I removed all the things that I don't need:-
How do I configure Spring MVC to return the correct date format in JSON?
Thank you very much.
You declared <mvc:annotation-driven> twice. Try removing the first declaration (empty, default config). Probably the message converter you configured in the second <mvc:annotation-driven> is getting overriden by the first declaration (with default message converters).

How to access request object accross all templates in velocity?

I a newbie in velocity templates with spring. I am using VelocityViewResolver to server vm files. I want to get the $request.getLocale() but unfortunately no request object is null.
I can access request object to the vm file(create_user.vm) which is set into context by controller directly but request object is not available in other vm files like header.vm. I need locale to pick the javascript file based on the locale.
I know I can pass the request object to header.vm or other vms like #parse("header.vm", $request) but I am not satisfied by this approach. Please let me know the right way..
#RequestMapping(value = "createuser", method = RequestMethod.GET)
public ModelAndView createUser(ModelMap model) throws SQLException {
......
.......
ModelAndView courseModelView = new ModelAndView ("create_user");
return courseModelView;
}
My servelt-dispatcher.xml has velocity configuration as fellow.
<!-- Presentation Configuration -->
<bean id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath" value="/WEB-INF/vm/" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".vm" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="toolboxConfigLocation" value="/WEB-INF/toolbox.xml">
</property>
</bean>
I would suggest you modify your controller method as:
#RequestMapping(value = "createuser", method = RequestMethod.GET)
public ModelAndView createUser(ModelMap model, HttpServletRequest request) throws SQLException {
......
.......
ModelAndView courseModelView = new ModelAndView ("create_user");
courseModelView.addObject("locale", request.getLocale());
return courseModelView;
}
Then the 'locale' object will be available in your model to be used in the template.

controller redirect is not working in spring mvc 3.2 with tiles 2

I am not getting the page displayed ,after tiles resolving the page which was redirected by a controller
I have my controller
public ModelAndView addUser(){
if(success){
return new ModelAndView("redirect:myredirectPage.html");
}else {
--show error page---
}
}
and in the same controller
#RequestMapping(value="/myredirectPage", method=RequestMethod.GET)
public ModelAndView showMyRedirectPage(){
ModelAndView modelView = new ModelAndView("redirectPage");
return modelView;
}
all I see in the my log is , tiles is resolving the redirected view, but the page is not getting displayed in the browser.
Added model object 'org.springframework.validatio
n.BindingResult.command' of type
[org.springframework.validation.BeanPropertyBin dingResult] to request
in view with name 'redirectPage' 02 Dec 2013 21:03:23
[http-apr-8080-exec-3] DEBUG [org.springframework.web.servl
et.DispatcherServlet] - - Successfully completed request
and I have spring config file view resolver, where tiles view resolver is given priority.
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1"></property>
<property name="prefix" value="/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="TilesviewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="order" value="0"></property>
<property name="viewClass">
<value>
org.springframework.web.servlet.view.tiles2.TilesView
</value>
</property>
</bean>
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
</bean>
cant able to debug, since I am not getting any error on logs. appreciate yours responses
Are you absolutely sure that the redirected request is hitting your showMyRedirectPage() method? That method is mapped to /myredirectPage but the redirect request is going to myredirectPage.html
Have you tried adding the .html extension to your #RequestMapping
#RequestMapping(value="/myredirectPage.html", method=RequestMethod.GET)
public ModelAndView showMyRedirectPage(){
ModelAndView modelView = new ModelAndView("redirectPage");
return modelView;
}

Handling REST calls xml response using JAXB

I have sent a GET REST call to "http://services.enterprisecloud.terremark.com/cloudapi/ecloud/organizations/" and the response is:
HTTP/1.1 200 OK Content-Length: 1373 Content-Type: application/vnd.tmrk.cloud.organization; type=collection x-tmrk-currentuser: /cloudapi/ecloud/admin/users/101 x-tmrk-token: cloud-F2A27F74-C04B-4566-AB53-CCC06DA2F798 Date: Thu, 12 May 2011 19:09:13 GMT
<Organizations href="/cloudapi/ecloud/organizations" type="application/vnd.tmrk.cloud.organization; type=collection" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
.....
.....
There is an XSD given by the vendor which didn't generate any class of type Organization or Organizations. The Organization related class generated by JAXB is ArrayOfOrganization which looks like:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ArrayOfOrganizationType", propOrder = {
"organization"
})
public class ArrayOfOrganizationType {
#XmlElement(name = "Organization", nillable = true)
protected List<OrganizationType> organization;
....
....
When I try to use the following code:
ResponseEntity exchange = template.exchange(URL,
HttpMethod.GET,
new HttpEntity(operation.getInput(), operation.getHeader()),
ArrayOfOrganizationType.class,
urlVariables);
The error I get is:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [com.trmk.dto.ArrayOfOrganizationType] and content type [application/vnd.tmrk.cloud.organization;type=collection]
In spring-dispatcher.xml, I have following:
<!-- Rest client -->
<bean id="httpClient" class="org.apache.http.impl.client.DefaultHttpClient">
<constructor-arg>
<bean class="org.apache.http.impl.conn.PoolingClientConnectionManager" />
</constructor-arg>
</bean>
<bean id="restClient" class="com.transport.ext.RestClient">
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"
p:marshaller-ref="jaxb2Marshaller" p:unmarshaller-ref="jaxb2Marshaller"
p:supportedMediaTypes="application/vnd.tmrk.cloud.organization" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="jaxb2Marshaller" class="com.util.DefaultJaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>java.lang.String</value>
<value>com.trmk.dto.ArrayOfOrganizationType</value>
</list>
</property>
</bean>
<!-- End of Rest client -->
Any recommendation on how to proceed will be highly helpful as I have run out of ideas to solve this problem
You can try adding #XmlRootElement(name="Organizations") to ArrayOfOrganizationType although obviously that's not ideal since you'd need to re-add it each time you regenerate the JAXB classes.

HTTP basic authentication through CXF interceptor not working

I'm having some trouble setting the HTTP Authorization header for a web service request using Apache CXF. I have my client setup through spring:
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<bean id="myHTTPAuthInterceptor" class="my.app.MyHTTPAuthInterceptor" autowire="constructor" />
<bean id="webServiceFactory" class="my.app.WebServiceFactory">
<property name="wsdlLocation" value="classpath:/my/app/webservice.wsdl" />
<property name="serviceURL">
<jee:jndi-lookup jndi-name="webservice/url" />
</property>
<property name="inInterceptors">
<list>
<ref bean="loggingInInterceptor" />
</list>
</property>
<property name="outInterceptors">
<list>
<ref bean="loggingOutInterceptor" />
<ref bean="myHTTPAuthInterceptor" />
</list>
</property>
</bean>
<bean id="myWebService" factory-bean="webServiceFactory" factory-method="getInstance" />
Headers are set through MyHTTPAuthInterceptor like this:
public MyHTTPAuthInterceptor(ConfigDao configDao)
{
super(Phase.POST_PROTOCOL);
this.configDao = configDao;
}
#Override
public void handleMessage(Message message) throws Fault
{
Map<String, List<?>> headers = (Map<String, List<?>>) message.get(Message.PROTOCOL_HEADERS);
String authString = configDao.getUsername() + ":" + config.getPassword();
headers.put("Authorization", Collections.singletonList("Basic " + new String(Base64.encodeBase64(authString.getBytes()))));
}
With username and both set to 'test', everything seems to be OK in the logs:
Headers: {SOAPAction=[""], Accept=[*/*], Authorization=[Basic dGVzdDp0ZXN0]}
However, the server returns a HTTP 401: Unauthorized.
Not knowing what's going wrong, I took a whole other approach by changing my web service client factory code. I added a basic authorization policy to the client's conduit like this:
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
AuthorizationPolicy authorizationPolicy = new AuthorizationPolicy();
authorizationPolicy.setUserName("test");
authorizationPolicy.setPassword("test");
authorizationPolicy.setAuthorizationType("Basic");
httpConduit.setAuthorization(authorizationPolicy);
Tested my setup again, same log (different order though):
Headers: {SOAPAction=[""], Authorization=[Basic dGVzdDp0ZXN0], Accept=[*/*]}
Now the server's response is 200 OK!
Problem solved you might think, but the second approach doesn't really work for me. My application is a multi-tenant environment, all with different username and password. With the second approach I cannot reuse my client.
How can I get my interceptor to work correctly? Am I plugging into the wrong phase? Does the order of the headers matter? If so, how do I change it?
I have almost exactly the same setup as yours but I am putting my interceptor in the PRE_PROTOCOL phase. So far, I have not experienced any problem. You might try that.
I think POST_PROTOCOL is just too late because too much has already been written to the stream.
If you are looking to externalize the client and authentication best approach is to setup httpConduit in spring context..
**in your spring context file...**
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
...
<bean id="properties" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="locations">
<util:list>
<value>file:${config.dir}/application.properties</value>
</util:list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
</bean>
...
<jaxws:client id="serviceClient" serviceClass="com.your.ServiceClass" address="${webservice.soap.address}" >
<jaxws:inInterceptors>
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:outInterceptors>
</jaxws:client>
...
applicaiton.properties
---------------------
webservices.http.auth.username=userName
webservices.http.auth.password=Password
webservice.soap.address=https://your.service.url/services/service
a) by mentioning the SOAP Address in the name attribute. which your can find in your WSDL
Ex: if in your WSDL..
<wsdl-definitions ... targetNamespace="http://your.target.namespace.com/" ...>
...
<wsdl:port binding="tns:YourServiceSoapBinding"
name="YourServiceImplPort">
<soap:address location="https://your.service.url/services/service" />
Then
...
xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
xmlns:sec="http://cxf.apache.org/configuration/security"
...
<http-conf:conduit name="https://your.service.url/services/service">
<http-conf:authorization>
<sec:UserName>${webservices.http.auth.username}</sec:UserName>
<sec:Password>${webservices.http.auth.password}</sec:Password>
<sec:AuthorizationType>Basic</sec:AuthorizationType>
</http-conf:authorization>
</http-conf:conduit>
Or
b) name attribute should be {targetNamespace}portName.http_conduit
<http-conf:conduit name="{http://your.target.namespace.com/}YourServiceImplPort.http_conduit">
<http-conf:authorization>
<sec:UserName>${webservices.http.auth.username}</sec:UserName>
<sec:Password>${webservices.http.auth.password}</sec:Password>
<sec:AuthorizationType>Basic</sec:AuthorizationType>
</http-conf:authorization>
</http-conf:conduit>

Categories