Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Thursday, 14 July 2016

[Spring / Async] How to invoke method in separate thread ?

[Spring / Async] How to invoke method in separate thread ? Some tasks need to be invoked in a separate thread - asynchronously. For instance when the operation needs a lot of time to finish. You just want to run it and return its id or some kind of status. In pure Java you would use Threads / Runnables and other stuff available in low-level cuncurrency API which JDK provides. Actually it isn't very convenient so Spring developers have taken care of that. I've prepared very simple Spring application which contains only two beans: RequestHandler and OperationRunner. The first one invokes the other. Here's the application runner:
    public class App {
        public static void main(final String [] args) throws InterruptedException {
            final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
            final RequestHandler handler = context.getBean(RequestHandler.class);
            handler.handle();
        }
    }
And a configuration:
@Configuration
@ComponentScan("gt.dev.spring.async")
public class AppConfig {
}
Implementation of components:
    @Component
    public class RequestHandler {
        private static final Logger LOG = Logger.getLogger(RequestHandler.class);

        private final OperationRunner operationRunner;

        @Autowired
        public RequestHandler(final OperationRunner operationRunner) {
            this.operationRunner = operationRunner;
        }

        public void handle() throws InterruptedException {
            LOG.info("Request processing started. Invoking operation...");
            operationRunner.run();
            LOG.info("Request processed");
        }
    }
And OperationRunner:
    @Component
    public class OperationRunner {
        private static final Logger LOG = Logger.getLogger(OperationRunner.class);

        public void run() throws InterruptedException {
            LOG.info("Operation started. Sleeping...");
            Thread.sleep(5000);
            LOG.info("Operation finished");
        }
    }
After running main method in App class I get the following logs:
maj 07, 2015 3:39:28 PM gt.dev.spring.async.RequestHandler handle
INFO: Request processing started. Invoking operation...
maj 07, 2015 3:39:28 PM gt.dev.spring.async.OperationRunner run
INFO: Operation started. Sleeping...
maj 07, 2015 3:39:33 PM gt.dev.spring.async.OperationRunner run
INFO: Operation finished
maj 07, 2015 3:39:33 PM gt.dev.spring.async.RequestHandler handle
INFO: Request processed
It works as expected:
  • RequestHandler logs information which indicates that process has been started
  • RequestHandler invokes OperationRunner
  • OperationRunner logs that operation started
  • OperationRunner performs long operation (sleeps for 5 seconds)
  • OperationRunner logs that operation finished
  • control gets back to RequestHandler
  • RequestHandler logs that request has been processed
Every client has to wait until the operation is completed. In some cases we may want to return some kind of id of operation to the client and process in the background asynchronously. Let's make the run() method async. To do that we need @Async annotation that indicates that particular method should be run in separate thread.
    @Component
    public class OperationRunner {
        private static final Logger LOG = Logger.getLogger(OperationRunner.class);

        @Async
        public void run() throws InterruptedException {
            LOG.info("Operation started. Sleeping...");
            Thread.sleep(5000);
            LOG.info("Operation finished");
        }
    }
Spring also has to know that async calls are enabled so we need one additional annotation in spring configuration - @EnableAsync. Lots of people forget about that. It's actually quite clever. You can put @Async on all the methods that need to be asynchronous and when needed disable all async calls by removing @EnableAsync.
    @Configuration
    @ComponentScan("gt.dev.spring.async")
    @EnableAsync
    public class AppConfig {
    }
After running the app I get the following log which proves that request has been processed before the async operation finished.
maj 07, 2015 3:54:13 PM gt.dev.spring.async.RequestHandler handle
INFO: Request processing started. Invoking operation...
maj 07, 2015 3:54:13 PM gt.dev.spring.async.RequestHandler handle
INFO: Request processed
maj 07, 2015 3:54:13 PM gt.dev.spring.async.OperationRunner run
INFO: Operation started. Sleeping...
maj 07, 2015 3:54:18 PM gt.dev.spring.async.OperationRunner run
INFO: Operation finished

Wednesday, 10 February 2016

[Java8 / Spring / Test] How to test TransactionTemplate's execute() method ?

If your app uses Spring framework you may be familiar with either @Transactional or TransactionTemplate. Altough TransactionTemplate couples your app with Spring a lot of people use it. In most cases it's being injected into DAOs or some kind of AbstractDAO. DAO is an object which typically is tested by integration test which enables in-memory database. In most cases you won't need unit testing here but what if TransactionTemplate has been injected into some kind of service / transaction etc - in general a class which has to be unit tested ? There is one problem with TransactionTemplate - execute() method takes TransactionCallback as a parameter. This is how you would invoke it:
transactionTemplate.execute((s) -> propertyDao.persist(copyOf(toSave).withNewResourceId(accountId)));
If you mock TransactionTemplate then propertyDao.persist() will never be invoked. In my unit test PropertyDao is a mock so now I cannot use Mockito.verify() to check whether persist method has been invoked (it returns void).
private final PropertyDao propertyDao = mock(PropertyDao.class);
Let's see how execute() method has been implemented:
    @Override
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
        }
        else {
            TransactionStatus status = this.transactionManager.getTransaction(this);
            T result;
            try {
                result = action.doInTransaction(status);
            }
            catch (RuntimeException ex) {
                // Transactional code threw application exception -> rollback
                rollbackOnException(status, ex);
                throw ex;
            }
            catch (Error err) {
                // Transactional code threw error -> rollback
                rollbackOnException(status, err);
                throw err;
            }
            catch (Exception ex) {
                // Transactional code threw unexpected exception -> rollback
                rollbackOnException(status, ex);
                throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
            }
            this.transactionManager.commit(status);
            return result;
        }
    }
The most important line:
result = action.doInTransaction(status);
It simply means that our function:
transactionTemplate.execute((s) -> propertyDao.persist(copyOf(toSave).withNewResourceId(accountId)));
is being invoked in the method so when you mocked the template it won't happen at all. How to deal with that ? My first thought was to use ArgumentCaptor to catch the parameter passed to execute method and invoke it but I think I found a better way.
class FunctionCallingTransactionTemplate extends TransactionTemplate {
        @Override public <T> T execute(TransactionCallback<T> action) throws TransactionException {
            final TransactionStatus irrelevantStatus = null;
            return action.doInTransaction(irrelevantStatus);
        }
    }
In the code above I extend TransactionTemplate so that in only invokes the action passed to execute() method without other stuff. I guess I'm gonna need this in many tests so we can create a simple trait:
public interface FunctionCallingTransactionTemplateTrait {
    default TransactionTemplate functionCallingTransactionTemplate() {
        return new FunctionCallingTransactionTemplate();
    }

    class FunctionCallingTransactionTemplate extends TransactionTemplate {
        @Override public <T> T execute(TransactionCallback<T> action) throws TransactionException {
            final TransactionStatus irrelevantStatus = null;
            return action.doInTransaction(irrelevantStatus);
        }
    }
}
Now in my test I have:
public class SaveAccountAttributesTransactionTest implements FunctionCallingTransactionTemplateTrait {
    private final ArgumentCaptor propertyCaptor = ArgumentCaptor.forClass(Property.class);
    private final PropertyDao propertyDao = mock(PropertyDao.class);
    private final TransactionTemplate transactionTemplate = functionCallingTransactionTemplate();

    private final SaveAccountAttributesTransaction transaction = new SaveAccountAttributesTransaction(propertyDao, transactionTemplate);
    ...
}
And some test:
    @Test
    public void shouldUpdateOneValueAndPersistOther() throws Exception {
        // given
        when(propertyDao.fetchResourceProperties("root", ACCOUNT)).thenReturn(Lists.newArrayList(
                propertyOf("firstProp", "2.21", "root"),
                propertyOf("secondProp", null, null),
                propertyOf("thirdProp", null, null),
                propertyOf("fourthProp", null, null)
        ));
        SaveAccountAttributesEvent event = new SaveAccountAttributesEvent("root", Lists.newArrayList(
                propertyOf("firstProp", "2.22", "root"),
                propertyOf("secondProp", null, null),
                propertyOf("thirdProp", "1.11", "root"),
                propertyOf("fourthProp","default", null)
        ));

        // when
        transaction.execute(event);

        // then
        verify(propertyDao).updatePropertyValue(anyString(), eq("2.22"));
        verify(propertyDao).persist(propertyCaptor.capture());
        assertThat(propertyCaptor.getAllValues()).extracting(Property::getResourceId, Property::getValue)
                .containsOnly(tuple("root", "1.11"));
    }
And it passess :) As you can see I verify behaviour of propertyDao which is being invoked by our extended TransactionTemplate. Hope it helps.

Monday, 27 April 2015

[Spring] How to create global exception handler ?

Sometimes you may want to handle exceptions globally. Let's say you have a service which exposes several rest endpoints which are conntected to the same database. Your data access objects may throw DatabaseConnectionException (or something like that) when db is not available. In such case the response should contain proper HTTP status code and error message. Sample rest endpoint:
@RestController
public class TemperatureEndpoint {
 @Inject
 private WeatherService temperatureService;

 @RequestMapping(
  value = "/temperature/{city}",
  method = RequestMethod.GET,
  produces = "application/json"
 )
 @ResponseBody
 public Temperature currentTemperature(@PathVariable("city") String city) {
  return temperatureService.getCurrentTemperatureIn(city);
 }
}
And another one:
@RestController
public class HumidityEndpoint {
 @Inject
 private HumidityService humidityService;

 @RequestMapping(
  value = "/humidity/{city}",
  method = RequestMethod.GET,
  produces = "application/json"
 )
 @ResponseBody
 public Humidity currentHumidity(@PathVariable("city") String city) {
  return humidityService.getCurrentHumidityIn(city);
 }
}
Both HumidityService and TemperatureService use the same database so you may use some unified runtime exception like DatabaseConnectionException. Handling the exception in both services isn't a good idea because the result will in most cases be the same. By the way each try-catch block makes you to write additional unit test so global handler seems to be a natural choice. Spring allows you to create a global exception handler (will be applied to all the rest controllers) using @ControllerAdvice annotation. Typically class annotated with ControllerAdvice contains methods which map exceptions to HTTP status codes and messages. In well-designed applications it can be the only place where exception handling occurs. Let's create some mappings. Each mapper should also generate proper json response so the client can handle it. Let's create a class annotated with @ControllerAdvice (note that the class has to be in scope of component scan).
@ControllerAdvice
public class WeatherServiceExceptionHandler {
        private static final Logger LOG = LoggerFactory.getLogger(WeatherServiceExceptionHandler.class);
}
I've added a logger because we may want to log something. Let's get back to DatabaseCommunicationException. In case the exception is thrown I want my service to log proper information and return exception's message in json so the client can handle it properly.
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(DatabaseCommunicationException.class)
public void handleDatabaseConnectionException(DatabaseCommunicationException e) { 
 LOG.error("DatabaseCommunicationException occurred", e);
}
As you can see I've created a method which takes as a parameter DatabaseCommunicationException so I can log its content. @ExceptionHandler annotation tells Spring that the method handles DatabaseCommunicationException exception. @ResponseStatus allows you to specify which HTTP status will be assigned to the response when this particular handler is being fired. In this case WeatherService cannot work without database connection so I chose 500 which is internal server error. You can obviously use whichever code you want to. For now the handler only logs a message and returns http code but it should also return a message to the client. In all RestControllers I use jackson to map POJO cleassess to json. It can be used in ControllerAdvice as well. Jackson dependency:
<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-databind</artifactId>
</dependency>
In this example json response will only contain a message but you can add here anything else. My response is just a sample immutable POJO. It looks as follows:
public class ErrorMessage {
 private final String message;

 public ErrorMessage(String message) {
  this.message = message;
 }

 public String getMessage() {
  return message;
 }
}
Now the handler should return ErrorMessage and indicate that response should be converted into json format. Complete method looks like this:
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(DatabaseCommunicationException.class)
@ResponseBody
public ErrorMessage handleDatabaseConnectionException(DatabaseCommunicationException e) {
 LOG.error("DatabaseCommunicationException occurred", e);
 return new ErrorMessage(e.getMessage());
}
Let's add another one. This time I want to tell the user that the service doesn't support requested city. Fo instance the user wants to get current temperature in Goszowice (which is very small village in southern Poland) but the database simply doesn't contain any data about this place. In such case TemperatureService throws UnsupportedCityException. I guess HttpStatus.NOT_FOUND (404) fits best.
@ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(UnsupportedCityException.class)
@ResponseBody
public ErrorMessage handleUnsupportedCityException(UnsupportedCityException e) {
 LOG.error("UnsupportedCityException occurred", e);
 return new ErrorMessage(e.getMessage());
}
As you can see bodies of both handlers are almost the same so it can be extracted to new method. It's just an example so let it be as it is. Complete code:
@ControllerAdvice
public class WeatherServiceExceptionHandler {
 private static final Logger LOG = LoggerFactory.getLogger(WeatherServiceExceptionHandler.class);

 @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
 @ExceptionHandler(DatabaseCommunicationException.class)
 @ResponseBody
 public ErrorMessage handleDatabaseConnectionException(DatabaseCommunicationException e) {
  LOG.error("DatabaseCommunicationException occurred", e);
  return new ErrorMessage(e.getMessage());
 }

 @ResponseStatus(value = HttpStatus.NOT_FOUND)
 @ExceptionHandler(UnsupportedCityException.class)
 @ResponseBody
 public ErrorMessage handleUnsupportedCityException(UnsupportedCityException e) {
  LOG.error("UnsupportedCityException occurred", e);
  return new ErrorMessage(e.getMessage());
 }
}
You can obviously provide a better way of constructing error message because you may not want to expose your internal messages. Something like this looks a bit better:
private static final ImmutableMap<Class, String> EXCEPTION_TO_MESSAGE_MAP = ImmutableMap.<Class, String>of(
   DatabaseCommunicationException.class, "Service is not available. Please try again later.",
   UnsupportedCityException.class, "Service does not track temperature in requested city."
);
That would be all :) Cheers

Friday, 10 April 2015

[Spring] How to test RestController using MockMvc ?

If you work in a software company you probably write integration tests which deploy your product into application server and then test functionalities exposed by web services. Integration tests ran by continuous integration server are very useful. Especially when the whole team develops some kind of functionality. After each commit you can easily check whether everything works fine and continue the development. Unfortunately it takes time... Typical workflow may look like this one: 1. Developer commits the code. 2. The code is being built on CI server. 3. Test environment has to be upgraded (war deployment). 4. The tests. It strongly depends on how big your project is but I'm sure you will have to wait for a while. Perfect tests should work as fast as unit tests. You should be able to run whole test suite and see results after seconds. And here comes Spring. Each rest controller can be conveniently tested using MockMvc. In order to do that you need to add spring-test into your dependencies' section:
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <scope>test</scope>
</dependency>
Let's say I want to test following endpoint:
@RestController
public class WeatherEndpoint {
  @Inject
  private WeatherService weatherService;
  
  @RequestMapping(value = "/weather/{city}",
                    method = RequestMethod.GET,
                    produces = "application/json"
  @ResponseBody
  public Weather currentWeather(@PathVariable("city") String city) {
    return weatherService.getCurrentWeatherIn(city);                                    }
}
It's just an example so my WeatherService returns fixed value:
private class WeatherService {
  public Weather getCurrentWeatherIn(String city) {
    return new Weather("20.3");
  }
}

@Bean
public WeatherService weatherService() {
  return new WeatherService();
}
We need to prepare test skeleton which will be able to: 1. Run the test using spring runner. 2. Run application context. 3. Expose instance of MockMvc class.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class})
@WebAppConfiguration
public class WeatherEndpointTest {
    private MockMvc mockMvc;

    @Inject
    private WebApplicationContext context;

    @Before
    public void contextSetup() {
        this.mockMvc = mockMvc();
    }

    private MockMvc mockMvc() {
        return MockMvcBuilders.<StandaloneMockMvcBuilder>webAppContextSetup(context).build();
    }
}
Now I'm going to explain each fragment of this template. @RunWith(SpringJUnit4ClassRunner.class) - indicates that Spring runner will be used. Unfortunately JUnit test can use only one runner so forget about Mockito runner. @ContextConfiguration(classes = {AppConfig.class}) - the runner will setup spring context using configuration classes listed in this annotation. In this example it will use only AppConfig. private MockMvc mockMvc - instance of MockMvc. This object allows to perform operations on rest endpoints. @Inject private WebApplicationContext context - context has to be injected in order to build mockMvc contextSetup() and mockMvc() - before each test mockMvc will be rebuilt Basically that's all. You can now start writing tests. Note that you can inject beans which have been added to spring container. In next episode I will explain how to do that and how to mock some of them in order to make tests isolated. currentWeather() returns Weather object which is a POJO class that contains only one field - (String) temperature. I've added jackson as dependency so the instance of Weather is being mapped to json. Here's our test:
    @Test
    public void shouldReturnInfoAboutWeather() throws Exception {
        // given
        String city = "Wroclaw";

        // when // then
        mockMvc.perform(get("/weather/" + city))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("temperature").value("20.3"));
    }
If you run the test you will see something like: INFO: Mapped "{[/weather/{city}],methods=[GET],params=[],headers=[],consumes=[],produces=[application/json],custom=[]}" onto public gt.dev.mockmvc.WeatherEndpoint.currentWeather(java.lang.String) which means that the context has been run and /weather endpoint exposed. In this particular example MockMvc performs GET on the endpoint and checks the result. In this case I expect http status to be OK (200) and media type json. Additionally I've added:
  <dependency>
   <groupId>com.jayway.jsonpath</groupId>
   <artifactId>json-path</artifactId>
   <scope>test</scope>
  </dependency>
so I can test the result mapped to json. Here I check whether temperature is equal to 20.3 (fixed value). Actually json-path is a really cool tool. You can assert that your json response is valid using very readable methods. The whole test below:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class})
@WebAppConfiguration
public class WeatherEndpointTest {
    private MockMvc mockMvc;

    @Inject
    private WebApplicationContext context;

    @Before
    public void contextSetup() {
        this.mockMvc = mockMvc();
    }

    @Test
    public void shouldReturnInfoAboutWeather() throws Exception {
        // given
        String city = "Wroclaw";

        // when // then
        mockMvc.perform(get("/weather/" + city))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("temperature").value("20.3"));
    }

    private MockMvc mockMvc() {
        return MockMvcBuilders.<StandaloneMockMvcBuilder>webAppContextSetup(context).build();
    }
}
I strongly recommend to experiment with mockMvc. You should definitely check what can be passed to andExpect() method. That's all folks. Next time I'm going to show how to mock services in sping container in order to make integration tests isolated.