Build a portable J2EE app across Bluemix and private cloud patterns

As a Java developer, you expect the “write once, run anywhere” promise of Java. How can you ensure this if you are moving your applications between IBM Bluemix and on-premise cloud environments like SmartCloud Orchestrator or PureApplication System?

As you start using different deployment platforms, your application code can inadvertently become dependent on the platform, losing the “run anywhere” value proposition. In this tutorial, we share how we solved the differences in database connectivity and database initialization between both environments. The application code, as well as deployment automation and database initialization code, is included.

We’ll show you how to write a J2EE application that you can deploy seamlessly both to Bluemix and, using Web Application Pattern, to SmartCloud Orchestrator.

Let’s first look at the code to see the differences in database connectivity between the platforms and how we implemented a portable solution. Then we’ll walk through the deployment automation, and the steps for packaging the application for deployment onto both platforms. Finally, we’ll deal with the challenge of database initialization in Bluemix without compromising application portability.

What you’ll need to build your application

  • A Bluemix account
  • A DevOps Services account
  • Familiarity with J2EE
  • Access to a SmartCloud Orchestrator or PureApplication instance

Step 1. Understand the J2EE application sample

  • Design: Our sample application displays a list of invoices. Its design follows the Model View Controller (MVC) pattern. A Java servlet invokes the data access layer to obtain the invoice list, then passes it over to the presentation layer, which in turn applies formatting rules to the data and renders a web page.
  • Code: Click Get the code button above to view the source code in a DevOps Services project. If you want to make changes to the code, click the EDIT CODE button.
    EDIT CODE button in DevOps Services
    And then FORK the DevOps Service project to create your own copy: FORK button in DevOps Services
    Or, you can download it to your file system using the File > Export menu item, and modify it with your favorite editor: File > Export menu in DevOps services
  • Database structure: Our sample database is fairly simple: a schema containing a table to represent invoices. Here is an extract of SampleDatabase.sql:
    CREATE SCHEMA DEMO;
    
    CREATE TABLE DEMO.INVOICES (
            INVOICE_ID INTEGER NOT NULL,
            CUSTOMER_NAME VARCHAR(100),
            SHIPPING_ADDRESS VARCHAR(250),
            AMOUNT DOUBLE
    );
    
    ALTER TABLE DEMO.INVOICES ADD PRIMARY KEY (INVOICE_ID);
    
    INSERT INTO DEMO.INVOICES (INVOICE_ID, CUSTOMER_NAME, SHIPPING_ADDRESS, AMOUNT)
         VALUES ( 11222, 'Marco De Santis', 'Via Sciangai 53 000144 Rome Italy', 100.0);
    INSERT INTO DEMO.INVOICES (INVOICE_ID, CUSTOMER_NAME, SHIPPING_ADDRESS, AMOUNT)
         VALUES ( 11223, 'Ruth Willenborg', '3901 S MIAMI BLVD DURHAM , NC , 27703-9135  United States', 1000.0);
    
  • Model: In order to model our Invoice object, a simple POJO in src/com/ibm/vapps/model/Invoice.java holds just a few fields, a constructor and getter/setter methods:
    public class Invoice {   
            private int id; 
            private String customerName;    
            private String shippingAddress; 
            private double amount;  
            ...
    }
    
  • Controller: The servlet retrieves data from the Data Access Object (DAO) and forwards it to the presentation layer:
    private static final String INVOICES_JSP_PAGE = "invoices.jsp";
    
    protected void doGet(HttpServletRequest request,
                            HttpServletResponse response) throws ServletException, IOException {
                    List<Invoice> invoices = new ArrayList<Invoice>();
                    try {
                            invoices = InvoicesDAO.retrieveInvoices();
                    } catch (DatabaseException e) {
                            request.setAttribute(ERROR_ATTRIBUTE_NAME,
                                            ErrorMessages.ERROR_RETRIEVING_DATA_MESSAGE);
                    }
                    request.setAttribute(INVOICES_ATTRIBUTE_NAME, invoices);
                    RequestDispatcher rd = getServletContext().getRequestDispatcher(
                                    INVOICES_JSP_PAGE);
                    rd.forward(request, response);
            }
    
  • View: The view layer is implemented via a JSP page that displays the invoice details in an HTML table with some styling:
     <table>
                    <thead>
                            <tr>
                                    <th>Invoice Id</th>
                                    <th>Customer Name</th>
                                    <th>Shipping Address</th>
                                    <th>Amount</th>
                            </tr>
                    </thead>
                    <tbody>           
                    <%
                            List<Invoice> invoices = (List<Invoice>)request.getAttribute("invoices");
                            for (Iterator<Invoice> iter = invoices.iterator(); iter.hasNext();) {
                                    Invoice invoice = iter.next();
                    %>
                    <tr>
                                    <td><%=invoice.getId()%></td>
                                    <td><%=invoice.getCustomerName()%></td>
                                    <td><%=invoice.getShippingAddress()%></td>
                                    </tr>
                    <%
                            }
                    %>
                    </tbody>
            </table>
    

You can look at the full code in WebContent/invoices.jsp in the DevOps Services project.

Step 2. Understand deployment platforms

To serve our J2EE application, we need three things: a J2EE container, a DBMS, and a JDBC configuration to support the communication. Let’s look at what the deployment infrastructures offer and how to use them to accomplish our task.

  • On SmartCloud Orchestrator and PureApplication, we will use Virtual Application (VApp) Patterns to define a deployable model of our application. Using the Application Builder User Interface, we can define our logical topology using nodes and connecting them each other. In our case, the VApp will look like this:
    Screenshot of the IBM SmartCloud Orchestrator - Virtual Application Builder
    1. The Enterprise Application node represents a WebSphere classic instance and requires an EAR file to be uploaded as a configuration parameter.
    2. The Database node represents a DB2 instance and requires a SQL script to be uploaded as a configuration parameter.
    3. The connection between them represents the JDBC configuration and requires the JNDI name for the data source as a configuration parameter.
  • When creating a new application on Bluemix, we can choose from a wide set of middleware runtimes.
    1. WebSphere Liberty for Java will suit our needs for the application server. Using the CLI, you can push a whole appserver profile archive, bundling your EAR.
    2. The SQL Database service will implement the persistence layer. Services are intended to be bound to applications, so this is the link between the application server and the database. This automatically makes communication among the middleware apps possible, pushing the configuration into an environment variable called VCAP_SERVICES. This is standard in Bluemix.

    Bluemix dashboard showing the Liberty for Java runtime and SQL Database service added

Step 3. Understand accessing services

Though it looks as if both platforms provide the same facilities, there are subtle differences between the application server and database communication configurations. With SmartCloud Orchestrator/PureApplication VApp, we are explicitly asked to provide a JNDI name to look up the data source, which differs from the Bluemix VCAP_SERVICES approach.

Looking closely at the Bluemix SQL Database service documentation, you’ll notice there is something more. The JDBC auto-configuration feature creates a JNDI resource called /jdbc/<ServiceInstanceName> and makes it available to Liberty.

Using the Virtual Application pattern in SmartCloud, Orchestrator imposes the use of the JNDI standard to retrieve the data source. However, it also requires coding the JNDI name to look up in the application, as described in the documentation:

“The application is assumed to use Java Naming and Directory Interface (JNDI) settings to locate the data source. Specify the JNDI name in the link property panel. During deployment, the JNDI name is set to the corresponding data source, and the name must match the name that is coded into the application.”

Bluemix supports both the VCAP_SERVICES variable holding the database connection configuration, and a JNDI name that can be looked up. In order to prevent our code from becoming dependent on Bluemix VCAP_SERVICES, we chose the J2EE JNDI standard.

Our application remains portable by coding our DAO to use a well-known JNDI name to suit the SmartCloud Orchestrator constraints, and naming our SQL Database instance accordingly on the Bluemix side.

So our src/com/ibm/vapps/dao/InvoicesDAO.java looks like this:

public class InvoicesDAO {
                                                
        private static final String DATASOURCE_JNDI_NAME = "jdbc/DemoDataSource";       
        
        private static Connection getJNDIConnection() throws NamingException, SQLException {
                Context initialContext = new InitialContext();                  
                DataSource datasource = (DataSource) initialContext.lookup(DATASOURCE_JNDI_NAME);               
                return datasource.getConnection();              
        }
        
        public static List<Invoice> retrieveInvoices() throws DatabaseException {
                ...
        }
}

Step 4. Package your application

  1. Follow the J2EE standards for packaging Enterprise Application EAR files (<filename.ear>).
  2. The database is distributed as a SQL file holding the Database Definition Language statements and some sample data (<filename.sql>).
  3. Overall, our application is composed of the EAR archive and the SQL file.

Step 5. Deploy to Bluemix and test

  1. With the Cloud Foundry CLI, you can push WAR files directly, but not EAR files. In order to run an EAR file in the Liberty runtime, it must be a packed server. You can build yours by following these steps:
    1. Extract the provided template wlp.zip.
    2. Rename your EAR file bluemix.ear.
    3. Copy the renamed EAR file into the wlp/usr/servers/libertyProfileServer/apps directory, overwriting the existing placeholder.
    4. Repackage the directory structure as a zip archive; for example, server.zip.
    5. Run the following command:
  2. Bind a SQL Database service instance to the new application. The name of the instance must match the JNDI resource. We call our service instance DemoDataSource because our application is supposed to look up the string jdbc/DemoDataSource. The auto-configuration feature will automatically create a JNDI resource in the container called jdbc/<service_name>.
  3. Open the SQL Database instance management application and load the SQL file to initialize the database using the SQL script provided.Initalizing the SQL Database instance
  4. Access the application at the URL provided by Bluemix. Accessing the application

Step 6. Deploy to SmartCloud Orchestrator and test

  1. Start by selecting Images&Pattern > Virtual Application Patterns from the main menu. Create a new one by clicking the green plus sign. Green plus sign
  2. Drag an Enterprise Application node from the palette on the left and drop it into the canvas. Use the configuration panel on the right to upload the EAR file. Uploading the EAR file
  3. Do the same with the DB2 Database node and upload the schema file. Uploading the schema file
  4. Connect the nodes by drawing an arrow from the application to the database. Give the configuration the same JNDI name, jdbc/DemoDataSource. Click Deploy. Connecting the nodes
  5. Once the deployment process ends, the same application we previously deployed will be running on the private cloud.The running application

Step 7. Automate deployment with SmartCloud Orchestrator

Both of the flows described above can be automated using the SmartCloud Orchestrator Content Pack for Web Applications. After installing it and all its dependencies in the SmartCloudOrchestrator directory, you will find offerings in your Self Service Catalog to deploy your J2EE application on SmartCloud Orchestrator, or on Bluemix. Let’s look at the Bluemix example.

  1. Select the offering in the Self-Service catalog: Selecting the 'Deploy J2EE Web Application to Bluemix' offering in the IBM SmartCloud Orchestrator Self-Service catalogSelecting the Java Enterprise archive
  2. Upload the SQL script: Uploading the SQL scriptDeploying the J2EE application

    Clicking the OK button triggers a SmartCloud Orchestrator business process that contacts Bluemix and performs the same operations you did manually in Step 4.

Step 8. Initialize the database

Automating database initialization is another area of difference between Bluemix and SmartCloud Orchestrator VApp pattern.

In the Bluemix manual steps, we employed a user interface to administer our database. However, there is not a programmatic way to perform the same operations. Moreover, for security reasons, Bluemix services run in an internal network that is not accessible from the Internet. This prevents us from configuring a SQL client to connect to our hosted database.

The Bluemix best practices recommendation is to have the application contain the logic for priming the database structure. However, since we want to keep our application portable, we developed an alternative solution using a simple auxiliary application.

For our automated solution, we wrote a Node.js application based on the Bluemix-provided Node.js Cache Web Starter. This boilerplate exposes an HTTP endpoint that accepts POST requests holding a SQL script in the body that is run against the SQL Database-bound service. The auxiliary application has a short life span because it is used just to serve the POST request that initializes the database and is then removed.

READ:Creating apps with Node.js Cache Web Starter

The initialization flow is achieved by having SmartCloud Orchestrator control a shell script that executes the following cf CLI commands:

  1. Log in with your credentials.
    cf login -a http://api.stage1.ng.bluemix.net -u ${USER} -p ${PASSWORD}
    
  2. Push a Liberty packed server using the –no-start option to prevent the application from starting immediately.
    cf push ${APP_NAME} -p ${PACKED_SERVER}.zip --no-start
    
  3. Create a SQL DB service instance.
    cf create-service sqldb sqldb_small ${JNDI_NAME}
    
  4. Bind the SQL DB service to the Liberty application.
    cf bind-service ${APP_NAME} ${JNDI_NAME}
    
  5. Push the auxiliary application.
    cf push ${APP_NAME}_dbinit  -c "node app.js" -p $WORK_DIR/dbinit.zip –no-start
    
  6. Bind it to the same SQL DB service instance.
    cf bind-service ${APP_NAME}_dbinit ${JNDI_NAME}
    
  7. Start the auxiliary application.
    cf start ${APP_NAME}_dbinit
    
  8. Perform an HTTP POST sending over the SQL statements to be executed against the database.
    curl -i -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "@${SQL_FILE}{APP_NAME}" http://${APP_NAME}-dbinit.stage1.mybluemix.net/runSQLScript
    
  9. Unbind the auxiliary application from the SQL DB service instance.
    cf unbind-service ${APP_NAME}_dbinit ${JNDI_NAME}
    
  10. Remove the auxiliary application from the Bluemix space.
    cf d -f ${APP_NAME}_dbinit
    
  11. Start the Liberty application.

In SmartCloud Orchestrator, the database is automatically initialized by the Pattern Engine that processes the script provided as input. When the DB2 instance starts, you will find your database initialized.

By staying within J2EE standards, the “write once, run anywhere” promise of Java can remain intact across IBM Bluemix and on-premise virtual application patterns. In this tutorial, you saw the differences in database connectivity, and how to use JNDI to keep your code portable. In addition, you saw a technique to automate Bluemix database initialization without coding the initialization into the application. With just a little extra effort, your application can remain independent of the platform on which it is deployed.

We would like to thank the team working with us on Bluemix database initialization:

Claudio Marinelli

Gianluca Bernardini

Paolo Ottaviano

BLUEMIX SERVICES USED IN THIS TUTORIAL:

RELATED TOPICS:J2EESmartCloud OrchestratorPure Application

Source: Build a portable J2EE app across Bluemix and private cloud patterns

Post author

Dustin Gurley is an Designer, Developer, Artist, Instructor, Critical Theorist and Systems Engineer. He has an extensive background working professionally with 2D/2.5D/3D Motion Graphics, Compositing, Film, Video, Photography and client-side performance techniques as it pertains to web development. Dustin recently completed work on his Master of Fine Art degree in Motion Media Design (Motion Graphics) from the Savannah College of Art and Design. Prior to beginning his graduate work, Dustin obtained a Bachelor of Art degree in Communication Studies with a concentration in Broadcast and Emerging Media from the University of North Carolina at Wilmington. In addition to design and modeling, Dustin enjoys toying with his view camera, working with scratch film, authoring media related material and contributing to various industry conferences. When not in front of a computer, Dustin can be found with his wife, Regina Everett Gurley. The couple enjoys dividing their time between their home just outside of Raleigh, North Carolina and the beautiful North Carolina coast. Currently, Dustin serves as the Lead Instructor of Internet Technologies for Wake Technical Community College in Raleigh, North Carolina.