Chapter 4. Design and Development
4.1. Credit Service Web Service
For the purpose of this reference architecture where the focus is on the BPM Suite, assume that an external web service provides the required information on the credit worthiness of mortgage applicants. For the sake of simplicity, create a basic Web Service that takes an applicant’s social security number as its only input and returns their Credit Score as the result. Rather than recreating the service, if you would prefer, you can build a deployable .war file from the accompanying source code (ocp-credit-service module) for this document. Creating a simple Web Service using JSR-181 and JSR-224 requires a simple Web Application with an empty web.xml file and an annotated Java class:
package com.redhat.bpms.examples.mortgage;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class CreditService
{
@WebMethod
public Integer getCreditScore(Integer ssn)
{
int lastDigit = ssn - 10 * ( ssn / 10 );
int score = 600 + ( lastDigit * 20 );
System.out.println( "For ssn " + ssn + ", will return credit score of " + score
);
return score;
}
}This class simply uses the last digit of the social security number to mock up a credit score. The web deployment descriptor remains empty:
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
</web-app>
Once deployed to OpenShift Container Platform, the service can then be reached via URL similar to the following:
4.2. Mortgage Loan Rules
As alluded to previously, the Knowledge Store portion of our project will be borrowed from a previously constructed project, discussed in greater detail as a part of the Business Process Management with Red Hat JBoss BPM Suite 6.3 Reference Architecture.
4.2.1. Knowledge Repository
The knowledge repository, once exported from an instance of Business Central (or similar authoring tool environment) will have the following structure:
Figure 4.1. ocp-mortgage-rules Module Directory Structure

A few changes are necessary prior to utilizing this repository for S2I image creation. First, the web service call URL and namespace can be modified, if necessary, with a search and replace within the .bpmn2 file associated to the business process.
Secondly, the Intelligent Process Server defaults to a very limited user and roles configuration at the server level. In order to allow multiple user types (such as broker, manager, appraiser) to perform functions via our User Interface, we need to see the S2I process with a few configuration files to bolster the list of users with access to the system.
Add a top-level folder called configuration with two files:
application-roles.properties:
kieserver=kie-server,guest brokerUser=kie-server,user,broker managerUser=kie-server,user,manager appraiserUser=kie-server,user,appraiser
application-users.properties:
kieserver=16c6511893651c9b4b57e0c027a96075 brokerUser=f5aac98dcdffb823f6cc6c31f9ebbce7 managerUser=b4d2a2fd7c71bda4583f17d9d78e160a appraiserUser=a02ebd14456015693ba2038c47a6a5d8
Note that the user kieserver will be overridden by the server on image creation. The passwords used for the application-users file above can be calculated at the terminal to fit the format expected within the server container. To determine a user password hash, perform the following command:
$ echo -n brokerUser:ApplicationRealm:password1! | openssl dgst -md5 -binary | od -A n -t x1 | tr -d ' '
The format of the -n attribute value should follow the username:app_realm:password format, where, unless specified differently via environmental variables, app realm will be ApplicationRealm. The examples above use password1! for every user. Should you wish to use different users or passwords, be sure to calculate and substitute in necessary values in both configuration files and the Java enum within the source code used as a lookup reference for available users.
Once defined and added as part of the S2I process, the content of these files (with the exception of the kieserver user), will get appended to the single default user setup created within the image.
4.3. Mortgage Dashboard
To this point, the code utilized for various deployments to OCP have either come from preexisting applications or exports. The final portion of the project, the user interface responsible for allowing interactivity with loan applications throughout the Mortgage Loan business process, differs in that it is original code demonstrating the capability of building kie-server API interfacing into a standalone project. This module consists of a PatternFly and AngularJS-based front-end and Java backend.
The source code for all modules, including this one, can be found in the repository referenced in this document, and previously linked. You may choose to start by instantiating a new module from scratch, using the accompanying code in its entirety, or using the code as a boilerplate by removing the majority of working files and keeping the directory structures and configuration portions intact.
4.3.1. User Interface
Further guidance and discussion regarding the main frameworks, PatternFly and AngularJS, can be found in the Red Hat Developers Blog article titled Building JBoss Projects with PatternFly and AngularJS and the official Angular-PatternFly documentation.
4.3.1.1. JavaScript and CSS Inclusions
The main entry point (in this use case, index.html) for the module application should include the following framework-required references and calls to script files created as a part of this module:
CSS Links:
<link rel="stylesheet" href="vendor/patternfly/dist/css/patternfly.min.css">
<link rel="stylesheet" href="vendor/patternfly/dist/css/patternfly-additions.min.css">
<link rel="stylesheet" href="vendor/angular-patternfly/dist/styles/angular-patternfly.min.css" />
<link rel="stylesheet" href="css/specific.css">
Script Links:
<script src="vendor/angular/angular.js"></script>
<script src="vendor/angular-route/angular-route.js"></script>
<script src="vendor/angular-resource/angular-resource.js"></script>
<script src="vendor/jquery/dist/jquery.js"></script>
<script src="vendor/bootstrap-select/dist/js/bootstrap-select.js"></script>
<script src="vendor/moment/moment.js"></script>
<script src="vendor/angular-route/angular-route.js"></script>
<script src="vendor/angular-sanitize/angular-sanitize.js"></script>
<script src="vendor/angular-animate/angular-animate.js"></script>
<script src="vendor/patternfly/dist/js/patternfly.js"></script>
<script src="vendor/angular-bootstrap/ui-bootstrap.js"></script>
<script src="vendor/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="vendor/bootstrap/dist/js/bootstrap.js"></script>
<script src="vendor/d3/d3.js"></script>
<script src="vendor/c3/c3.js"></script>
<script src="vendor/lodash/lodash.js"></script>
<script src="vendor/matchHeight/dist/jquery.matchHeight.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers/home.js"></script>
<script src="js/controllers/appraiser.js"></script>
<script src="js/controllers/applicant.js"></script>
<script src="js/controllers/dataCorrection.js"></script>
<script src="js/controllers/manager.js"></script>
<script src="js/controllers/troubleshoot.js"></script>
<script src="js/controllers/downPayment.js"></script>
<script src="js/controllers/financialReview.js"></script>
4.3.1.2. Bower Configuration
Bower, a dependency management (package manager) tool for front end utilities such as frameworks, libraries, assets, etc, can be configured with two files placed in the module’s top-level directory for configuration of Bower:
.bowerrc - used to specify where third-party libraries should be included and referenced from within the module:
{
"directory": "src/main/webapp/vendor"
}bower.json - used to specify module dependencies and properties relevant to the project:
{
"name": "ocp-mortgage-dashboard",
"description": "",
"main": "index.js",
"authors": [
"jary@redhat.com"
],
"license": "Apache-2.0",
"homepage": "",
"private": true,
"dependencies": {
"angular": "^1.4.0",
"angular-loader": "^1.4.0",
"angular-mocks": "^1.4.0",
"angular-route": "^1.4.0",
"angular-resource": "^1.4.0",
"bootstrap": "3.2.0",
"angular-patternfly": "^3.13.0"
},
"ignore": [
"*/.",
"node_modules",
"bower_components",
"vendor",
"test",
"tests"
],
"resolutions": {
"bootstrap": "~3.3.7",
"angular": "1.5.9"
}
}4.3.1.3. AngularJS Application Configuration
The main AngularJS configuration file, app.js, contains a few pertinent sections worth further inspection. The first fo these is module inclusions:
var app = angular.module('app', [
'ngRoute',
'ngResource',
'ngSanitize'
]);4.3.1.3.1. Constants
Next, we can define a set of constants to globally represent the various states that work items can be found in through the business process:
app.constant('Constants', {
PROCESS_STATE: {
0: 'PENDING',
1: 'ACTIVE',
2: 'COMPLETED',
3: 'ABORTED',
4: 'SUSPENDED'
}
});
4.3.1.3.2. Routing
Lastly, we can configure the routing of the application to map addresses to relevant controllers and views:
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
})
.when('/dataCorrection', {
templateUrl: 'views/dataCorrection.html',
controller: 'DataCorrectionCtrl'
})
.when('/manager', {
templateUrl: 'views/manager.html',
controller: 'ManagerCtrl'
})
.when('/applicant', {
templateUrl: 'views/applicant.html',
controller: 'ApplicantCtrl'
})
.when('/appraiser', {
templateUrl: 'views/appraiser.html',
controller: 'AppraiserCtrl'
})
.when('/troubleshoot', {
templateUrl: 'views/troubleshoot.html',
controller: 'TroubleshootCtrl'
})
.when('/downPayment', {
templateUrl: 'views/downPayment.html',
controller: 'DownPaymentCtrl'
})
.when('/financialReview', {
templateUrl: 'views/financialReview.html',
controller: 'FinancialReviewCtrl'
})
.otherwise({
redirectTo: '/'
});
}]);4.3.1.3.3. Controllers
The AngularJS controllers mapped above, in this use case, largely consist of calls to the Java back-end for API manipulation and code regarding UI formatting and notifications. The Data Correction controller lists various prime examples of how various workflow actions related to work items, such as claiming, starting, stopping, releasing and completing a task, can be invoked from within the UI:
dataCorrection.js:
'use strict';
angular.module('app')
.controller("DataCorrectionCtrl", ['$scope', '$http', 'Constants', '$location', '$timeout',
function ($scope, $http, Constants, $location, $timeout) {
$scope.consts = Constants;
$scope.taskInProgress = false;
fetchTasks();
function fetchTasks() {
$http.get("service/broker/dataCorrectionTasks").success(function (data) {
$scope.tasks = data;
});
}
$scope.claimTask = function (taskId) {
$http.post("service/broker/claimTask", taskId).then(
function (data) {
$scope.successMessage = "Task claimed Successfully";
$scope.successVisible = true;
fetchTasks();
$timeout(function () {
$scope.successVisible = false;
$scope.successMessage = "";
}, 2000);
},
function (data) {
$scope.errorMessage = "Error occurred while attempting to claim task";
$scope.errorVisible = true;
fetchTasks();
console.log("ERROR: " + JSON.stringify({data: data}));
$timeout(function () {
$scope.errorVisible = false;
$scope.errorMessage = "";
}, 2000);
});
};
....
$scope.submitApp = function() {
var $application = {
applicant : {
name: $scope.applicantName,
ssn: $scope.applicantSsn,
income: $scope.applicantIncome,
creditScore: null
},
property : {
address: $scope.propertyAddress,
price: $scope.propertyPrice
},
downPayment: $scope.downPayment,
amortization: $scope.amortization,
appraisal : null,
mortgageAmount: ($scope.propertyPrice - $scope.downPayment),
apr: $scope.apr
};
$http.post("service/broker/completeTask/" + $scope.taskInProgressId, $application).then(
function(data) {
$scope.successMessage = "Data Correction submitted successfully";
$scope.successVisible = true;
$timeout(function() {
$scope.successVisible = false;
$scope.successMessage = "";
$location.path("/home");
}, 2000);
},
function (data) {
$scope.errorMessage = "Error occurred while attempting to submit Data Correction";
$scope.errorVisible = true;
console.log("ERROR: " + JSON.stringify({data: data}));
$timeout(function() {
$scope.errorVisible = false;
$scope.successMessage = "";
}, 2000);
});
};
}]);Similar controllers are utilized for each of the various points in the business process where human interaction is required.
4.3.1.3.4. Views
The dataCorrection.html file gives a great example of how data can be both displayed from scope, as responses to server queries are received, as well as how information can be collected and passed along for submission to other back-end controller points, thereby giving us the means to relay and receive information from the human task portions of the business process:
...
<tbody ng-repeat="task in tasks">
<tr>
<td>{{task["task-id"]}}</td>
<td>{{task["task-name"]}}</td>
<td>{{task["task-actual-owner"]}}</td>
<td>{{task["task-proc-inst-id"]}}</td>
<td>{{task["task-status"]}}</td>
<td>{{task["task-container-id"]}}</td>
<td>{{task["task-created-on"] | date:'MM-dd-yyyy HH:mm:ss Z'}}</td>
...
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-3 control-label" for="applicantName">Applicant Name</label>
<div class="col-sm-9">
<input ng-model="applicantName" type="text" id="applicantName" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="applicantSsn">Social Security Number</label>
<div class="col-sm-9">
<input ng-model="applicantSsn" type="text" id="applicantSsn" class="form-control">
</div>
</div>
...
</form>
</div>
</div>
4.3.2. Java Application Components
4.3.2.1. Maven Configuration and Dependencies
The high-level project 'parent' pom.xml file consists of basic project structural information, i.e. submodule listings:
<groupId>com.redhat.bpms.examples</groupId>
<artifactId>ocp-mortgage-example</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>ocp-mortgage-rules</module>
<module>ocp-mortgage-dashboard</module>
<module>ocp-credit-service</module>
</modules>
Each child module (ocp-credit-service, ocp-mortgage-rules, and ocp-mortgage-dashboard) has it’s own pom.xml file that is responsible for describing any dependencies and build plugin configuration relevant to the respective module. All three have been built according to each module’s needs and worthy of inspection, but exploring the content of each is left as an exercise for the reader as brevity necessitates exclusion of these lengthy files.
4.3.2.2. Logging Configuration
Logging, in this project example, is configured via logback and utilized via slf4j. The logback.xml file should be added to the /src/main/resources directory, where it will be automatically detected by the framework.
logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<logger name="com.redhat.bpms" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Once configuration is in place, the logger can be instantiated and used inside any Java class as follows:
private static final Logger logger = LoggerFactory.getLogger(ThisClassName.class);
...
logger.error("ERROR in Rest endpoint releaseTask...", error);4.3.2.3. Data Model
Like the Mortgage Loan business process Knowledge Store, our module must be able to communicate via a common language in order to transfer information between the two. While it’s possible to abstract the data model to its own submodule for easier sharing amongst dependent modules, in this use case the data model has been manually added to both the rules and user interface modules as they’re unlikely to change during the lifetime of this example application and reference another repository or submodule complicates the ability to deploy the two modules independently in OCP. For this use case, the Java POJOs created automatically for the rules module by Business Central have simply been wholesale copied into a matching package structure within the dashboard module.
4.3.2.3.1. Mapping for Work Item Consumption
Intelligent Process Server human task items are particular about the format that must be met for inbound data. This means that in order to supply information needed for entry points of these steps, we must adhere to the custom JSON format required, rather than simply trusting a mapping framework to generate a default JSON format and anticipating successful consumption. The following is an example mapping for the Application POJO from the data model which properly generated the necessary wrapper JSON elements and adherence to generics properties lists:
public final class ApplicationMapper {
...
try {
Map<String, Object> wrapper = new HashMap<>();
Map<String, Object> appContent = new HashMap<>();
Map<String, Object> applicant = new HashMap<>();
Map<String, Object> property = new HashMap<>();
applicant.put("name", application.getApplicant().getName());
applicant.put("ssn", application.getApplicant().getSsn());
applicant.put("income", application.getApplicant().getIncome());
property.put("address", application.getProperty().getAddress());
property.put("price", application.getProperty().getPrice());
appContent.put("applicant", applicant);
appContent.put("property", property);
appContent.put("downPayment", application.getDownPayment());
appContent.put("amortization", application.getAmortization());
if (application.getAppraisal() != null) {
Map<String, Object> appraisal = new HashMap<>();
appraisal.put("property", property);
appraisal.put("value", application.getAppraisal().getValue());
appContent.put("appraisal", appraisal);
}
wrapper.put("com.redhat.bpms.examples.mortgage.Application", appContent);
payload.put("application", wrapper);
} catch (Exception e) {
...
}
4.3.2.4. Controllers
Java controllers are used in typical RESTful web service style to simply relay requests from front-end interactions to services that are responsible for the business logic layer relevant to each entity or human task type. In more advanced examples, this allows possible combining of multiple KIE calls and/or decouple application code from KIE configuration and implementation details. Weld dependency injection and javax.javaee_api annotations allow for simple, straight-forward controller classes that are easy to both read and consume. The following class, BrokerController, is a good example of code that allows various actions correlating to the user interface to be performed:
@GET
@Path("/dataCorrectionTasks")
@Produces(MediaType.APPLICATION_JSON)
public List<TaskSummary> listTasks() {
return brokerRestService.listTasks();
}
@POST
@Path("/claimTask")
@Consumes(MediaType.APPLICATION_JSON)
public Response.Status claimTask(Long taskId) {
return brokerRestService.claimTask(taskId);
}
...
@POST
@Path("/completeTask/{taskId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response.Status completeTask(@PathParam("taskId") Long taskId, Application application) {
return brokerRestService.completeTask(taskId, application);
}
}
4.3.2.5. Services
The Java service classes all extend from a base class RestClientService which houses a few calls across the multiple services, and also build on that functionality to define business logic and kie-server API calls relevant to each entity or human task type. ApplicantRestService is a small yet efficient example of how such calls to the Knowledge Store are performed:
@Singleton
public class ApplicantRestService extends RestClientService {
private static final Logger logger = LoggerFactory.getLogger(ApplicantRestService.class);
public Response.Status startApp(Application application) {
try {
ProcessServicesClient processServicesClient = initClient(Configuration.Users.KIESERVER)
.getServicesClient(ProcessServicesClient.class);
processServicesClient.startProcess(containerId, PROCESS_ID, ApplicationMapper.convert(application));
} catch (Exception e) {
logger.error("ERROR in Rest endpoint startApp...", e);
}
return Response.Status.OK;
}
}Like with the pom.xml configurations, the code required for each of the business process steps is lengthy and spans multiple services, so exploring the content of each is left as an exercise for the reader.
4.3.2.6. Configuration and Utility
Lastly, there are a few files which serve to aid in configuring Weld and abstracting some of the RESTful information needed to establish and utilize clients.
Configuration.java
public final class Configuration {
public static final String REST_BASE_URI = "http://rules.bxms.ose/kie-server/services/rest/server";
public static enum Users {
KIESERVER("kieserver", "kieserver1!"),
BROKER("brokerUser", "password1!"),
MANAGER("managerUser", "password1!"),
APPRAISER("appraiserUser", "password1!");
private final String username;
private final String password;
Users (String username, String password) {
this.username = username;
this.password = password;
}
public String username() {
return this.username;
}
public String password() {
return this.password;
}
}
}
EndpointApplication.java
@ApplicationPath("service")
public class EndpointApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> resources = new java.util.HashSet<>();
addRestResourceClasses(resources);
return resources;
}
private void addRestResourceClasses(final Set<Class<?>> resources) {
resources.add(HomeController.class);
resources.add(BrokerController.class);
resources.add(ApplicantController.class);
resources.add(TroubleshootController.class);
resources.add(AppraiserController.class);
resources.add(FinanceController.class);
}
}

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.