Liquibase in Spring Boot project

Initializing and managing data base is an old preoccupation and many solutions (like DbUnit, Spring SQL script handling, Flyway) have been proposed over time to manage database initialization and versions during development and testing phase.

Spring Boot integrates Liquibase to initialize a database in very simple way. That makes it a default choice for many developers for data base initialization.

Il this article, we will deal with Liquibase in simple Spring-boot project and we will propose a way to use it to manage database versions and updates in production.

Start with Liquibase

To start using Liquibase we can generate a project in the spring initializer https://start.spring.io.

We enter Group=”com.mycompany”, Artifact=”todo-web” and Package Name=”com.mycompany.todo.web”
Next, we select dependencies :

  • Spring Web Starter
  • Spring Data JPA
  • H2 Database
  • Liquibase Migration
  • Rest Repositories
  • Lombok

Download the project and open it with your favorite IDE.

In this step, we will add change log files to the project to create a data base schema. Spring Boot default configuration look for Yaml file at this location resources/db/changelog/db.changelog-master.yaml.

Using Yaml can be a bit tricky, as the Liquibase documentation focuses on XML configuration.
For this reason, we made a choice to use XML.

To configure the default change log location, we have to add to the property file src/main/resources/application.properties this entry:
spring.liquibase.change-log: classpath:db/changelog/master.xml

For a better database version management, it’s recommended to split the change log to files and to import them in the main file (in our case it’s master.xml file).
Bellow, we provide the master.xml file code:

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">

  <include file="R0/01_initial_schema.xml" 
           relativeToChangelogFile="true"/>

</databaseChangeLog>

Next, we add the inner change log file at this location src/main/resources/db/changelog/R01_initial_schema.xml.

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">

<property name="now" value="now()" dbms="h2"/>
<property name="now" value="now()" dbms="oracle"/>
<property name="autoIncrement" value="true"/>

<!-- The initial schema. -->
<changeSet id="01" author="SAB">

<createTable tableName="REF_USER">
    <column name="ID" type="bigint" autoIncrement="${autoIncrement}">
        <constraints primaryKey="true" nullable="false"/>
    </column>
    <column name="NAME" type="varchar(100)"/>
</createTable>

<createTable tableName="REF_TODO">
    <column name="ID" type="bigint" autoIncrement="${autoIncrement}">
        <constraints primaryKey="true" nullable="false"/>
    </column>
    <column name="USER_ID" type="bigint"/>
    <column name="TITLE" type="varchar(100)"/>
    <column name="DESCRIPTION" type="varchar(100)"/>
    <column name="TARGET_DATE" type="date"/>
    <column name="IS_DONE" type="boolean"/>
</createTable>

<addForeignKeyConstraint baseColumnNames="USER_ID"
    baseTableName="REF_TODO" constraintName="FK_TODO_USER"
    referencedColumnNames="ID" referencedTableName="REF_USER"/>

<loadData file="users.csv" relativeToChangelogFile="true"
separator=";" tableName="REF_USER">
</loadData>

</changeSet>

<changeSet author="SAB" id="tagDatabase">
    <tagDatabase tag="v0.0.1"/>
</changeSet>

</databaseChangeLog>

We create two tables in the database REF_USER and REF_TODO. The first table will be initialized by a list of users provided in the CSV file bellow:

ID;NAME
1;Samer ABDELKAFI
2;Nicolas CAGE
3;Nicole KIDMAN

We activate H2 console in the file src/main/resources/application.properties to display database content.

spring.h2.console.enabled=true

In the project root directory run:
mvn spring-boot:run

You can connect to the database and display tables at this link:
http://localhost:8080/h2-console

Then, provide those entries in the login form:
Driver Class: org.h2.Driver
JDBC URL: jdbc:h2:mem:testdb
User Name: sa
Password:

When we explore the database, we will find the two tables REF_USER and REF_TOFO. And we will notice the existence of two extra tables DATABASECHANGELOGLOCK and DATABASECHANGELOG.

  • The DATABASECHANGELOGLOCK is used to avoid conflict in case you have two Liquibase instance trying to update the database.
  • The DATABASECHANGELOG provides the list of change-sets and tags executed on the database.

Update production database with SQL script

Usually large companies would keep classic process by updating the database with SQL script. They have a DBA (Data Base Administrator) that control database updates and validate SQL script before execution in production.

Liquibase can help you to generate the SQL script required for production.

Add Liquibase plugin to the pom.xml file:

 <plugin>
     <groupId>org.liquibase</groupId>
     <artifactId>liquibase-maven-plugin</artifactId>
     <version>3.7.0</version>
     <configuration>
         <changeLogFile>src/main/resources/db/changelog/master.xml</changeLogFile>
         <driver>org.h2.Driver</driver>
         <url>jdbc:h2:mem:testdb</url>
         <defaultSchemaName>TODO</defaultSchemaName>
         <username>sa</username>
         <password>""</password>
         <verbose>true</verbose>
         <logging>debug</logging>
     </configuration>
     <dependencies>
         <dependency>
             <groupId>org.liquibase.ext</groupId>
             <artifactId>liquibase-hibernate5</artifactId>
             <version>3.6</version>
         </dependency>
     </dependencies>
 </plugin>

Then, execute mvn liquibase:updateSQL

We will get the SQL script here target/liquibase/migrate.sql

If your target database is oracle you have do make some changes to generate oracle sql script.

set driver tag value = oracle.jdbc.driver.OracleDriver
set url tag value = jdbc:oracle:thin:”host”:”port”/TODO

Then, add the oracle driver dependency to the project.

Update production database with Liquibase

In this part of the article, we will deal with advanced features and we will use Docker container to run an Oracle database.

Update a production database could be done at start-up of your web application by spring-boot and Liquibase.
I’m not fun of this solution because even if Liquibase manage concurrent update by the lock table in case we have more than one instance of the application, I would like to update database data without the need of deploying the web application.

In this part, we will provide a solution to manage a manage database update in a separate module that could be packaged and deployed independently.

We create a maven project with three modules:

todo (parent module)
– todo-config (module 1)
– todo-web (module 2)
– todo-liqui (module 3)

To create the project, you can use maven and copy pom.xml and src directory to the todo-web module

$ mvn archetype:generate -DgroupId=com.mycompany -DartifactId=todo -DinteractiveMode=false
$ cd todo
$ mvn archetype:generate -DgroupId=com.mycompany -DartifactId=todo-config -DinteractiveMode=false
$ mvn archetype:generate -DgroupId=com.mycompany -DartifactId=todo-web -DinteractiveMode=false
$ mvn archetype:generate -DgroupId=com.mycompany -DartifactId=todo-liqui -DinteractiveMode=false

Then use Spring Initializr to generate todo-web module and todo-liqui module.

For todo-liqui module, we select dependencies
– Liquibase Migration
– H2 Database

Then, for todo-web module, we select :
– Spring Web Starter
– Spring Data JPA
– Rest Repositories
– Lombok

After downloading zip file, we extract them and copy to the module directory the pom.xml file and the src directory.
We will start by configuring the todo-config module. This module will handle database configuration for todo-liqui module and todo-web module.
We made this choice for simplicity. I recommend using a config server (https://spring.io/projects/spring-cloud-config).

In the config module, we will add 2 files:

src/main/resources/application-dev.properties
with this content

spring.h2.console.enabled=true

src/main/resources/application-prod.properties

spring.datasource.type: com.zaxxer.hikari.HikariDataSource
spring.datasource.url: jdbc:oracle:thin:@192.168.99.100:49161/xe
spring.datasource.username: TODO
spring.datasource.password: TODO

Next, we will configure the Liquibase module.
In the pom.xml, we add dependency to the config-module and oracle driver for production database:

<dependency>
    <groupId>com.mycompany</groupId>
    <artifactId>todo-config</artifactId>
    <version>${project.parent.version}</version>
</dependency>

<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc6</artifactId>
    <version>11.2.0</version>
</dependency>

Then, we configure spring-boot-maven-plugin to generate executable jar (todo-liqui-1.0-SNAPSHOT-exec.jar)

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>com.mycompany.todo.liqui.TodoLiquibase</mainClass>
        <classifier>exec</classifier>
    </configuration>
    <executions>
        <execution>
            <goals>
                 <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

With this config, the plugin will generate an executable Jar here :

Finally, we configure the todo-web module by adding those dependency:

<dependencies>
    <dependency>
        <groupId>com.mycompany</groupId>
        <artifactId>todo-config</artifactId>
        <version>${project.parent.version}</version>
    </dependency>
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc6</artifactId>
        <version>11.2.0</version>
</dependency>

<!-- Other dependnecies here -->

</dependencies>

<profiles>
    <profile>
        <id>dev</id>
        <dependencies>
            <dependency>
                <groupId>com.mycompany</groupId>
                <artifactId>todo-liqui</artifactId>
                <version>${project.parent.version}</version>
            </dependency>
        </dependencies>
     </profile>
</profiles>

You can add JPA entity and spring rest repository for CRUD operations.

To go faster, checkout the project from Gitub here: https://github.com/samer-abdelkafi/liquibase-project.

We will simulate production Oracle database by using this Docker image:
https://github.com/wnameless/docker-oracle-xe-11g

create a schema TODO in your oracle database:

CREATE TABLESPACE tbs_perm_01
DATAFILE 'tbs_perm_01.dat'
SIZE 20M
ONLINE;

CREATE TEMPORARY TABLESPACE tbs_temp_01
TEMPFILE 'tbs_temp_01.dbf'
SIZE 5M
AUTOEXTEND ON;

CREATE USER TODO
IDENTIFIED BY TODO
DEFAULT TABLESPACE tbs_perm_01
TEMPORARY TABLESPACE tbs_temp_01
QUOTA 20M on tbs_perm_01;

GRANT create session TO TODO;
GRANT create table TO TODO;
GRANT create view TO TODO;
GRANT create any trigger TO TODO;
GRANT create any procedure TO TODO;
GRANT create sequence TO TODO;
GRANT create synonym TO TODO;

Now, we will run the application in deferent context.

– Development context: initialize H2 database and run the web application.

$ cd todo-web
$ mvn spring-boot:run -Dspring.profiles.active=liqui,dev -P dev

– Running the application in production database:

$ cd todo-web
$ mvn spring-boot:run -Dspring.profiles.active=prod
or
$ java -jar -Dspring.profiles.active=prod target/todo-web-1.0-SNAPSHOT.jar

– Update production database without start application:

$ cd todo-liqui
$ mvn spring-boot:run -Dspring.profiles.active=prod,liqui
or
$ java -jar -Dspring.profiles.active=liqui,prod target/todo-liqui-1.0-SNAPSHOT-exec.jar

Conclusion

In this article we provide an overview of some use cases of Liquibase. It could be used as a database initializer, SQL script generator, production database update and versioning.
Liquibase has many other features like rollback and conditional update.
I use Liquibase to update production database within a Jenkins Job it works fine and it freed me from writing long SQL script.

MapStruct

MapStruct is a serious alternative for Dozer bean mapping project. It’s a code generator which simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.

MapStruct solves some traditional mapping issues like a lack of performance, difficult mapping code debugging and late errors détection (usually in code execution).

This posts presents how to use MapStruct by dealing with bean mapping case.

Maven configuration

MapStruct is an annotation processor which is plugged into the Java compiler. That why we need to configure maven processor plugin to parse the mapping annotations and generate code.

    <dependencies>
      <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
      </dependency>
      <!-- other project dependencies -->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.bsc.maven</groupId>
                <artifactId>maven-processor-plugin</artifactId>
                <version>2.2.4</version>
                <configuration>
                    <defaultOutputDirectory>
                        ${project.build.directory}/generated-sources
                    </defaultOutputDirectory>
                    <processors>
                        <processor>org.mapstruct.ap.MappingProcessor</processor>
                    </processors>
                </configuration>
                <executions>
                    <execution>
                        <id>process</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>process</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

Source classes

The source code bellow, presents the source class and its dependencies.

public class User {

    private String fullName;
    private Address address;
    private List<Authority> authorities;

    // Add getters and setters
}


public class Address {

    private String street;
    private String city;
    private String state;
    private String zipCode;

    @Override
    public String toString() {
        return street + ", " + city + ", " + state + ", " + zipCode;
    }
    
    // Add getters and setters
}

public class Authority {
    private String code;
    private String value;
    
    // Add getters and setters
}

Destination classes

The destination class has a String type address and a set of string for authorities values.

The mapper have to do three actions:

  1. Copy name field value.
  2. Convert Address object to String.
  3. Convert List to Set.

public class UserDto {

    private String name;
    private String address;
    private Set<String> authorities;

    // Add getters and setters
}

Mapping code

@Mapper
public abstract class UserMapping {
    public static UserMapping INSTANCE = Mappers.getMapper(UserMapping.class);

    @Mappings({
         @Mapping(source="fullName", target="name"),
         @Mapping(target="address", expression="java(user.getAddress().toString())")
    })
    public abstract UserDto userToDto (User user);

    @IterableMapping(elementTargetType = String.class)
    protected abstract Set<String> mapListToSet(List<Authority> value);

    protected String mapAuthorityToString(Authority authority) {
        return authority.getValue();
    }
}

Generated mapping code

Mapping code will be gerated in the generate-sources phase when executing Maven install goal.

If there is no mapping specification error, you can check mapping code under project-folder/target/generated-sources folder.

@Generated(value = "org.mapstruct.ap.MappingProcessor",  date = "today",
    comments = "version: 1.0.0.CR2, compiler: javac, 
    environment: Java 1.7.0 (Sun Microsystems Inc.)"
)
public class UserMappingImpl extends UserMapping {

    @Override
    public UserDto userToDto(User user) {
        if ( user == null ) {
            return null;
        }

        UserDto userDto = new UserDto();
        userDto.setName( user.getFullName() );
        userDto.setAuthorities( mapListToSet( user.getAuthorities() ) );
        userDto.setAddress( user.getAddress().toString());
        return userDto;
    }

    @Override
    protected Set<String> mapListToSet(List<Authority> value) {
        if ( value == null ) {
            return null;
        }
        Set<String> set_ = new HashSet<String>();
        for ( Authority authority : value ) {
            set_.add( mapAuthorityToString( authority ) );
        }
        return set_;
    }
}

Test beans mapping

public class MappingTest {
    @Test
    public void testMapping() {
        Address address = new Address( "street", "city", "state", "zipCode");
        User user = new User("name", address, Arrays.asList(new Authority("1", "admin"), new Authority("2", "user")));

        UserDto userDto = UserMapping.INSTANCE.userToDto(user);

        Assert.assertEquals("name", userDto.getName());
        Assert.assertEquals("street, city, state, zipCode", userDto.getAddress());
        Assert.assertTrue(userDto.getAuthorities().containsAll(Arrays.asList("admin", "user")));
    }

}

In this posts, we dealt with some MapStruct features like collections mapping and expressions. It has other mapping features that can fit your mapping needs like Decorators, Enum type mapping and customized mappings with Before/After mapping methods.

At the moment when I’m writing this article, Mapstruct is in RC2 version. I think soon we will have a release which is mature enough to be considered for production use.

Integrate Gulp on a Maven managed project

Using front-end frameworks for a java web application like AngularJs and Bootstrap needs some caution to manage JavaScript and Css dependencies.

There are two tools family for front-end libraries management. The Java ones like Wro4j and Jawr witch have the advantage to integrate easily to Java project management softs like maven and grails and don’t need any extra knowledge.

The second ones are JavaScript tools like Grunt, Bower and Gulp. Those tools have a great success and have the advantage to be used for front-end frameworks development.

Photo credit: Tony lea

As a result, you will face less problems where managing front-end dependencies and optimizing web resources.

This this post presents a solution to use Maven, Bower and Gulp to manage front-end libraries for a java web project.

The source code of this post is available in this link and you can test it here.

Add frontend maven Plugin

Frontend plugin install node and npm and It lets you run Bower and Gulp.

Add the xml bellow to your project pom.xml file to configure “frontend-maven-plugin”.

<build>
    <plugins>    
         <plugin>
             <groupId>com.github.eirslett</groupId>
             <artifactId>frontend-maven-plugin</artifactId>
             <version>0.0.20</version>
             <executions>
                 <execution>
                     <id>install node and npm</id>
                     <goals>
                         <goal>install-node-and-npm</goal>
                     </goals>
                     <phase>generate-resources</phase>
                     <configuration>
                         <nodeVersion>v0.10.26</nodeVersion>
                         <npmVersion>1.4.3</npmVersion>
                     </configuration>
                </execution>
                <execution>
                     <id>npm install</id>
                     <goals>
                        <goal>npm</goal>
                     </goals>
                     <phase>generate-resources</phase>
                </execution>
                <execution>
                     <id>bower install</id>
                     <goals>
                        <goal>bower</goal>
                     </goals>
                     <configuration>
                        <arguments>install</arguments>
                     </configuration>
                </execution>
                <execution>
                     <id>gulp build</id>
                     <goals>
                         <goal>gulp</goal>
                     </goals>
                     <phase>generate-resources</phase>
                </execution>
            </executions>
         </plugin>
     </plugins>
</build>

Add front-end dependencies

Bower works by fetching and installing front-end packages. For more information about bower visit the website bower.io.

Add a bower.json file with front-end dependencies in the root directory of your maven project (next to the pom.xml file).

{
  "name": "example",
  "version": "0.0.1",
  "dependencies": {
    "jquery": "~2.1.3",
    "angular": "1.4.5",
    "angular-route": "1.4.5",
    "angular-resource": "1.4.5",
    "angular-sanitize": "1.4.5",
    "bootstrap": "3.3.4",
    "bootstrap-material-design": "0.3.0",
    "angular-swagger-ui": "~0.2.3"
  },
  "private": true
}

Add Glup module dependencies

Add package.json file with the content bellow.

{
  "name": "example",
  "version": "0.0.1",
  "dependencies": {
    "bower": "~1.3.12",
    "gulp": "^3.9.0",
    "gulp-concat": "^2.6.0",
    "gulp-concat-vendor": "0.0.4",
    "gulp-uglify": "^1.3.0",
    "main-bower-files": "^2.9.0",
    "gulp-minify-css" : "^1.2.1",
    "gulp-notify":"2.2.0",
    "gulp-inject": "1.5.0",
    "gulp-run-sequence": "0.3.2",
    "stream-series": "0.1.1",
    "gulp-gzip": "1.2.0",
    "gulp-clone": "1.0.0"
  },
  "jspm": {
    "dependencies": {
      "jquery": "github:jquery/jquery@^2.1.3"
    },
    "devDependencies": {
      "traceur": "github:jmcriffey/bower-traceur@0.0.88",
      "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.88"
    }
  },
  "scripts": {
    "prebuild": "npm install",
    "build": "gulp"
  }
}

Add a Gulp execution file

Create the Gulp execution file gulpfile.js with the content bellow.

var gulp           = require('gulp');
var concat         = require('gulp-concat');
var concatVendor   = require('gulp-concat-vendor');
var uglify         = require('gulp-uglify');
var minify         = require('gulp-minify-css')
var mainBowerFiles = require('main-bower-files');
var inject         = require('gulp-inject');
var runSequence    = require('gulp-run-sequence');
var gzip           = require('gulp-gzip');
var clone          = require('gulp-clone');
var series         = require('stream-series');

var vendorJs;
var vendorCss;

gulp.task('lib-js-files', function () {
    vendorJs = gulp.src(mainBowerFiles('**/*.js'),{ base: 'bower_components' })
        .pipe(concatVendor('lib.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('src/main/webapp/resources/vendor/js'));

    vendorJs.pipe(clone())
        .pipe(gzip())
        .pipe(gulp.dest('src/main/webapp/resources/vendor/js'));
});

gulp.task('lib-css-files', function () {
    vendorCss = gulp.src(mainBowerFiles('**/*.css'), {base: 'bower_components'})
        .pipe(concat('lib.min.css'))
        .pipe(minify())
        .pipe(gulp.dest('src/main/webapp/resources/vendor/css'));

    vendorCss.pipe(clone())
        .pipe(clone())
        .pipe(gzip())
        .pipe(gulp.dest('src/main/webapp/resources/vendor/css'));
});

gulp.task('index', function () {
    var target = gulp.src("src/main/webapp/index.html");
    var sources = gulp.src(['src/main/webapp/resources/js/*.js', 'src/main/webapp/resources/css/*.css'], {read: false});
    return target.pipe(inject(series(vendorJs, vendorCss, sources), {relative: true}))
        .pipe(gulp.dest('src/main/webapp'));
});

gulp.task('copyFonts', function() {
    gulp.src(mainBowerFiles('**/dist/fonts/*.{ttf,woff,woff2,eof,svg}'))
        .pipe(gulp.dest('src/main/webapp/resources/vendor/fonts'));
});

// Default Task
gulp.task('default', function () {
    runSequence('lib-js-files', 'lib-css-files', 'index', 'copyFonts');
});

The gulp script has 4 tasks:

  • lib-js-files : concat, uglify and compress all vendor javascript dependencies
  • lib-css-files : concat, minify and compress all vendor css dependencies
  • index : inject the dependencies to file
  • copyFonts : copy fonts to vendor/font directory to keep css references

The graph bellow presents gulp tasks flows.

gulpgliffy

Configure Spring to resolve Gzip files

In this step, we configure Spring MVC framework to look for Gzip files for the application web resources.

@Configuration
@EnableWebMvc
// Extra config annotations 
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/vendor/**")
                .addResourceLocations("/resources/vendor/")
                .setCachePeriod(0)
                .resourceChain(true)
                .addResolver(new GzipResourceResolver())
                .addResolver(new PathResourceResolver());
    }

    // Extra config here

}

Build and deploy your application

Build your application by running :

mvn clean install

Under OpenShift, you may need to change home directory (npm needs to create files under HOME directory):
>HOME=$OPENSHIFT_DATA_DIR

If everything is well done, your project directory will have the structure described below.

project
   +-- bower_components **
   +-- node **
   +-- node_module **
   +-- src
   |    +-- main
   |    |     +-- java
   |    |     +-- resources
   |    |     \-- webapp
   |    |           +-- resources
   |    |           |      +-- css
   |    |           |      +-- js
   |    |           |      \-- vendor **
   |    |           |            +-- css
   |    |           |            |    +-- lib.min.css
   |    |           |            |    \-- lib.min.css.gz
   |    |           |            +-- fonts 
   |    |           |            |    +-- glyphicons-halflings-regular.svg
   |    |           |            |    +-- ....
   |    |           |            \-- js
   |    |           |                 +-- lib.min.js
   |    |           |                 \-- lib.min.js.gz
   |    |           +-- WEB-INF
   |    |           \-- index.html 
   |    +-- test
   +-- target
   +-- bower.json
   +-- gulpfile.js
   +-- package.json
   \-- pom.xml

Finally, deploy and run the application. The sample application of this post is deployed on this link.

The two pictures bellow present Chrome DevTools to compare network performance of the optimized web application which has a load time of 2.74s and the same application without resource optimization which has a load time of 3.37s.

2015_09_23_16_02_46_tomcat7_samerabdelkafi.rhcloud.com_fng_project_apiDoc 2015_09_23_16_03_24_tomcat7_samerabdelkafi.rhcloud.com_ng_swagger_apiDoc

Deploy your application to OpenShift

OpenShift provides free Tomcat application server hosting, which could be useful for hosting a demo application.

Deploying a web application can be done in many ways. You can use Git to push application code on the server, or use an Openshift IDE plugin, or connect with an SSH connection to checkout your code, build your application and deploy it.

   

Golden Gate Bridge

In this post, I present another way to deploy a web application on Openshift by using tomcat maven plugin and tomcat manager.

Add OpenShift application

If you don’t have an OpenShift account, you have to sign up in this link http://www.openshift.com/app/account/new.
Create your application on the OpenShift online user interface, Click “Add new Application” button. Choose Tomcat 7 as application type.

Connect to the server

Use ssh connection to connect to thes server https://developers.openshift.com/en/managing-remote-connection.html.

Install and configure Tomcat Manager

cd jbossews/
mkdir tomcat
wget http://wwwftp.ciril.fr/pub/apache/tomcat/tomcat-7/v7.0.62/bin/apache-tomcat-7.0.62.zip
unzip apache-tomcat-7.0.62.zip
cp -avr apache-tomcat-7.0.62/webapps/manager/ ../webapps/
cd ..
rm -r tomcat/

Next, configure tomcat users file.
Under /var/lib/openshift/OPENSHIFT_APP_UUID/jbossews/conf directory edit the tomcat-users.xml

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
  <role rolename="tomcat"/>
  <role rolename="manager-script"/>
  <role rolename="manager-gui"/>
  <user username="tomcat" password="changeit" roles="tomcat,manager-script,manager-gui"/>
</tomcat-users>

Deploy your application

You can access the manager user interface in this link http://OPENSHIFT_APP_NAME-DOMAIN_NAME.rhcloud.com/manager to deploy your application or a third party war files.

Configure the tomcat maven plugin as shown in the pom file bellow.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	
	<!-- project config -->
	
	<build>
		<plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <url>${tomcaturl}</url>
                    <username>${tomcatuser}</username>
                    <password>${tomcatpw}</password>
                    <path>/myproject</path>
                </configuration>
            </plugin>
		</plugins>
	</build>
</project>

Finally, build and deploy your application.

mvn clean install tomcat7:redeploy 
-Dtomcatuser=tomcat 
-Dtomcatpw=changeit
-Dtomcaturl=http://OPENSHIFT_APP_NAME-DOMAIN_NAME.rhcloud.com/manager/text