Monday, June 29, 2015

JBoss Fuse - What's new in JBoss Fuse 6.2

Have been busy for a while, other then working on my Lab for Summit, I am also working on migrating my early Home Loan getting started demo to JBoss Fuse 6.2 which is going launch later in the month.

What is going on JBoss Fuse 6.2.

First of all, MY PERSONAL FAVORITE, which is the huge improvement on the Development tool, JBoss Developer Studio. One of the thing I always wants to have is DEBUGGING. Now we can actually set the debug point on every endpoint of the applications, and then through the debug view, we can now see the stack frame for the suspended threads for each route. What I found the most helpful is the view that allow you to trace or even change the header and body in Camel message. The way to start the debugging is just like other Java application. In JBoss Developer Studio, click on "Run as Debug..". Other really cool improvement is the addition of commonly use component available on Camel canvas. The properties now provides the out of box built in configuration field that is going to help developer on what can be configure in that particular components rather then going back and forward to the fuse documentation. To improve development experience, one thing I notice from the tooling, when you drag n drop a component in the canvas, the decency in the pom.xml file is automatically added.

There are also improvement on the camel components (version 2.15.1) and quite a few new connector added. We have improve the salesforce, Olingo component, added the connectors to Dropbox, Box.com  and Google Drive. To me, because I need to interact Database very often, now I can finally use Simple expressions as parameters in the SQL components. I have not play with all of them yet, but be sure to check them out. Another really useful feature in this release is the REST DSL, this allow developer to  establish the REST web endpoint with few lines of setting versus creating and configure in POJO then exposes as the old ways.

Another important new feature is the Role Base Access Control. Powered by JAAS implemented. Many enterprise needed this in real production environment, what this does, it authorized user's right on access the Fuse console, JMX, command line mode and the service allowed in OSGi.

You can find out more details in Jon's blog.
http://janstey.blogspot.com/2015/06/jboss-fuse-62-is-out.html

Friday, May 22, 2015

Integration Project- Micro Services Migration Story with JBoss BPM Travel Agency - Part Five

Part Five- Migrating Web Services.


This post is the fifth part of a JBoss Travel Agency Integration Demo series, and we are going to talk about how to migrate from an existing web service to JBoss Fuse. And it is so much easier then you can ever imagine. When migrating web services, we know there will be a contract already exists, it is the WSDL file. So all we have to do is just to implement this contract. If you want to know more about the micro service architecture of the demo, please see this post for more details. For now, all you have to know, it that now we are going to migrate from an already existing web service to JBoss Fuse, and then dispatch these request to difference message queue in broker, so later on the backend service can retrieve and process it.

After receiving hotel request from client, the web service will be dispatched to messaging queues. And because the result needs to go back to the client, we need to make sure the request comes back, there for we need to set the JMS to in-out to make sure it gets the return message back. Also, we need to transform the data from client to the format backend service understand, which is JSON.


OK, first things first, from the WSDL file we have, by adding the cxf-codegen maven plugin to generate the related object and services, and direct it to read the wsdl file locate in the project.
      
      
        org.apache.cxf
        cxf-codegen-plugin
        2.7.0.redhat-610379
        
          
            generate-sources
            generate-sources
            
              ${basedir}/target/generated/src/main/java
              
                
                  ${basedir}/src/main/resources/wsdl/HotelWS.wsdl
                  
                    -impl
                  
                
              
            
            
              wsdl2java
            
          
        


And then we can start setting up the web service endpoint



Rather then just a simple connection to the messaging queue, this small application needs interact with messaging a lot, so we are going to create a pool to the broker.

    
    
    


    
    


    
    


    


Take a look at the WSDL, here you see it has three services, with different inputs, so we are going to implement these services.

The first route is the universal endpoint, where it will take in the entire request, and dispatch them to the routes that actually implements the web service.


Then we go on can create the route that handles booking and cancel booking, it simply converts the input into whatever format is needed.

Booking Route

Cancel Booking Route


For available hotel request it is a bit more complex, because the web service requires a specific object to return, so here we are going to do map JSON content and turn it into the specific object.
package org.blogdemo.travelagency.hotelwsendpoint;

import java.util.Map;

import acme.service.soap.hotelws.Resort;

public class ListHotelBean {
 
 public Resort getResort(Map promotedResorts){
  Resort resort = new Resort();
  resort.setHotelId((Integer)promotedResorts.get("hotelId"));
  resort.setHotelName((String)promotedResorts.get("hotelName"));
  resort.setHotelCity((String)promotedResorts.get("hotelCity"));
  resort.setAvailableFrom((String)promotedResorts.get("availableFrom"));
  resort.setAvailableTo((String)promotedResorts.get("availableTo"));
  resort.setRatePerPerson(((Double)promotedResorts.get("ratePerPerson")));
  
  return resort;
 }
}

Register the mapping bean in camel context,
    

And lastly, the available route.


And that's it. More detail instructions are available in the slides below.



 For more detail side in BPM Suite, please click here to Eric Schabell's post.

Micro Services Migration Story with JBoss BPM Travel Agency

  Part One: Introduction and Installation
  Part Two: Moving towards Microservices
  Part Three: Retrieving Data with JDBC 
  Part Four: Persistence with JPA
  Part Five: Migrating Web Service 

Tuesday, May 19, 2015

Integration Project- Micro Services Migration Story with JBoss BPM Travel Agency - Part Four

Part Four- Persistence with JPA

In the post, I am going to show you how to create the fuse side travel agency demo from scratch. We are going to take a look at how create the Flight/Hotel booking and cancellation service. If you would like to know more about the micro service architecture this demo is trying to showcase, please go to part 2 of the series
for more information. Due to the fact that Flight and Hotel booking are identical, I will be focusing on the Hotel side. What this service does, is it simply books the hotel when receives a an ID when the booking is done, it'll return a 26 character BookingID as key. This key is then used if the user wants to cancel the booking. As for cancellation, it'll return the cost to cancel it, if the key provided is not found, it'll return 0, as nothing can be cancelled using the key provided.

All request to backend services in this demo are all async, so there are different message queue in the broker connecting to the promotion, booking and cancellation services. For booking service we are going to create an JPA entity contains the booking ID we have received from the process then store the entity to it's table. And in cancellation, it first check if the the booking id exists in the table, if yes, it'll calculate the fee and return it. If nothing found, then a constant number "zero" will return.



We need to first configure the two endpoints from message broker and database.
Like in normal JPA application, first we will need to identify our entities, in this case, we have two, booking entity and cancel booking entity.
package org.blogdemo.travelagency.hotelbookingservice;

import static javax.persistence.LockModeType.PESSIMISTIC_WRITE;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@NamedQuery(name = "queryHotelBookingById", query = "select hotelbooking from Booking hotelbooking where hotelbooking.bookingid=:bookingid " , lockMode = PESSIMISTIC_WRITE)
@Table(name = "hotelbooking")
public class Booking { 
 @Column(name = "bookingid")
 @Id
 String bookingid;
 
 @Column(name = "recieveDate")
 Date recieveDate;

 public String getBookingid() {
  return bookingid;
 }

 public void setBookingid(String bookingid) {
  this.bookingid = bookingid;
 }

 public Date getRecieveDate() {
  return recieveDate;
 }

 public void setRecieveDate(Date recieveDate) {
  this.recieveDate = recieveDate;
 }

}


package org.blogdemo.travelagency.hotelbookingservice;
import java.util.Date;
import javax.persistence.*;
@Entity
@Table(name = "cancelhotelbooking")
public class CancelBooking {
 @Column(name = "bookingid")
 @Id
 String bookingid;
 
 @Column(name = "recieveDate")
 Date recieveDate;

 public String getBookingid() { bookingid;
 public void setBookingid(String bookingid) {
  this.bookingid = bookingid;
 }
 public Date getRecieveDate() { recieveDate;}
 public void setRecieveDate(Date recieveDate) {
  this.recieveDate = recieveDate;
 }
}


And then we need to set the datasource and bean information. Under resource/META-INF create a xml file called persistence.xml. This file contains the database setting, login authentication, and depend on the JPA library you use, there are minor difference when you want to configure the behavior of how it should sync with database. I am using OpenJPA this time.

  
    org.blogdemo.travelagency.hotelbookingservice.Booking
    org.blogdemo.travelagency.hotelbookingservice.CancelBooking
      
       
        
       
       
       
       
       
        
    

1. Meesage broker location and authentication

    
  
  



2. Entity and Transaction managers, and also the datasource setting like normal JPA applications     CAUTION,   
(In Fuse, it's best to use hibernate and Aries JTA.  I will migrate this project in Fuse 6.2)

  
  


  

   
  
  
    
  

 

For booking, because we need to insert a new entity, therefore create a service to handle the creation of the entity.And inside booking service it also handles all the necessary business logic such as generate random booking id and calculating the cancelation fee. Then we need to register the service bean And create the route to book hotel
Create route to cancel booking


Detail instructions are here:

 

For more detail side in BPM Suite, please click here to Eric Schabell's post.

Micro Services Migration Story with JBoss BPM Travel Agency

  Part One: Introduction and Installation
  Part Two: Moving towards Microservices
  Part Three: Retrieving Data with JDBC 
  Part Four: Persistence with JPA

Friday, May 15, 2015

Integration Project- Micro Services Migration Story with JBoss BPM Travel Agency - Part Three

Part Three - Retrieving Data with JDBC


In the post, I am going to show you how to create the fuse side travel agency demo from scratch. In the original Traveling Agency project, the hotel and flight information are all hardcoded in the application, in the Fuse version, I moved the settings to database. So in the project, you will find that there is a H2 database (File) installed. If you want to know more about the micro service architecture of the demo, please see this post for more details. We are going to concentrate on creating the promotion recommendation service of flights and hotel. Using the Camel JDBC components. I am using Hotel promotion service as example, the same applies to flight too, This service lists all the available hotels to the client with condition like the location of the hotels. After retrieving all the possible hotel data from database, it'll either randomly pick one of them, or create a default hotel.

First we need to collect the request from messaging broker, then using the input as parameters in our SQL statement, then query database using Camel-JDBC component, and process the list into Java object, then randomly choose on hotel from the list using a customize POJO bean. At last return the result back to broker.

There are two configuration you will need to predefine 2 configuration, 
1. Location and authentication of messaging broker

      
  
  

2. Datasource 

  
  
  
  
 
I love to turn data into POJO so it's easier to process in my java code, therefore I am going to create an object type "ResortInfo" to hold the data.
package org.blogdemo.travelagency.promotionhotel;

import java.io.Serializable;
import java.math.BigDecimal;

public class ResortInfo implements Serializable{

  private static final long serialVersionUID =  
   6313854994010205L;
  int hotelId;
  String hotelName;
  BigDecimal ratePerPerson;
  String hotelCity;
  String availableFrom;
  String availableTo;
  public int getHotelId() {return hotelId; }
  public void setHotelId(int hotelId) {
    this.hotelId = hotelId; 
  }
  public String getHotelName() {return hotelName;}
  public void setHotelName(String hotelName) {
    this.hotelName = hotelName;
  }
  public BigDecimal getRatePerPerson() {
    return   ratePerPerson;
  }
  public void setRatePerPerson(BigDecimal ratePerPerson){
    this.ratePerPerson = ratePerPerson;
  }
  public String getHotelCity() {return hotelCity;}
  public void setHotelCity(String hotelCity) {
    this.hotelCity = hotelCity;
  }
  public String getAvailableFrom() {return availableFrom;}
  public void setAvailableFrom(String availableFrom){
    this.availableFrom = availableFrom;
  }
  public String getAvailableTo() {return availableTo;}
  public void setAvailableTo(String availableTo){ 
    this.availableTo = availableTo;
  }
}


The current Camel version I am using does not support automatically mapping ResultSet to POJO, so I wrote a helper class to do it. (Don't worry, Camel version 2.14 will have this.) 

package org.blogdemo.travelagency.promotionhotel;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ResortDataConvertor {

  public List processResultSet(List> resultset){
  List avaliableResorts = new ArrayList();
  System.out.println("resultset:["+resultset.size()+"]");
    for(Map obj:resultset){
      ResortInfo resortInfo = new ResortInfo();
      resortInfo.setHotelId((Integer)obj.get("HOTELID"));
      resortInfo.setHotelName((String)obj.get("HOTELNAME"));
      resortInfo.setHotelCity((String)obj.get("HOTELCITY"));
      resortInfo.setAvailableFrom(new SimpleDateFormat("MM-dd-yyyy HH:mm:ss").format(obj.get("AVAILABLEFROM")));
      resortInfo.setAvailableTo(new SimpleDateFormat("MM-dd-yyyy HH:mm:ss").format(obj.get("AVAILABLETO")));
      resortInfo.setRatePerPerson((BigDecimal)obj.get("RATEPERPERSON"));
      avaliableResorts.add(resortInfo);
    }
   return avaliableResorts;
  } 
}


Because of the restriction in the business flow, we can only return one hotel at a time, create the java class that randomly choose one hotel, and also create a default one if no criteria is matched in database.

package org.blogdemo.travelagency.promotionhotel;

import java.math.BigDecimal;
import java.util.List;
import java.util.Random;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;

public class RandomHotelBean implements Processor{
  @Override
  public void process(Exchange exchange) throws Exception {
    List resorts = (List)exchange.getIn().getBody();
    ResortInfo resort;    
    if(resorts == null || resorts.size() == 0){
      resort = new ResortInfo();
      resort.setHotelId(201);
      resort.setHotelName("The Grand Default Hotel");
      resort.setHotelCity((String)exchange.getIn().getHeader("requestCity"));
      resort.setAvailableFrom((String)exchange.getIn().getHeader("requestStartDate"));
      resort.setAvailableTo((String)exchange.getIn().getHeader("requestEndDate"));
      resort.setRatePerPerson(new BigDecimal("109.99"));
    }else{
      Random rand = new Random();
      int  n = rand.nextInt(resorts.size());
      resort = resorts.get(n);
    }
    exchange.getOut().setBody(resort);
  }
  
}

Don't forget to register the beans too. 



And this is the route. 



If you would like to add test, here is how you do,
package org.blogdemo.travelagency.promtionhotel;

import javax.jms.ConnectionFactory;
import static org.apache.camel.component.jms.JmsComponent.jmsComponentAutoAcknowledge;


import org.apache.camel.CamelContext;
import org.apache.camel.test.blueprint.CamelBlueprintTestSupport;
import org.junit.Test;

public class RouteTest extends CamelBlueprintTestSupport {
 
  protected String componentName = "activemq";
 
    @Override
    protected String getBlueprintDescriptor() {
        return "/OSGI-INF/blueprint/blueprint.xml";
    }
    
    protected CamelContext createCamelContext() throws Exception {
        CamelContext camelContext = super.createCamelContext();

        ConnectionFactory connectionFactory = CamelJmsTestHelper.createConnectionFactory();
        camelContext.addComponent(componentName, jmsComponentAutoAcknowledge(connectionFactory));

        return camelContext;
    }

    @Test
    public void testRoute() throws Exception {
     String out = template.requestBody("activemq:queue:requestHotel", "{\"startCity\":\"London\",\"startDate\":\"2015-01-01\",\"endDate\":\"2015-03-01\"}", String.class);
     
     assertEquals("{\"hotelId\":201,\"hotelName\":\"The Grand Default Hotel\",\"ratePerPerson\":109.99,\"hotelCity\":null,\"availableFrom\":\"2015-01-01\",\"availableTo\":\"2015-03-01\"}", out);
     
    }

}

For more details steps, such as setting up dependencies, see here.


Next time we will take a look persistence with JPA.

For more detail side in BPM Suite, please click here to Eric Schabell's post.

Micro Services Migration Story with JBoss BPM Travel Agency
Part One: Introduction and Installation
Part Two: Moving towards Microservices
Part Three: Retrieving Data with JDBC 

Wednesday, May 13, 2015

Red Hat JBoss xPaaS - When xPaaS meets DEVOPS - Part Five

This post is part of short session I spoke in Japan, strictly my opinion.

The last part of my talk is about continuous delivery. The idea of this is to make enterprise more lean,
and react more responsively from user feedback. We all try to find ways to make our application more agile. The way I see many enterprise are still doing especially in my region is they tend to have a rather "Large" release. Each release contains too many updates and new functions. Due to the facts that enterprise wants to have more stable releases, so they have a tendency of having a fully tested large bundle of application to deploy. The process normally takes weeks, by the time software reaches the end users might be too late, and feedback and response to the application from user has to wait weeks. For developer, it's painful to handle all the different versions, and QAs needs to test a large set of functionalities. If anything goes wrong in production, to rollback to previous version will cost enterprise a fortune, because all the available services now needs to shutdown due to one single problem.


On Red Hat JBoss xPaaS, with a little help from Jenkins it is really easy to achieve continuous delivery. This is how it's done, when developer finished developing their application, they first push
their application to an source control repository, in this case we are using git, then a pre-installed Jenkins server on Red Hat OpenShift (PaaS) will pick up the change, start building the application with maven plugins, and then it will run through a series of automatic test, when there are problems, it'll automatically send a test report to the developer, so the developer gets the instant feedback on their application. When the application pass the automatic tests, Jenkins will automatically spin up a container with the middleware it needs and the newly developed application. There is a Jenkins plugin you can utilized, so it saves you lots of scripting work. Then another set of testing will be done before it reaches the UAT, the same applies to UAT, after it passed the test in testing environment, it'll deploy the application to the newly created UAT containers. This is where the QA comes in, does testing and then signal Jenkins to spin up Production container and ready to provide service in Production.

This new continuous delivery, can shorten the lifecycle so it can adapt and response to user feedback in much quicker way, and react faster to market shift and also aline with most current business strategy. And because each build has less amount of changes and updates, it's much easier to rollback and it'll have less impact to the business.

That's all I have gone through in my talk, in summary, Red Hat JBoss xPaaS provides an unify entry point for DEVOPS, so both DEV and OPS speak the same language and let enterprise standardize the development and operation environment. With continuous development, now enterprise can be more agile and flexible to bring business idea into action much faster. It also improve productivity and most importantly, resolve the conflict between DEV and OPS.

Monday, May 11, 2015

Integration Project- Micro Services Migration Story with JBoss BPM Travel Agency - Part Two

Part two - Moving towards Microservices


Recently "microservices" has become a popular phenomena in application development, if you google it, you will find hundreds definition, best practice and design patterns on this new software architecture. To me, this is not new, back on the "SOA" days, I found the hardest thing for us, is to decide the granularity of the services. That is, deciding how much data each service needs to provide, without wasting too much resource in each client call, or have tiny services that client needs to orchestrate and manage everything. For many "political" reasons, we weren't able to provide service with reasonable size, they needs to be huge with loads of business logic. At the time "Monolithic Applications" was the mainstream way of application development and deployment. As a developer, we hated the decision, because it would involve too many people to just push out one service releases. We came up with an idea of breaking the services down into many modules, and each module simply does very simple functions. We had no choice because it would be a headache to manage the team of eight, and having them wait for each other. The way we implement it, is through use of EJB call, can dependency JARs. As it's still deployed as on big monolithic applications, scaling part of the service became a problem. The only thing we can do it to added more server running the application. At the time, we had the idea of micro services, it's just the technology just isn't complete to support us. Now that application container became lightweight, it's cheap to spin up one and having it just run one single task. And with the fast computing power, and simple data exchange format like JSON, now it's alright to make service call asynchronize, there are many advantages when it's asynchronized. Might cover that in other post. So when I see the travel agency project, I think it is a great example to show how micro services can be done using JBoss Fuse.


The Travel Agency Demo showcase a business process for a user to request a holiday itinerary, it'll look up the available flight and hotel, and booked the flight and hotel respectively, if the credit card is invalid, it will cancel the booking. The original application was in JavaEE, everything is all packaged and wrote in the same package. It'll face the problem when it tries to scale-up any of the services.

During the re-construction , I want to make sure it does not impact the original business flow. So it's provide the same services as it is. OK, now we start moving to microservice. First of all, breaking down to smaller pieces, because Flight and Hotel are in nature different services, so I separate them in two. In the application, it has web service endpoints to talk to BPM Suite or other client, and at the backend, the available data and booking data are stored in a Database. In between front and backend, I decide to make the communication async, so it's easier to scale-out and make sure it's absolutely decoupled, and language independent.
For backend, it needs to provide 3 different functionality,
  1. Recommend 1 available Flight/Hotel 
  2. Book and record Flight/Hotel 
  3. Cancel previous booking of Flight/Hotel 
You would think I'll break them into 3 services, and deploy them separately into different container right? Nope! I am not going to do it ,this time. Instead, I'll be breaking them into 2 projects, one give a promoted recommendation of hotel/flight, the other handles booking and canceling. The reason business-wise, putting booking and cancellation together is they logically belong together, you need the booking number to cancel a booking, if anything changes to booking, then it may alway impact cancellation. And technology-wise because I use JDBC to retrieve the list, and JPA for both Booking and Cancellation, so the configuration are shared for both booking and cancellation. That's why I want to group them together.

 

It is very easy for people to over-do the microservice, for me it's not all about breaking down service to make it do one thing per service, but to make it easier for developer to maintain and make sense logically. But what if one day booking and cancellation needs to separate? Don't worry, one thing JBoss Fuse is great to implement microservice is the way we can break different functions into different Camel routes. So when it's time to separate them, it's simply the matter of moving the route to another project.


(Project contains 2 routes, booking and canceling)

(Booking route)

(Cancel Booking route)

This is how I move the monolithic application to a more microservice approach. Now it's going to be much easier to scale out, and the application will have less impact to other application if anything goes wrong. From the diagram, you can see now, we can add a gateway in front to dispatch loads to the two containers listening and waiting for incoming request, and have messaging broker to serve as another layer of buffer.


OK, enough of the architecture talk, next time, we'll take a look at how to migrate web service to JBoss Fuse. (Contract-first Web Service in Camel).

For more detail side in BPM Suite, please click here to Eric Schabell's post.

Micro Services Migration Story with JBoss BPM Travel Agency
Part One: Introduction and Installation
Part Two: Moving towards Microservices

Friday, May 8, 2015

Red Hat JBoss xPaaS - When xPaaS meets DEVOPS - Part Four

This post is part of short session I spoke in Japan, strictly my opinion.

Automation plays an essential role in DEVOPS, the entire cycle will cripple without automation, the whole idea of DEVOPS is to speed up the process inside enterprise, and relief developer and operation from day to day repetitively meaningless routines. Automation meaning that anything the machine can do, make the machine do it. In the old day, due to technology limitation, politics or if it's just simply not having time to do it, there are many jobs that needs to have human involve. If you seriously want to introduce DEVOPS, You will find that you spend majority of time dealing with automations.

There are few aspect in Automation in the DEVOPS lifecycle, let's take a look at my real life experience, it's one of the most anti-automation case I have ever seen in many years of my consulting career. This happens in one of the SI company, where they were responsible for maintaining a banking solution for a bank, due to many reason, their project has failed to deliver the full function on time, so their project manager decided to move the current version to production first as phase one, then deliver the rest of the services later when it's ready. It was a large monolith application, the developer in the project will push out a version of the application, then a quality assurance personnel will come in and test that version of the application, the scope of the application was huge, plus the new function, it take a day or 2 for the QA to test and return the feedback to the developers. By the time the developer receives the feedback, he/she has already pushed out 2 more version, now he has to go back to the problematic version, and fix it, for me as a developer, I hate being interrupt in the middle of something, and go back to the code 2 week before and try to remember, pick up and fix bug. It's not the problem of fixing the bug, if the feed back could of given to developer right after the code to is out, it'll save a lot of time from all these recaps. This process has taken 2 weeks in the QA cycle before it is pushed to production. The operation then take the application, create an pre-production environment, and then it is tested again, of course by human again. A normal function for this bank will normally take about a month.



This is an extreme case, and I see many improvements can be done, but most importantly, eliminating the human testing routines and should be the top priority. To help speed up the automatic testing, in Red Hat JBoss xPaaS, you can setup an CI server such as Jenkins, that detect from our version control repo, which is built-in in xPaaS using Git. So when everything there is a push to the git, the CI server can detect it, then automatically create an production like container to hold the application, then it can automatically deploy and test the current version. Of course, to make the automation of testing easier, one might also want to consider breaking down applications into smaller pieces, and have these smaller pieces deploy on separate container, this makes testing unit simple and quick. Because of the nature in xPaaS, it is very easy for xPaaS to quickly spin up a container, without heavy involvement of developer and operations. Allowing this will make the whole architecture to become more agile and more flexible.

Normally automating these processes are difficult, because you will need to take in account on different version, software, networks and other environment factors. That makes automation complex, Red Hat JBoss xPaaS has a range of middleware already built-in so there is no need to care about the installation script, that make the entire automation much quicker and easier to maintain.
That's all for automation, next time it's the last part of the series, Continuous Delivery.