Friday, February 26, 2016

Play with spring integration test

This article is mainly about using spring-test for integration test. Some basic issuses of integration test are discussed for projects with following natures:

  • It's a maven project
  • It's also a Spring webmvc proejct
  • It's developed in Eclipse.

This article is based on Spring framework 4.2 +

0. Basics

0.1  Unit test and  integration test

Test target of unit test is every individual POJO, if this target object need other external objects, mock them. In a spring web/rest application, unit tests run without Spring context.  JUnit test runner usually sets to someone from mock framework.  such as @RunWith(MockitoJUnitRunner.class) if Mockito is used.

From "unit test"'s view, it doesn't matter if you application is a Spring mvc application. It even doesn't matter if you application uses spring or not. Because unit tests only verify a bunch of  classes one by one, decouple them with the help of mock.

Test target of integration test is functions of a group of components. In a spring web/rest application, spring-test provide Spring TestContext Frame to simulate a servlet container, so your code can run in a web context without  deployment to a real server. JUnit test runner usually sets to the one provided by spring-test, like @RunWith(SpringJUnit4ClassRunner.class) 

0.2 JUnit, Running test in eclipse, Running test by maven

JUnit is just a test framework, it has no idea of the currently running test is a "unit test" or an "integration test".  Or we can say, a JUnit test case can be a unit test, it can also be a integration test.

Eclipse has no concept of integration test but only unit test. Eclipse has no requirement about test case class names.  e.g a class named "BookRestControllerAbc.java" can run as a JUnit test.

Maven uses plugin "surefire" to run "unit test", use plugin "failsafe" to run "integration" test.  They have decidedly naming conventions.

Surefire plugin by default will automatically run test cases with following names:

  • "**/Test*.java"
  • "**/*Test.java"
  • "**/*TestCase.java"

Failsafe plugin by default will automatically run test cases with following names:

  • "**/IT*.java"
  • "**/*IT.java"
  • "**/*ITCase.java"

So the test cases can be categoried by their class names.

0.3 Watch out dir /src/main/webapp

Even it's a maven project in eclipse, eclipse and maven have their own way to manage dependencies and resources. This is the key to understand the problems like "Why I can run my test case in eclipse but will can't run the same test in maven build?" or the reverse one,  "Why  I can my test case with maven but can't run it in Eclipse". Usually the cause of these kinds of problems seem like can't find some resources in a certain senario.

Here is the dilemma:

In Eclipse, create a maven web application will create directory /src/main/webapp as web root.

web.xml must in directory /src/main/webapp/WEB-INF. The default spring configuration, either applicationContext.xml or dispatcher-servlet.xml  required by Spring webmvc need to be in the same directory /src/main/webapp/WEB-INF.

But by default /src/main/webapp is NOT a resource directory for both maven and Eclipse.

You can succeed in "Run on Server" in Eclipse, because web root /src/main/webapp also get packaged with the help of package behavior for war in Eclipse.

But for integration tests, they will definitly fail no matter using maven or in Eclipse.   Since neither applicationContext.xml nor dispatcher-servlet.xml  in web/main/webapp are recognized as resources. The error message will be some kind of file not found.

If you simply move, e.g dispatcher-servlet.xml from /src/main/webapp/WEB-INF/ to maven's resource path like /src/main/resources/WEB-INF/,  the application can no longer run in Eclipse.  The error message will complain can not find /WEB-INF/dispatcher-servlet.xml or similar.

So here's the best way in my experience:

  1. Move spring configuration applicationContext.xml or dispatcher-servlet.xml to maven resources path /main/resources
  2. In web.xml, set context location for servlet dispatcher like below
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 
    <!--  specify location of spring config, don't use default -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:dispatcher-servlet.xml</param-value>
    </init-param>
    
    <load-on-startup>1</load-on-startup>
  </servlet>

The final project hierarchy like below.

image

 

Since resources are now in the path recognized by both Maven and Eclipse, we can successfully run our web application in Eclipse as well as run integration test in maven command line or in Eclipse.

This probably make you stop using xml-styled spring configuration and embrace the Java-styled spring configuration for the sake of a easier life.

1. Config pom.xml for integration test

Maven has failsafe plugin for integration test.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.19.1</version>
  <configuration>
    <encoding>UTF-8</encoding>
  </configuration>
  <!-- uncomment to bind maven lifecycle
  <executions> 
    <execution> 
      <id>integration-test</id> 
      <goals> 
        <goal>integration-test</goal>
        <goal>verify</goal> 
      </goals> 
    </execution> 
  </executions> 
  -->
</plugin>

Without bind to any lifecycle like above, you can run integration test with command

mvn failsafe:integration-test

2. Spring integration test case with JUnit

Cheat sheet for creating Spring integration test with JUnit.

  1. Set JUnit runner @RunWith(SpringJUnit4ClassRunner.class)
  2. Load spring config with @WebAppConfiguration and @ContextConfiguration("classpath:dispatcher-servlet.xml")
  3. @Autowire WebApplicationContext wac in JUnit test case class.
  4. Create MockMvc in JUnit's @Before method, mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
  5. In test case method, use MockMvc.perform(MockMvcRequestBuilders.xxx()) to send request and andExpect(MockMvcResultMatchers.yyy) to verify response.

3. possible issues

3.1 javax.servlet.ServletContext  Not Found

This error message often happens when running integration test with plugin failsafe. You may see an error message like below.

Failed to instantiate [org.springframework.test.context.web.WebDelegatingSmartContextLoader]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: javax/servlet/ServletContext

Add the following dependency can solve the problem.

  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
  </dependency>

Make sure the major version (3.0) should be same as specified in web.xml.

3.2 Could not obtain DataSource when using @Sql + JPA

When testing, you probably like to use memory database such as HSQL, H2 or Derby.  If all the connection configurations. e.g driver, url, username and password, are in JPA /META-INF/persistence.xml file, not in spring's configuration, you may see an error like below complain couldn't obtain DataSource from transaction manager when using @Sql to insert test data before a certain test case.

java.lang.IllegalStateException: Failed to execute SQL scripts for test context ..(some text ignored).. could not obtain DataSource from transaction manager [org.springframework.orm.jpa.JpaTransactionManager] (named '').

This because the @Sql need DataSource in spring context, just add a DataSource in your spring configuration. Make sure the this datasource point to the same database as  in the persistence.xml. For example, if the persistence.xml using a H2 database like below,

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
 xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="spring-persistence-jpa-tx" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
  
   <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
   <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:mydb" />
   <property name="javax.persistence.jdbc.user" value="sa" />
   <property name="javax.persistence.jdbc.password" value="" />
   
   <!-- Automatically drop then create table -->
   <property name="hibernate.hbm2ddl.auto" value="create" />
   <!-- print out sql  -->
   <property name="hibernate.show_sql" value="true"/>
  </properties>
 </persistence-unit>
</persistence>

Then we need to add a DataSource bean in Spring configure to be able to use @Sql annotation. In xml-styled configuration, with spring-jdbc's help, it's easy to create a datasource using embedded database.

<?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:beans="http://www.springframework.org/schema/beans"
  xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  xsi:schemaLocation="
    http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

  <!--  omit other beans  -->
  
  <jdbc:embedded-database id="usually_is_dataSource" type="H2" database-name="mydb">
    <!-- 
    <jdbc:script location="classpath:anyfile1.sql"/>
    <jdbc:script location="classpath:anyfile2.sql"/>
     -->
  </jdbc:embedded-database>
</beans>

Notice if using the embedded memory, the database'name is important. The database-name MUST be same as what in persistence.xml. In  this demo it's 'mydb'.

4. A complete demo

In the end of the article,a demo of integration test for get resource of a REST web service is provided. It inerts test data before test running, then runs the junit test case try to access a url and check the status of the response is 200. Finally, restores the database by deleting the inserted test data.

package com.shengwang.demo.controller;

// import ignored

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({"classpath:/WEB-INF/dispatcher-servlet.xml"})
public class BookRestControllerIT {
  @Autowired
  WebApplicationContext wac;
  private MockMvc mockMvc;

  @Before
  public void setUp() throws Exception {
    mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
  }

  @Test
//  @Sql(scripts="classpath:insertOneBook.sql")
  @SqlGroup ({
      @Sql(statements="insert into book (book_id,title) values (1,'Test title')"),
      @Sql(statements="delete from book where book_id=1",executionPhase=ExecutionPhase.AFTER_TEST_METHOD)
  })
  public void testFindBookWithExistBookId() {
    try {
      mockMvc.perform(get("/book/1")).andExpect(MockMvcResultMatchers.status().isOk());
    } catch (Exception e) {
      fail("testFindBookWithExistBookId failed");
    }
  }
}

It passes successfully in Eclipse.

image

It also passes in maven command line invocation. 

mvn clean test-compile failsafe:integration-test

5. More

Here provides a hello world level spring integration test case. In read usage, you should separate your unit test cases from integration test cases. That need more configuration in Eclipse (in Java Build Path) and in Maven pom.xml (with the help of plugin build-helper-maven-plugin) respectively. See another tutorial on "How to separate integration test from unit test"

Sunday, February 21, 2016

Exception handling in spring mvc/rest application

0. What you need

  • Spring framework 4.x

1. Goal of Exception handling

First let's clarify the goal for exception handling in Spring mvc/rest application:

Goal 1. Deprive exception handling code from business logic to make code cleaner. The fundamental try-catch mix the business code with exception handling code in methods of Controller, we want to separate them.

Goal 2. Fully cover all exception. Final user should not see any exception trace or  server default return page.  No pages like below should return to final user. Or in other words, there should be a default exception view for unhandled exceptions in spring application.

image

image

 

Goal 3. Different exceptions can result in different views.  e.g any **CustomerException  return /customerError page, any **OrderException return /orderError page. The web application can has more views for different exceptions

Goal 4. Mappings between exception and the output view should be configurable, not hard coded.

Goal 5. Modify mappings between exception and the output view doesn't need to change any business code.

2. Four common ways to handle exception in Sping MVC

2.1 @ResponseStatus + User defined Exceptions (Not recommend).

@ResponseStatus can be used at user defined exception class, which means if this exception is not handled by anyone else , the return code will be set to a specified value. It's not recommend because it only set the response http status code and then let the server to use its default page for that status code.  Usually it'a a ugly html page. For example if a ResourceNotFoundException defined as below, with @ResponseStatus before class definition.

@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="some description text")
public class ResourceNotFoundException extends RuntimeException { }

Notice the application defined exception extends from RuntimeException, it's an unchecked exception. The controller BookController.java doesn't seem to have any exception related code.

package com.shengwang.demo.controller;

//... import ignored ...

@RestController
@RequestMapping("/book")
public class BookController {
 @Autowired
 private BookService bookService;

 @RequestMapping(value = "/{bookId}", method = RequestMethod.GET)
 public ResponseEntity findById(@PathVariable long bookId) {
  Book book = bookService.findById(bookId); // may throw ResourceNotFoundException  
  return ResponseEntity.ok(book);
 }
}

If the bookService.findById() throw a application defined exception ResourceNotFoundException, The final result looks like below.

image

This page is ugly to final user. Furthurmore, this html error page is unsuitable for REST web service. For such case, it's preferable to use ResponseEntity as a return type and avoid the use of @ResponseStatus altogether. The 'not recommend' isn't for using your own exceptions, but for @ResponseStatus.

2.2 @ExceptionHandler in Controller

@ExceptionHandler is the key annotation to separate exception handling code from the normal business logic.  @ExceptionHandler in a controller class only works for that controller.

package com.shengwang.demo.controller;

//... import ignored ....

@RestController
@RequestMapping("/book")
public class BookController {
 @Autowired
 private BookService bookService;

 @RequestMapping(value = "/{bookId}", method = RequestMethod.GET)
 public ResponseEntity<Book> findById(@PathVariable long bookId) {
  Book book = bookService.findById(bookId); // throw ResourceNotFoundException here  
  return ResponseEntity.ok(book);
 }
 
 // handle any ResourceNotFoundException thrown from all methods of this controller
 @ExceptionHandler(ResourceNotFoundException.class)
 private ResponseEntity<Void> handleResourceNotFoundException(ResourceNotFoundException e) {
  return ResponseEntity.notFound().build();
 }
}

By using @ExceptionHandler, we extract exception handling to another method. make the code cleaner, especially when there are more methods may throw this ResourceNotFoundException exception. The exception handler method can return ResponseEntity<Void or YourErrorInfoClass> for REST APIs or ModelAndView / String as view name for other kind of Web applications to display error page. 

2.3 @ControllerAdvice + @ExceptionHandler

@ExceptionHandler in a controller class only works for that controller. What if more controllers all need to handle the same exception?   Annotation @ControllerAdvice can help to weaving exception handler method into more controller classes using the AOP way.  This is pretty much same as using @ExceptionHandler in controller, the only difference is extracting all @ExceptionHandler method originally located in controllers  in to an independent class with annotation @ControllerAdvice.

Another benefit of using @ControllerAdvice is making a default exception handler for all unhandled exceptions(handle Exception.class) easily.

package com.shengwang.demo.controller;

//... import ignored ....

@ControllerAdvice
public class GlobalDefaultExceptionHandler {
 @ExceptionHandler(Exception.class)
 private ResponseEntity<Void> defaultExceptionHandler(Exception e) {
  // usually will log the exception first
  return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
 }
}

This can make sure final user will not see the exception call trace.

2.4 Defined your own HandlerExceptionResolver

What does an excpetion resolver in spring mvc do? It decides which view to display when a certain exception is thrown and not unhandled in other place of your code.

What is a excetion resolver in spring mvc? Any bean implement interface org.springframework.web.servlet.HandlerExceptionResolver.

public interface HandlerExceptionResolver {

  //only one method in this interface, return ModelAndView
  ModelAndView resolveException(HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler,
                              Exception ex);
}

Notice the return type is ModelAndView, so it may be used more in NON-REST web applications to display an error page.

How to define you own HandlerExceptionResolver? Usually by using spring's SimpleMappingExceptionResolver class directly, or extending from it. Class SimpleMappingExceptionResolver allows you to set a mapping between exception and view's name,  which can fulfill the goal 3,4, different excpetions can result in different error page.  It also allow you to set a default view for unhandled exceptions.  In xml styled spring configuration, add SimpleMappingExceptionResolver to context.

  <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    
    <!-- set default view for unhandled exception -->
    <property name="defaultErrorView" value="/defaultError"/>
    
    <property name="exceptionMappings">
      <map>
        <entry key="ResourceNotFoundException" value="/resourceError" />
        <!-- another way to set default view for unhandled exception
        <entry key="Exception" value="/defaultError" />
        -->
      </map>
    </property>
    
    <!-- Name of logger to use to log exceptions. Unset by default, so logging disabled -->
    <property name="warnLogCategory" value="example.MvcLogger"/>
  </bean>

By using placeholder, you can move the hard-coded exception name and view name to a properties file to achieve goal 5.

3. When to use what?

There's no fixed rules in spring mvc. My preferable choices are:

  • Extend your own exceptions from RuntimeException.
  • @ExceptionHandler in controller class only for exceptions not common among controllers.
  • For REST APIs, use @ControllerAdvice + @ExceptionHandler (return ResponseEntity<Void or YourErrorInfoClass>)
  • For NON-REST web application,  choice1, @ControllerAdvice+@ExceptionHandler(return ModelAndView or just String as view name). Or choice 2,  your own HandlerExceptionResolver (for more flexible exception-view mapping configuration)

4. More

Besides the above ways, there are more ways in spring mvc for exception handling. e.g. extend the abstract class org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler to return a ResponseEntity. So it can also be used in RESTful applications. The ways to handle exception  in Spring mvc are so flexible, but in real use, don't mix them too much, keep it simple, keep it clean.

See also Spring blog Exception Handling in Spring MVC by PAUL and his demo code on github

Thursday, February 18, 2016

Response for GET/POST/PUT/DELETE in REST web service

Before talking about the details, one thing need to be clarified. HTTP status code is designed far before RESTful web service appears.  So when REST go popular, people just try to use the most reasonable http behaviour in everyone's understanding of REST.  Here in this article I put some summary in my own experience from a developer's view  as well as other web resources such as restapitutorial.com and RFC 7231

Also this article provide demo implementation for REST by Spring webmvc, focused not on how to build rest service itself but on what to return for Get/Post/Put/Delete request for REST service.

0. Brief for HTTP status code

  • 2xx, group for success
  • 3xx, group for redirection
  • 4xx, group for client error. Client side has make a incorrect request
  • 5xx, group for server error. Server failed to fulfile the request.

In a developer's view, rest client API should return normally if the response status code is 2xx. Rest client API should throw an Excpetion if the response status code is 4xx. For example Spring's rest client, org.springframework.web.client.RestTemplate, works in that way and rightly so. 

1. Summary of REST response

image

More explanation about the above table:

The above table assume all operation is allowed. If a operation, e.g. delete on /resource to perform a delete-all operation,  is not permitted, a 405 (Method Not Allowed) should be returned. In most case, developers don’t need to anything about this. Spring’s DispatcherServlet will do that by default if it can’t find a method for the incoming request.

Usually, response 404 (Not Found) doesn't need response body at all, but can also has a body with value set to null.  Whether adding a null body or not mainly depends on method return type. (See code below for GET and DELETE, they both may return 404, but 404 from GET has a null body, 404 from DELETE hasn't, depend on the return type of the method)

GET

204(No Content) doesn't has response body. It means success but no need to reply anything to client. (Since 204 belongs to 2xx, group of success, use it as "resource not found" for GET response is inappropriate).

In all the REST response, 204(No Content) should be treat as a no-body 200(OK).

POST

Post should only work on url like /resource, not on /resource/id. Because the id of newly created resource should be assigned by server, not by client. The request body of POST needn't has the resource id value. If it has, the value of resource id will be ignored by server.

If the resource already exists in database and can not insert due to any kind of unique constraint, 409(Conflict) should be returned.

The success post response will has a "Location" http header point to newly created resource. The response body can be empty, since the resouce location is set in http header. But sometimes  can also direct return the json for newly created resource for simplicity, client need not to send an extra GET to get the assigned ID for the resource. Spring's RestTemplate class provide methods for both senarios.  It provides methods to get the 'Location' header of Post response.

public URI postForLocation(...)

Also RestTemplate provide methods like below for response with response body.

public <T> T postForObject(...)

PUT

PUT should only work on url like /resource/id, not on /resource, since modify the whole resource seems a little bit weird. The request body of POST needn't has the resource id value. Because the resource id usually on the url path.

PUT is used to update resource, in theory can also be used to create new resource according to RFC7231. But in practical whether allow "createIfNotExist" is totally up to you. Furthurmore, in real project, resources will finally get stored in DBMS like Oracle or MySQL. Usually the resource id (primary key in DB's view) is created automatically either by a identity in your Java code by JPA provider or by a sequence in Database. Which means the resource id should not be determined by client, so I personally like to disable this 'createIfNotExist' feature for PUT, just return a 404 (Not Found) for non-exist resource is good enough.

PATCH

The request body of Patch is not resource itself, but a delta. See RFC 6902 for the Json patch format.  PATCH is also used to update reousrce. It can be more effecient and atomic than PUT, since PUT need the whole resource in the request body, PATCH request body only has the difference.

DELETE 

If the delete target doesn't exist, return 404(Not Found). In some special case, the server take the delete request, but will delete the resource in async way, which means return client a 202(Accept) before the resource really get deleted.

2. Examples for REST return

Here are examples implemented with Spring webmvc for all REST method. Spring version is 4.2.1.RELEASE. With ResponseEntity's help, it's very easy to set response header, body and status code. Again examples are only focused on what to return. Suppose we have a resource entity called User. We also has 2 application defined exceptions,  ResourceAlreadyExistException and ResourceNotFoundException.

GET return demo

 @RequestMapping(path="/user",method=RequestMethod.GET)
 public ResponseEntity<List<User>> findAll() {
  // read from database
   List<User> users = userService.findAll();
  return ResponseEntity.ok(users);  // return 200, with json body
 }
 
 @RequestMapping(path="/user/{userId}",method=RequestMethod.GET)
 public ResponseEntity<User> findById(@PathVariable long userId) {
  try {
   // read from database
   User user = userService.findById(userId);
   return ResponseEntity.ok(user);  // return 200, with json body
  } catch (ResourceNotFoundException e) {
   return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); //return 404, with null body
  }
 }

The above 2 methods, first one return resource collection, second one return a specified single resource. If resource not found, return 404. Here need to set body to null to make the return type consistent as ResourceEntity<User>. (Of course, you can also use @ExceptionHandler or @ControllerAdvice to provide a more consistent return for all exceptions in real project)

POST return demo

 @RequestMapping(path="/user",method=RequestMethod.POST)
 public ResponseEntity<Void> create(@RequestBody User user) throws URISyntaxException {
  try {
   // save to database
   User newUser = userService.saveUser(user);
   return ResponseEntity.created(new URI("/user/"+newUser.getUserId())).build();
  } catch (ResourceAlreadyExistException e) {
   // log excpetion first, then return Conflict (409)
   return ResponseEntity.status(HttpStatus.CONFLICT).build();
  }
 }

The package capture for above method looks like below, you can find the response has 'Location' set in the header.

image

PUT return demo

Here we disable the 'createIfNotExist' feature for PUT, only allow it to update existing resources.

 @RequestMapping(path="/user/{userId}",method=RequestMethod.PUT)
 public ResponseEntity<Void> updateExist(@RequestBody User user,@PathVariable long userId)  {
  try {
   user.setUserId(userId);
   userService.update(user);
   return ResponseEntity.noContent().build();
  } catch (ResourceNotFoundException e) {
   return ResponseEntity.notFound().build();
  }
 }

DELETE return demo

Here the code snippet doesn't allow to  delete non-exists resource. But if you think in it'ok, you can change return to 204 or 200. 

 @RequestMapping(path="/user/{userId}",method=RequestMethod.DELETE)
 public ResponseEntity<Void> deleteById(@PathVariable long userId) {
  try {
   userService.deleteById(userId);
   return ResponseEntity.noContent().build();
  } catch (ResourceNotFoundException e) {
   return ResponseEntity.notFound().build();
  }
 }

REST client

By using RestTemplate class, It' really easy to make a REST client to consume the service. Here's an example to GET resource.

 RestTemplate restTemplate = new RestTemplate();
 String getUrl = "http://localhost:8080/spring-mvc-helloworld/user/1";
 ResponseEntity<User> entity = restTemplate.getForEntity(new URI(getUrl), User.class);
 HttpStatus statusCode = entity.getStatusCode();
 User user = entity.getBody();

3. Recap

This article mainly describes how to reponse to GET/POST/PUT/DELETE request in REST web service side.   But conventions  can be changed according to your bussiness requirement. But basic principle is always use 2xx for success and 4xx for failure. Spring also provides convenient classes like ResponseEntity and RestTemplate that you can utilize.

Tuesday, February 16, 2016

How to use H2 embeded database in spring application

H2, as a embeded memory database, is mainly used for development and test phase. H2 also has a web console which is very convenient.

image

1. Use H2 in Spring boot application

Spring boot has almost everything done for you. You need to do 2 steps, first inluce H2 in you pom.xml.

  <!-- H2 -->
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
  </dependency>

The version of H2 is managed by spring boot and  scope can be set to runtime because you code should not have any APIs come from H2 itself. Then specify datasource url to use H2 in the application.properties file or in lieu of yaml styled configuration

# define DataSrouce properties 
# use h2 can have a buid in web console http://localhost:8080/h2-console
spring.datasource.url=jdbc:h2:mem:mydb

In spring boot application, there is no need to set username and password for using H2 memory database, because H2's default username is 'sa' (or SA, case insensitive), default password is empty. These default username/password will be auto configured by spring boot.  Also Spring boot will config the H2 web console for you automatcally at http://localhost:8080/h2-console

Use H2 in spring-mvc application (non-spring boot)

In non-spring boot application, which is normall just powered by spring-webmvc and other spring artifacts, using H2 need 3 steps. First is also to include H2 in pom.xml

  <!-- H2 -->
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>1.4.190</version>
  </dependency>

Secondly,  use H2 in any ORM configuration. For example if your project uses JPA as persistence and uses Hibernate as JPA service provider. The JPA's configuration file /META-INF/persistence.xml may looks like below.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
 xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="spring-persistence-jpa-tx" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
   <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
   <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:mydb" />
   <property name="javax.persistence.jdbc.user" value="sa" />
   <property name="javax.persistence.jdbc.password" value="" />
   
   <!-- Automatically drop then create table -->
   <property name="hibernate.hbm2ddl.auto" value="create" />
   <!-- print out sql  -->
   <property name="hibernate.show_sql" value="true"/>
  </properties>
 </persistence-unit>
</persistence>

Specify the jdbc driver, jdbc url and etc , just like other database.

Finally, and optionally, If you want the H2 web console, you need to add a bean into spring context. In xml-styled spring configuration file, it looks like below.

 <bean id="h2WebServer" class="org.h2.tools.Server" factory-method="createWebServer"
  init-method="start" destroy-method="stop">
  <constructor-arg value="-web,-webAllowOthers,-webDaemon,-webPort,8082" />
 </bean>
In java-styled spring configuration, it looks like below.
@Configuration
@ComponentScan
public class JavaConfiguration {
 //... other beans
 
 @Bean(initMethod="start",destroyMethod="stop")
 public org.h2.tools.Server h2WebConsonleServer () throws SQLException {
   return org.h2.tools.Server.createWebServer("-web","-webAllowOthers","-webDaemon","-webPort", "8082");
 }
}

The -webDaemon means run H2 web console in a daemon thread which will not block the application process termination. -webPort can set the web console service port, default is 8082. The web console url is http://localhost:8082 (Different from the web console url in spring boot application)

Use H2 in spring boot application

In a spring boot application, first add H2 to the dependency.

  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.182</version>
    <scope>runtime</scope>
  </dependency>

Choose a proper H2 version to use and also set scope to runtime become you should not expliity use any class from H2 in you application.

Then configure application.properties or application.yml file for spring boot datasource. Following is example in application.yml file.

spring:
  datasource:
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:
    driver-class-name: org.h2.Driver
    platform: h2

  # enable H2 web console and set url for web console
  # http://localhost:8080/console
  h2:
    console:
      enabled: true
      path: /console
The default username for H2 is 'sa', default password is empty.

If you also use spring security in your application, which means spring security is in your pom.xml like below.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Then you need to add a line in your spring security configuration to make H2 web console working.

@EnableWebSecurity
 
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
 
 //......
 
 
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/").permitAll(); 
    http.authorizeRequests().antMatchers("/imgs/**").permitAll(); 
    http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN");
    http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
  
    // add this line to use H2 web console
    http.headers().frameOptions().disable();
  }
}

Without this line to setup frame in spring security, you will see a empty page after you login the H2  web console.

Monday, February 15, 2016

Generate a random String of specified length in Java

Sure you can write a short piece of code of your own to do this, but apache's common library makes it just a one line task.  Furthurmore, the library is so widely used, it's maybe already in your class path involved by other third party library even you haven't noticed that. 

In case you don't have it in your class path, add this to you maven pom.xml

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.0</version>
</dependency>

Here are the ways to create the most used random String, include random ASCII, random letters, random digits and random letter+digits. Use RandomStringUtils's static methods to get what you need.

  int count = 6; // length of the generated String
  
  // from all ASCII code [32-126] 
  System.out.println(RandomStringUtils.randomAscii(count));  // ex. L\=u8_
  
  // from [A-Z,a-z]
  System.out.println(RandomStringUtils.randomAlphabetic(count)); // ex. TxTbYm
  
  // from [0-9]
  System.out.println(RandomStringUtils.randomNumeric(count));  // ex. 953626
  
  // from [A-Z,a-z,0-9]
  System.out.println(RandomStringUtils.randomAlphanumeric(count));//ex. Wuycb5

Wednesday, February 3, 2016

Improve content assist for xml in Eclipse

The default configuration for xml content assist doen't feel very convenient for real usage.When you change you maven pom.xml or maybe the spring xml-style config files, the eclipse doesn't give you much help automatically. To get assist you need to keep pressing Ctrl+Space.

Here is a simple tip to improve the user experience for editing XML files, by making auto-trigger for assist more often. Window->preferences, set

<=:. _abcdefghijklmnopqrstuvwxyz like below. (There's a space among this string, before '_').

image

This can help you work more smoothly with XML files in Eclipse.

Monday, February 1, 2016

Quick overview of Maven plugin configuration

This article tries to give a quick generic overview of Maven plugins. In the end a pluginin usage is given and explained, so you can get a feeling of how maven plugins look like as a whole.  plugins refer to maven plugins in the following context.

1. Plugin naming rules

The naming convention for official plugins is maven-<plugin name>-plugin. For example, maven-surefire-plugin

The recommand third-party and user-defined plugin should be named <plugin name>-maven-plugin.  For example the plugin for spring boot is named spring-boot-maven-plugin

2. Plugins type

Build plugins will be executed during the build and they should be configured in the <build/> element from the POM. 
Reporting plugins will be executed during the site generation and they should be configured in the <reporting/> element from the POM.

Build plugins  ( <build/>) are more common to see.

3. Available plugins list

Here is the complete availble plugins from official, you can also find a plugin is a build plugin or a report plugin or both on that page.

4. Plugin goal

A plugin goal represents a specific task (finer than a build phase) which contributes to the building and managing of a project. It may be bound to zero or more build phases.

If a goal is bound to one or more build phases, that goal will be called in all those phases automatically.  For example if a plugin has goal bound to phase 'package', then when run 'mvn package' that plugin's goal will be executed.

A goal not bound to any build phase could be executed outside of the build lifecycle by direct invocation.

#============================================================================
# Syntax for invoke a plugin's goal from command line:
# mvn <plugin name>:<goal> [-D<plugin name>.<property name>=<property value>]
#============================================================================
#For example, use 'exec' plugin, goal is 'java', set the plugin property of 'mainClass'
mvn exec:java -Dexec.mainClass="com.shengwang.demo.DemoMain"

All official plugins support a goal called 'help'. It has 2 properties 'detail' and 'goal'. See example below.

5. How to get available goals of a certain plugin.

  • Search keyword "maven <plugin name>  plugin" on google, jump to plugin's official web to learn how to use it. It's suitable for first time using a plugin.
  • In command line, run 'mvn <plugin name>:help', get all available goals. It's more convinient for experienced users for just missing some details.

For example here is the output of the 'mvn exec:help'

image

6. How to get more usage help from command line.

First run 'mvn <plugin name>:help' for all goals, then run 'mvn <plugin name>:help  -Ddetail=true -Dgoal=<goal>' for more usage details of a certain goal. You can try to run 'mvn exec:help -Dgoal=java -Ddetail=true' to see the output. (Don't forget the -D)

7. Basic configuration structure of plugins

For Build plugins, it's included in <build/>.  it has <executions/>, which is the real actiion that are intened to do during bounded build lifecycle.  Let's examine an example:

<build>
  <plugins>
  
    <!-- .... other plugins ...-->
  
    <!-- =========================================== -->
    <!-- make sure antrun is after packaging plugins -->
    <!-- =========================================== -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <version>1.8</version>
      <executions>
        <execution>
          <id>scp-to-remote</id>
          <phase>package</phase>
          <goals>
            <goal>run</goal>
          </goals>
          <configuration>
            <target>
              <!-- keyfile+passphrase or  password, choose one -->
              <!-- 
              <scp localFile="${project.basedir}/target/qos-spark-1.0.jar"
                remoteToFile="root@192.168.203.156:/usr/sanss" verbose="true"
                keyfile="C:\Users\shengw\.ssh\192.168.203.156\id_rsa"
                passphrase="">
              </scp>
               -->
              <scp localFile="${project.basedir}/target/qos-spark-1.0.jar"
                remoteToFile="root@192.168.203.156:/usr/sanss" verbose="true"
                password="passwordForSSH" trust="true">
              </scp>
            </target>
          </configuration>
        </execution>
      </executions>
      <!-- libraries for scp impl      -->
      <!-- antrun doesn't use os's scp -->
      <dependencies>
        <dependency>
          <groupId>com.jcraft</groupId>
          <artifactId>jsch</artifactId>
          <version>0.1.53</version>
        </dependency>
        <dependency>
          <groupId>ant</groupId>
          <artifactId>ant-jsch</artifactId>
          <version>1.6.5</version>
        </dependency>
      </dependencies>
    </plugin>
    <!-- =========================================== -->
    <!--   antrun plugin end                         -->
    <!-- =========================================== -->
  
  </plugins>
</build>

It's a long one, but no need to be panic. This example has the common configuration structure for maven plugins.  Most of the plugins' configuration are shorter.

It's included in <build/> because its a build plugin.

The first part of plugin configuration is trival:  <groupId>, <artifactId> and <version>.

Then there's one <execution> defined in above example. There are <id>, <phase>, <goal> and <configuration> in this <execution>. 

  • <id> - identify this execution. If more pom.xml  has the same plugin bound to same phase and has the same id, these <execution>  will be merged when building. Normally just give a meanful string for this execution.
  • <phase> - bind the execution to a build lifecycle. So it can get executed automatically at that build lifecycle. The above demo, executing antrun plugin when use 'mvn package'
  • <goal> - which behavior of this plugin to be used. In the example, the goal "run" will run some ant tasks. The tasks are defined in <configuration>.
  • <configuration> - Set property for the plugin. Normally any parameter in <configuration> has a corresponding field variable in the plugin class definition, just like a jave bean. For the <target> property, AntRunMojo.java from antrun plugin's source has the variable. Just treat the properties in <configuration> as setting values for these field variables.

image

If there are more <execution>, every one can has its own <configuration>. If <configuration> is out of <executions>, it's  for all <execution>.

Finally, <dependencies> in the <plugin>, to set external libraries only used for this plugin. Most plugins don't need to specify <dependencies> for plugin itself.

How to let maven copy(scp) created jar/war package to other location

In real development, you may found your development environment is different from the running environment. For example, like my own case,  I'm working on a Hadoop/Spark related project.  So I have all my source code and development environment like Eclipse/Maven setup on my Windows laptop, but maven created jar file has to be deployed to a linux  server in the lab with Spark installed. Maunally copy maven created jar file to remote server for every run is tedious, so I need to let maven automatically do the file upload when running command 'mvn package'.

The basic idea is to use a maven plugin called 'antrun', run a ant task at maven lifecycle 'package'. Ant has scp task to copy files locally or remotely.  By utilizing this scp task from ant, you can copy the created jar/war file to anywhere, such as to a different local directory or to a remote host.

The follow demo try to copy a jar file to a remote host.  All you need is to modify maven pom.xml file, add and config this antrun plugin.

1. pom.xml

<build>
  <plugins>
  
    <!-- .... other plugins ...-->
  
    <!-- =========================================== -->
    <!-- make sure antrun is after packaging plugins -->
    <!-- =========================================== -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <version>1.8</version>
      <executions>
        <execution>
          <id>scp-to-remote</id>
          <phase>package</phase>
          <goals>
            <goal>run</goal>
          </goals>
          <configuration>
            <target>
              <!-- keyfile+passphrase or  password, choose one -->
              <!-- 
              <scp localFile="${project.basedir}/target/qos-spark-1.0.jar"
                remoteToFile="root@192.168.203.156:/usr/sanss" verbose="true"
                keyfile="C:\Users\shengw\.ssh\192.168.203.156\id_rsa"
                passphrase="">
              </scp>
               -->
              <scp localFile="${project.basedir}/target/qos-spark-1.0.jar"
                remoteToFile="root@192.168.203.156:/usr/sanss" verbose="true"
                password="mypassword" trust="true">
              </scp>
            </target>
          </configuration>
        </execution>
      </executions>
      <!-- libraries for scp impl      -->
      <!-- antrun doesn't use os's scp -->
      <dependencies>
        <dependency>
          <groupId>com.jcraft</groupId>
          <artifactId>jsch</artifactId>
          <version>0.1.53</version>
        </dependency>
        <dependency>
          <groupId>ant</groupId>
          <artifactId>ant-jsch</artifactId>
          <version>1.6.5</version>
        </dependency>
      </dependencies>
    </plugin>
    <!-- =========================================== -->
    <!--   antrun plugin end                         -->
    <!-- =========================================== -->
  
  </plugins>
</build>

This looks long, but don't be panic. To use it you need to change some properties for <scp> part.

localFile is the src file, remoteToFile is the dest file. You also need to change the authentication information like where you private key file is by keyfile, if you use public/private key for authentication, or just set the password for ssh.

Also you can use antrun to copy created jar to other dir on local file system by replace remoteToFile to localToFile.

Also remember to put you antrun plugin after other package plugin to make sure the jar file is already created when reach antrun. It's safe to put it at the end of the plugin list.

Let's run 'mvn  package', the output looks like below.

image

2. See also

Powered by Blogger.

About The Author

My Photo
Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List