Continuous Deployment is the automated process of building, testing and deploying changes to an application whenever they’re committed to source control. This practice is becoming increasingly adopted by the industry, largely facilitated by the DevOps movement, and can deliver many benefits to your business. In this series you’ll learn how to set up a continuous deployment pipeline for AcmeAir, a web application for a fictitious airline:


Pipeline Diagram

In Part 1 of this series we’ll be setting up a development environment for AcmeAir. We’ll clone the AcmeAir Git repository and import it as a set of Maven-based projects. Using the WebSphere Application Server Developer Tools (WDT), we’ll deploy the application to a local development Liberty Profile server and automatically publish incremental updates.

Setting up a developer environment

First things first, we need Eclipse with the WebSphere Developer Tools (WDT). Download it here. Don’t worry about step 3, we’ll do this later.

On Linux platforms you might get an error later on caused by Eclipse/web browser integration. To avoid that, run the command below.

$ sudo apt-get install libwebkitgtk*

Once we’ve done that we need to set up Eclipse.

Set up Eclipse for Maven

The Maven integration in Eclipse defaults to settings familiar to Eclipse users who are used to developing Java EE applications. We’re going to use settings more familiar to Maven users:

  1. Select Window > Preferences.
  2. Expand Java EE > Maven > Maven Project Settings.
  3. Click on the Set all Maven values button.
  4. Next select Java EE > Project.
  5. Uncheck the Add project to an EAR checkbox at the top of the window.
  6. While we’re here, select General > Editors > Text Editors and tick Show line numbers.
  7. Click OK.

Eclipse is now ready to work with Maven projects and WebSphere Liberty Profile.

Getting the source

Now we have our Eclipse environment, let’s get some code to work with.

The source is hosted on GitHub. We’ll need to fork the repository and then create a local clone. You’ll need a GitHub account for this. After you’ve signed in, go to the AcmeAir repository and fork it.

Clone the AcmeAir Git repository

The application we’re going to use is hosted on GitHub. You need to clone this repository into Eclipse:

  1. Open the Git Repositories view. Select Window > Show View > Other….
  2. Expand Git then select Git Repositories and click OK.
  3. In the Git Repositories view, click on Clone a Git Repository.
  4. In the URI field, type in https://github.com/<your GitHub ID>/sample.acmeair.git.
  5. Click Next.
  6. Leave all the branches selected and click Next.
  7. Leave the default value for the Directory field.
  8. Set the Initial branch to lab and click Finish.

The Git repository is now cloned.

Import the AcmeAir projects into Eclipse

The acmeair repository contains five Maven based projects. To import them into Eclipse:

  1. Select File > Import > Maven > Existing Maven Projects and click Next.
  2. In the Root Directory field, type in /home/<username>/git/sample.acmeair then click Refresh.
  3. Click Finish to import the projects. The Enterprise Explorer view will look like this:


Enterprise Explore

The acmeair project represents the root of the Git repository. A project has been created for each of directories beneath this.

Now run Maven to build the projects and create the application artifacts: right-click on the acmeair project and select Run As > Maven install.

Creating and configuring a Liberty Profile server

OK, we have the code, now let’s set up a WebSphere Liberty Profile server to run it.

Create a Liberty Profile server

To create a server we need to download Liberty profile and make sure Eclipse knows about it. Luckily, we can do all this inside Eclipse as part of the creation process:

  1. In the Servers view, right-click and select New > Server.
  2. For the server type select WebSphere Application Server V8.5 Liberty Profile and click Next.
  3. Click download or install.
  4. Select Download and install a new runtime environment from:.
  5. Select Liberty Repository then Liberty Profile V8.5.5.2 Runtime.

Carry on through the rest of the dialog, and make a note of your installation directory.

  1. Click Finish.
  2. Click Next.
  3. In Server name, type in AcmeAirDemo. Leave the server configuration. Click Next.
  4. Click Add All >>.
  5. Click Finish.

In the Servers view you should see an entry named WebSphere Application Server V8.5 Liberty Profile at localhost.

Configure the HTTP endpoint

Before we can run AcmeAir, we need to configure our server. Let’s start with the HTTP endpoint. In the Server view:

  1. Expand WebSphere Application Server V8.5 Liberty Profile.
  2. Double click Server Configuration [server.xml].

The server.xml configuration file will open in the design view by default. Go to the Source tab of the editor to see the underlying XML.

Replace the <httpEndPoint> element with:

<httpEndpoint id="defaultHttpEndpoint"
              host="*"
              httpPort="9080" >
  <tcpOptions soReuseAddr="true"/>
</httpEndpoint>

host=* means the server will listen on all adapters, rather than just localhost which is the default. The soReuseAddr flag allows the server to restart quickly without port conflicts.

Configure the database

We’re going to be using Apache Derby as our database. Download it here. We want the lib distribution. Extract the lib/derby.jar file into <liberty install directory>/usr/shared/resources.

Next, add the database configuration: the JDBC driver for the Derby database which refers to a library containing the Derby databse files themselves.

<jdbcDriver id="DerbyEmbedded" libraryRef="DerbyLib"/>
<library filesetRef="DerbyFileset" id="DerbyLib"/>
<fileset id="DerbyFileset"
         dir="${shared.resource.dir}"
         includes="derby.jar"/>

Now add transactional and non-transactional Data Source configurations to the server.xml to configure how database connections are made.

<dataSource id="jdbc/acmeairdatasource" 
            jdbcDriverRef="DerbyEmbedded" 
            jndiName="jdbc/acmeairdatasource">
    <properties createDatabase="create" databaseName="acmeairdatabase"/>
</dataSource>

<dataSource id="jdbc/acmeairdatasourcenonjta" 
            jdbcDriverRef="DerbyEmbedded" 
            jndiName="jdbc/acmeairdatasourcenonjta" transactional="false">
    <properties createDatabase="create" databaseName="acmeairdatabase"/>
</dataSource>

Running AcmeAir

Everything should be set up and ready to go. Let’s run AcmeAir.

  1. Right-click WebSphere Application Server V8.5 Liberty Profile in the Servers view
  2. Click Start

After several seconds the Console view will automatically come to the front and display that the Application acmeair-webapp has started. Also included is a link to the application’s URL, something like http://localhost:9080/acmeair-webapp. Click it to verify the application is running. You should see the AcmeAir plane.


Enterprise Explore

Initialize the application

Before we use AcmeAir we need to initialise it with some sample data.

Go to http://localhost:9080/acmeair-webapp/rest/api/loader/loadSmall

This request will take several seconds to complete. Wait until you see the Sample data loaded message in the browser.

Update AcmeAir

The AcmeAir application has one small problem. It does not return to the welcome page after pressing on the Logout action. This bug, therefore, can reveal private customer data or flight information to others. You will use the WDT incremental update feature to fix this bug without restarting the application.

To replicate the problem:

  1. Go back to the AcmeAir homepage.
  2. Click Login then Ok.
  3. Click the Account action and then click on the Logout action.

The screen will remain displaying the customer profile information.

The problem lies in the acmeair-common.js JavaScript file. In Eclipse, while the application is still running, open the acmeair-webapp/src/main/webapp/js/acmeair-common.js file.

Click on the logout function in the Outline view.

Add window.location='index.html'; after line 89. The new function should look like the following:

function logout() {
    updateLoggedInUserWelcome();
    var loggedinuser = dojo.cookie("loggedinuser");
    if (loggedinuser == null) {
        return;
    }

    dojo.xhrGet({
        content : {
            login: loggedinuser
        },
        url: 'rest/api/login/logout',
        load: function(response, ioArgs) {
            if (response != 'logged out') {
                // TODO: why isn't error function being called in this case
                alert('error logging out, response: ' + response);
                return;
            }
            dojo.cookie("loggedinuser", null, {expires: -1});
            updateLoggedInUserWelcome();
            window.location='index.html'; 
        },
        error: function(response, ioArgs) {
            alert('error logging out, response: ' + response);
        }
    });
}

Save the file.

To verify this fix, return to the application your web browser and refresh the page. Login to the application again and click on the Account action. Click on the Logout action. This time the application should return to the welcome page.

That’s it for today. We’ve got AcmeAir running on Liberty profile with changes being published automatically to the development server. In Part 2 we’ll be running builds and integration tests using Maven. See you then!

2 Comments on "DevOps with Liberty, Maven, and Chef (Part 1)"

  1. I am getting the below error while doing a login

    err] at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:837)
    [err] at [internal classes]
    [err] at com.acmeair.jpa.service.CustomerServiceImpl.getCustomerByUsername(CustomerServiceImpl.java:94)
    [err] at com.acmeair.jpa.service.CustomerServiceImpl.validateCustomer(CustomerServiceImpl.java:101)
    [err] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [err] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    [err] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [err] at java.lang.reflect.Method.invoke(Method.java:498)
    [err] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:319)
    [err] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
    [err] at com.sun.proxy.$Proxy47.validateCustomer(Unknown Source)
    [err] at com.acmeair.web.LoginREST.login(LoginREST.java:41)
    [err] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [err] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    [err] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [err] at java.lang.reflect.Method.invoke(Method.java:498)
    [err] at com.ibm.ws.jaxrs20.server.LibertyJaxRsServerFactoryBean.performInvocation(LibertyJaxRsServerFactoryBean.java:636)
    [err] at [internal classes]
    [err] at com.acmeair.web.RESTCookieSessionFilter.doFilter(RESTCookieSessionFilter.java:63)
    [err] at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    [err] at [internal classes]
    [err] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    [err] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    [err] at java.lang.Thread.run(Thread.java:745)
    [err] Caused by: java.lang.NoClassDefFoundError: org/eclipse/persistence/transaction/JTASynchronizationListener
    [err] at org.eclipse.persistence.transaction.JTATransactionController.(JTATransactionController.java:65)
    [err] at [internal classes]
    [err] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    [err] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    [err] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    [err] at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    [err] at java.lang.Class.newInstance(Class.java:442)
    [err] at org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(PrivilegedAccessHelper.java:428)
    [err] at [internal classes]
    [err] … 72 more

  2. Great set of articles. Would be great if you could add a pictorial view of what is being accomplished in the article in part 1. Kind of like a devOPS pipeline. Thanks.

Join The Discussion

Your email address will not be published. Required fields are marked *