9/26/2014

Spring RestTemplate does not capture response body when error code 40x is returned

When consuming REST web services using SpringTemplate default configuration, when the response returns error HTTP status code, SpringTemplate will throw HttpClientErrorException . But you can still get the response body by calling:

clientEx.getResponseBodyAsByteArray();

OR

clientEx.getResponseBodyAsString();

But there is one special case, when error code is 40x, it fails to capture the response body.

Reason is described in ticket SPR-9999

While solution is quite simple as some one mentioned in the ticket. Use the "org.springframework.http.client.HttpComponentsClientHttpRequestFactory" instead of the default one.

So the configuration should be:

<bean id="clientHttpRequestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"/>
    
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
     <property name="requestFactory" ref="clientHttpRequestFactory"/>
</bean>




9/10/2014

Spring Test: Inject Mock Object into proxy-based Spring bean.(Like bean with @Transactional or @Aync ...)

Spring Test, which I have to say, is the perfect lib for testing projects build on Spring.

There is one very useful class called "ReflectionTestUtils". With that, we dont need to put any setter or getter  for autowired fields inside our services and we can partially replace one autowired field/service to our defined mock object. Very convenient and flexible.

Example:

FooDao mockFooDao = Mockito.mock(FooDao.class);
Mockito.when(mockFooDao.foo()).thenReturn("Var");
ReflectionTestUtils.setField(fooService, "fooDao", mockFooDao);

There is one thing we need to pay attention, is, component bean may be wrapped by proxy class when we use annotations from spring like @Transactional or @Aync. In this case, we need to unwrap the bean before we inject the mock object, otherwise, the "ReflectionTestUtils" cannot inject the mock object, because the "fooService" is proxy object, does not have the "fooDao" field inside it!

So we need to unwrap the bean like this:

protected Object unwrapService(Object service) throws Exception {
   if(AopUtils.isAopProxy(service) && service instanceof Advised) {
 Object target = ((Advised) service).getTargetSource().getTarget();
 return target;
   }
   return null;
}
And:
ReflectionTestUtils.setField(unwrapService(fooService), "fooDao", mockFooDao);

In this way, it works perfect.

Enjoy.