Sunday, January 3, 2016

Experiencing Spring boot for REST, WAR and few things

This work has for starting point the need to build war because we don't manage installation on the target server. So it is more easy in our context to ask for one tomcat installation for many web app rather than many standalone app.So we have to transform a spring boot stand alone product in war.
As usal the spring boot guys implement this feature.

The target deployment

The access url

This product product has multiple instance on the same tomcat deployed on different context root.
Just changing the war name like this

${context_root_name}#myWarName.war 

The application setup

  • change log target
  • many product instance means many RDBMS instances to (in our case) we have to change properties files

Few words on product

This product is built on micro services architecture approach. A big  word to tell that we want to define many small and independant services :
  • independant means with their own execution context and data managment
  • interoperability relying on "neutral/pivot" data exchange format  (JSON) and communication API (HTTP) : Here we uses spring REST (hateos).
this product is one of our micro service that collaborate with many other product :
  • jenkins (REST API)
  • a wiki (manly consumer)
  • maven (client API) 
  • a configuration server API for register and provide communication end point for each participant

Hateoas  and data storage with spring

More than rewrite Spring tutorial, I give you the pointers Building REST services with Spring, and
 Accessing JPA Data with REST

Protect deployment behavior

We don't have a lot of time to automate the deployment yet. Whe could do that in the near future with  ansible, fabric, salt or simply simple script because we don't have full admin rights on this machine. I have also in mind to see if we could use Karaf with many servlet container instances.
Anyway, at the begining we choose to product safe war (that are no able to connect to RDBMS) deploy manually . We have only two environements to deploy and they are not really enough stable to spend time in automation now)  To does that whe add the following in maven pom to remove the propertie file that sets up the application :


<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
        <execution>
            <id>package</id>
            <phase>package</phase>
            <configuration>
                <target>
                    <unzip
                        src="target/${project.artifactId}-${project.version}
                            .${project.packaging}"
                        dest="target/excludeProps/" />
                    <delete
                        file="target/excludeProps/WEB-INF/classes/
                            application.properties" />
                    <delete file="target/excludeProps/WEB-INF/
                            classes/logback.xml" />
                    <zip
                        destfile="target/${project.artifactId}-
                            ${project.version}_SAFE.${project.packaging}"
                        basedir="target/excludeProps" encoding="UTF-8" />
                    <delete dir="target/excludeProps" />
                </target>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Build the war

first I used the 58.2 spring boot doc... but no chance,  nothing work.
Then after googling I add spring-webmvc to make things work properly and then it works

<dependency>
 <groupid>org.springframework</groupid>
 <artifactid>spring-webmvc</artifactid>
</dependency>

provide test resources 

How does I test rest and controller ?
One solution is to use Spring test Stack :
  • @RunWith(SpringJUnit4ClassRunner.class) and @SpringApplicationConfiguration(classes =...) This uses the custom extension of BlockJUnit4ClassRunner which provides functionality of the Spring TestContext 
  •  @WebIntegrationTest({ "server.port=0", "management.port=0" }) wich provide a complete startup of application (http listening on port)
  • @ActiveProfiles("test") point the properties setup for tests (use  : application-test.properties)
  • Use  MockMvc see Introducing Spring Test MVC HtmlUnit

 So we have

@RunWith(SpringJUnit4ClassRunner.class
@SpringApplicationConfiguration(classes = Application.class) 
@WebIntegrationTest({ "server.port=0", "management.port=0" })
@ActiveProfiles("test")
public class EnvControllerTest {

} 

Then we declare our mock variable, wrap my web application context and a JPA repository :
private MockMvc mockMvc;


@Autowired
private WebApplicationContext webApplicationContext;

@Autowired
EnvRepository aRepository;

Then I can setup up my test with mock initialization and then repository setup
@Before
public void setup() throws Exception {
 this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
 this.aRepository.deleteAll();
 //init repositories...
}

At the end I could now use the mock calling URL, get response and verify it.

@Test
 public void aNewTest() throws Exception {
 ResultActions perform = mockMvc.perform(get("/restpath/1"));
 MvcResult andReturn = perform.andReturn();
 String res = andReturn.getResponse().getContentAsString();
}

Manage http error

A last small thing that test make thougt about... error processing managment.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class ErrorServerException extends RuntimeException {
    private static final long serialVersionUID = -4464650477685224460L;

    public ErrorServerException(String message) {
        super("Server error : " + message + ".");
    }
}