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.



8/15/2014

MongoDB escape special characters in a collection

Regarding how to escape dots in map key, please refer to below page:

MongoDB-Escape dots '.' in map key


If we have a collection named "test-core". then we do the operation using script directly, like:
db.test-core.ensureIndex( { userid: 1 } )
The server will throw exception: ReferenceError: core is not defined

This is because the character '-' inside the collection name is not correctly read.

The way to resolve is : 
db["test-core"].ensureIndex( { userid: 1 } )

6/10/2014

Unit Test for Web Services Client who use Spring Template

Recently was doing one client project which will call the web services provided by our core platform. That project is using SpringTemplate to call the Web services. How to write Junit tests for the client code brought up my interest.

After doing some research, the most commonly used method is to use Mock, of course. Some uses Mockito or EasyMock, which are all fine to me. But what I thought was we are using Spring framework anyway, there should be some Mock classes specially for SpringTemplate, and it should already be there. Come on, it's SPRING! In this case, we do not need to write Mock classed for SpringTemplate ourselves. There it is, as I thought, there are plenty of Mock classes sitting there for easy usage.

I made several test cases, although setup the dependencies costs me some time, but that is because of our project's limitation, which is not...you know. All the test cases are easy to implement, and covers well. I'm pretty satisfied. So just gives a record here, because I think it's interesting and the problems that I have encounted may have been faced by other devs too.

Dependencies:

The lib that I use is

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test-mvc</artifactId>
    <version>1.0.0.M2</version>
    <scope>test</scope>
</dependency>

Actually this library has already been integrated into spring-test in Spring version 3.2.X, but our projects are all using Spring 3.1.3.RELEASE. So I have to add it saparately. And be cautious on the dependency conflict(This will really cause trouble...when there are multiple versions of lib exist or same name of class exist but with different content and if the wrong lib/class is loaded, the class not found exception/method not found exception will be thrown, you dont want to see this...).

There are several ways to check the dependencies.

1. Use mvn dependency:tree/mvn dependency:analyze. It will print the structure of the dependencies.
2. You can check on the website: http://mvnrepository.com/artifact/junit/junit/4.10. It will normally tells you which dependencies the jar depends on.
3. You could do Ctrl+shift+T in Eclipse to check how many classes with same name exist in your work space.

The best practice is to make sure there is no dependency conflict. You will never know when it will gives you that exception.

Junit 4.10 and Mockito-all:1.9.5 CANNOT be used, because they all include hamcrest-core:1.1 inside its package which will not work with spring-test-mvc:1.0.0.M2 and there is no way to exclude them. So use Junit 4.11 and Mockito-core:1.9.5 instead.

But, Junit 4.11 and Mockito-core:1.9.5 both have dependency org.hamcrest:hamcrest-core, but they require different version of them. So they are not compatible, and we need to exclude hamcrest-core in both of them and use org.hamcrest:hamcrest-all:1.3

So the dependencies that I use is like:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11/version>
    <exclusions>
        <exclusion>
            <artifactId>hamcrest-core</artifactId>
            <groupId>org.hamcrest</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.9.5</version>
    <exclusions>
        <exclusion>
            <artifactId>hamcrest-core</artifactId>
            <groupId>org.hamcrest</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test-mvc</artifactId>
    <version>1.0.0.M2</version>
    <scope>test</scope>
</dependency>

So if you are using different versions of the dependencies, check before you put it!

Ok, we can go to the main part.

Code to be tested: 

The code that I was testing is:

    @SuppressWarnings("rawtypes")
    @Override
    public long count(UserRef ref) throws IntegrationException {
        try {
            UserMessageCountCriteria criteria = new UserMessageCountCriteria();
            criteria.setIncludeAgentMessage(true);
            criteria.setOrgName(ref.getLoginOrganizationName());
            criteria.setReadStatus(MessageReadStatusType.unRead);
            criteria.setLabels(new ArrayList<String>());
            criteria.getLabels().add(serviceInfo.getMessageCountLabel());
            criteria.setUserName(ref.getUser().getUserName());
            
            ResponseEntity<Map> responseEntity = null;
            HttpEntity<Map<String, Object>> entity = new HttpEntity<Map<String, Object>>(this.getUserAuthRequestMap(criteria, ref), this.getHttpHeaders());
            responseEntity = restTemplate.exchange(serviceInfo.getServiceMessageCountUrl(), HttpMethod.POST, entity, Map.class);
            
            return (Long) getResponseBody(responseEntity, Long.class);
        } catch (Exception ex) {
            ServiceExceptionHandler.handle(ex, this.getClass().getSimpleName());
        }
        
        return 0L;
    }

protected Object getResponseBody(ResponseEntity<Map> responseEntity, Class clazz) throws IntegrationException {

        if (LOGGER.isDebugEnabled()) {
            try {
                LOGGER.debug(JsonCodec.marshal(responseEntity));
            } catch (Exception ex) {
                throw new IllegalArgumentException(ex);
            }
        }

        if (responseEntity == null) {
            throw new RuntimeException("Response is empty");
        }

        HttpHeaders headers = responseEntity.getHeaders();
        if (headers == null) {
            throw new RuntimeException("Response Header is empty");
        }

        Map<String, Object> responseBody = responseEntity.getBody();
        Map<String, String> status = (Map<String, String>) responseBody.get("status");
        String statusCode = status.get("code");
        String statusMsg = status.get("message");
        if (!StringUtils.equals(statusCode, serviceInfo.getAPISuccessCode())) {
            throw new IntegrationException(statusCode, statusMsg);
        }

        Object object = responseBody.get("content");
        if (clazz == null) {
            return object;
        }
        
        if(object == null)
            return null;

        try {
            String json = JsonCodec.marshal(object);
            if (StringUtils.isEmpty(json)) {
                return null;
            }
            return JsonCodec.unmarshal(clazz, json);
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex);
        }
    }

It's pretty basic.

Test Case:

First I create one Base class to setup my Mock server, this class can be extended by all the other service test class.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/test-portalintegration-servlet.xml" })
public class ServiceClientImplTestBase {

    private static final Logger logger = Logger.getLogger(ServiceClientImplTestBase.class);
    
    protected MockRestServiceServer mockServer;
    
    @Autowired
    private ApplicationContext appContext;
    
    @Autowired
    protected MessageServiceClient messageServiceClient;
    
    @Autowired
    protected ServiceInfo serviceInfo;
    
    @Before
    public void setUp() {
        mockServer = MockRestServiceServer.createServer((RestTemplate) appContext.getBean("restTemplate")); // (1)
        
    }
    
    protected String loadFile(String path){
        
        byte[] content;
        try {
            content = IOUtils.toByteArray(this.getClass().getClassLoader().getResourceAsStream(path));
        } catch (IOException e) {
            logger.error(String.format("failed to load file from classpath: %s", path), e);
            return null;
        }
        
        return new String(content);
    }
}

Then I create one class to test one of the service client.

public class MessageServiceClientImplTest extends ServiceClientImplTestBase{
    @Test
    public void countTest() throws Exception {
        mockServer.expect(RequestMatchers.requestTo(serviceInfo.getServiceMessageCountUrl()))
        .andExpect(RequestMatchers.method(HttpMethod.POST))
        .andRespond(ResponseCreators.withSuccess(loadFile("testdata/sample-response/message-count-success-response.json"), MediaType.APPLICATION_JSON)); // (2)

        UserRef userRef = this.getUserRef("dummy"); //this method just helps me create one dto. Nothing special.
        
        long result = messageServiceClient.count(userRef);
        
        Assert.assertEquals(1, result); // (3)
        mockServer.verify(); // (4)
    }
}

Then you can run the countTest() and it works perfect.

This example is just a very basic Mock, showing you how it works. Depends on your service and client requirement, you need to customize the expect rule to verify the functionality of your client impl.

This is really easy. I like it.

End.