Oracle Java Cloud Service, Oracle JET and ADF BC REST Production Experience

Technical Tips and Tricks

by Andrejus Baranovskis

May 2017

Introduction

The Java Cloud Service (JCS) production application the is the focus of this article includes invoice processing, warehouse stock management and production line management. This application was implemented for a candy production and distribution startup company in Lithuania.

The startup's primary goal in this endeavor was to minimize and simplify the IT infrastructure's complexity and investment, which is why JCS was chosen. Oracle ADF is integrated into the Oracle PaaS Cloud offering. JCS offers a cloud-based deployment environment, which is preconfigured with support for Oracle ADF features. Oracle ADF provides a powerful functionality for business logic implementation in the ADF BC layer. It allows exposing of ADF BC objects and methods through REST services.

While ADF support was one of the key decision points for using JCS, the fact that Oracle JET can also be served from JCS instance makes it really convenient and enables us to run server-side business logic and serve client-side content from the same cloud instance.

The invoice processing module is based on this list of functionalities:

  • Dashboard - statistical data display
  • New Invoice – invoice creation
  • Template Setup – invoice template setup, to speed up invoice creation
  • Invoice Search – search through all invoices and invoice edit
  • Customer Setup – customer data management
  • Supplier Setup – supplier data management

The UI is implemented entirely with Oracle JET, which supports responsive UI behavior out of the box. This means the UI is implemented only once and adjusts to whichever screen the user is viewing the material on, from desktop to mobile.

The Invoice Search screen features a search form block and results table with pagination:

baranovskis-jcs-jet-fig01
Figure 1

Figure 2 shows the same Invoice Search form displayed on a mobile device screen. JET table pagination control is automatically updated to fit into a narrow layout:

baranovskis-jcs-jet-fig02
Figure 2

The user has the option to select the invoice for editing. The Invoice edit screen brings functionality to edit invoice data. We are using the Alta UI look and feel shipped with JET. Alta UI templates provide a clean and light UI experience (Figure 3):

baranovskis-jcs-jet-fig03
Figure 3

Invoice items can be edited inside a dialog. This allows the end user to focus on a specific item and keep track of changes more easily. Oracle JET runs on the client side, and navigation between invoice items works quickly. There is no need to close the dialog each time; when the user wants to open another invoice item, a simple click on the invoice item is enough (Figure 4):

baranovskis-jcs-jet-fig04
Figure 4

Application Architecture Overview

The application is based on two deployment artifacts. There are two separate EAR deployment archives, one for the ADF Business Components REST service and another for the Oracle JET client-side application. Both artifacts are deployed to a JCS instance. ADF BC REST service is consumed by the Oracle JET application.

The ADF BC REST service is secure and can be accessed only by authenticated and authorized users. (Figure 5)

baranovskis-jcs-jet-fig05
Figure 5

EAR with Oracle JET acts only as a wrapper. No content from this EAR is executed on the server.

Oracle JET is minified for production deployment with this Grunt command:

sudo grunt build:release

Minified content is copied into an empty Web application containing web.xml, packaged into EAR file and deployed to a WebLogic instance running on Oracle Java Cloud (JCS).

The ADF BC REST application is compiled and packaged into its own EAR, and deployed to the same WebLogic instance as Oracle JET.

Complex business logic and business rule validations are implemented using ADF BC custom methods. These custom methods are exposed through the ADF BC REST interface and are accessible for client-side calls. The ADF BC REST service is responsible for providing such standard operations as search, update, create and delete.

Oracle JET client-side logic is implemented to minimize the number of REST service calls. For example, when a user is changing invoice item data, we are synchronizing the invoice item model on the client side, without re-querying the full invoice item list from the server.

Oracle JET Implementation

UI implementation with Oracle JET

Application Structure

Application structure follows the Oracle JET template structure. Each logical application module is implemented in a separate JET module. I recommend creating common modules, where you could keep common functions and variables specific to the module. This would allow easier reuse, especially if you want to use a service from one module in another.

Figure 6 illustrates the JET application structure:

baranovskis-jcs-jet-fig06
Figure 6

Let’s take the invoice module as an example. This module imports two other common modules – invoiceController and customerController. Declaring modules in the define block preforms the import. To use modules in the JET JS function, you should pay attention to the order. Usually, you would get variables for JET Core and Knockout imports first in the function variables list; next should be variables for your own modules in the same order as it is set in the define block. (Figure 7)

baranovskis-jcs-jet-fig07
Figure 7

The invoice module provides a list of customers, in case the user wants to edit the invoice and change the assigned customer. This is why we import the customerController module, where a corresponding REST service call is constructed to fetch customer data (Figure 8):

baranovskis-jcs-jet-fig08
Figure 8

The same customerController module is used in the customer module (Figure 9):

baranovskis-jcs-jet-fig09
Figure 9

Such an approach, in which functionality is split into reusable modules, improves application maintenance in the future.

JET Router

The menu structure is implemented using JET router. In more complex applications (such as login page implementation), a static menu structure does not work. Before a user is authenticated, only the login module should be accessible without the application menu list; after successful authentication, the login module should be replaced by the application menu.

An Oracle JET application created from a template comes with an applicationController module, which is automatically loaded on first application access. By default, it contains JET Router initialization code. In this case, I have changed it to contain only the login module. This means, by default, only the login module will be accessible to the user (Figure 10):

baranovskis-jcs-jet-fig010
Figure 10

Upon successful authentication, in the success callback we re-initialize the JET router with the application menu structure. From the login module, we get access to the router root instance, configure it with the menu structure (by listing JET module names in array). Array with Navigation Data—with labels visible to the user—should also be constructed, by resetting the original array with menu labels (Figure 11):

baranovskis-jcs-jet-fig011
Figure 11

The JET router API function synch should be called at the end, to force menu changes to be visible on UI.

Read more: JET Router Cookbook.

Cross-Module Data Synchronization

For better maintenance, the Oracle JET application can be split into several modules. However, there are situations when different modules are dependent on each other (e.g., the customers list can be used in the invoice template module). On the other hand, the customer list is updated in the customer module. Each time the list is updated in the customer module, the customer list in the template module should synch the latest changes.

We can achieve this by calling the JS function in the dependent module. Such a call can be done through the module imported using the define block. In the example below, I will explain how to refresh the customer list in the invoice template module when this list is changed in the customer module.

Make sure to include the dependent module (template module) into the define block of the customer module (Figure 12):

baranovskis-jcs-jet-fig012
Figure 12

Once the module is included, we can reference it anywhere inside the customer module and call functions. As illusrated in Figure 13, we call the synchCustomersList function from the template module in the customer change success callback:

baranovskis-jcs-jet-fig013
Figure 13

Let’s take a look at what this function does. In this particular implementation, it clears the array of customer entries within the template module, fetching the fresh list through a REST service call, and populates the array from scratch. We could pass only the changed customer model and synch it into the array; however, the business logic requirement here was to re-fetch the entire list of customers every time the customer data was changed (Figure 14):

baranovskis-jcs-jet-fig014
Figure 14

Responsive Form Layout – Columns Grid

Oracle JET allows us to build complex layout forms. These forms are responsive out of the box. Read more about it in the JET Cookbook: Columns Grid.

On a wide layout, the form is displayed with three columns—with two rows in the first column, one row in the second, and two rows in the third:

baranovskis-jcs-jet-fig015
Figure 15

In the case of narrow display, the form layout is adjusted to one column (Figure 16):

baranovskis-jcs-jet-fig016
Figure 16

To achieve such behavior, we are using Oracle JET flex classes. Here is an example of the first column, which contains the customer search list and the status choice list (Figure 17):

baranovskis-jcs-jet-fig017
Figure 17

Two other columns are also wrapped into the Oracle JET flex class (Figure 18):

baranovskis-jcs-jet-fig018
Figure 18

Client Security and Secure ADF BC REST Service Interaction

REST service calls must be protected. Only authenticated and authorized users should have access to REST services and data.

In our system we rely on a server-side session ID generated during the first REST call to ADF BC REST service. We call the custom method exposed through the ADF BC REST service and pass the username/password through the Authorization header (Figure 19):

baranovskis-jcs-jet-fig019
Figure 19

If authentication is successful, in the callback we read the assigned server-side session ID from the custom response parameter and store it on the client side. At the same time, we clear username/password values, so as not to keep them on the client. All subsequent REST calls are done using the session ID, a value that is passed with each REST call. The server-side session exists with the same ID, to allow access to server- side services, without keeping the username/password on the client side (Figure 20):

baranovskis-jcs-jet-fig020
Figure 20

Field Validation

Oracle JET works great with both client- and server-side validations. We are using both types of validations. Deciding where to implement validation logic is not easy; from our experience, it makes sense to duplicate some of the critical validations on both client and server. Client validation would provide more performance, while server-side validation will ensure that only correct data will be submitted to the database. When validation logic is complex, it is likely easier to implement it on the server side.

In this example, client-side validation checks if the value exists in the list (Figure 21):

baranovskis-jcs-jet-fig021
Figure 21

If the list doesn’t return any key, it means the value doesn’t exist and we are going to provide error message text. JET provides a tracker object, which can be registered on all fields with validation. If there are validation errors, we can check tracker status in the beginning of the method and stop further execution.

baranovskis-jcs-jet-fig022
Figure 22

The error message for the Oracle JET HTML component can be defined through messageCustom property. The tracker is defined through the invalidComponentTracker property (Figure 23):

baranovskis-jcs-jet-fig023
Figure 23

Server-side validation is implemented in a very similar way: it is reported on the UI in the same way as client-side validation (Figure 24):

baranovskis-jcs-jet-fig024
Figure 24

The error message is assigned in the REST service call failure callback. We read the error response text and, based on a key, decide which error text to return (Figure 25):

baranovskis-jcs-jet-fig025
Figure 25

Read more: Cross-Field Validation Cookbook.

ADF BC REST Implementation

ADF BC REST is a natural choice for us to implement server-side logic and services. Oracle ADF BC technology offers a strong API for managing and processing data on the backend—it integrates very well with DB and gives various options to override data processing events (e.g., pre-update, pre-delete, etc.). This makes it relatively easy to code business logic and validation rules to protect and ensure consistency of the data. ADF BC implementation can be accessed from various clients, including Excel, ADF Faces and REST client. The REST API offered by ADF BC is rich and provides such options as hierarchical data query, pagination, batch processing and custom methods invocation.

Secure ADF BC REST Service

ADF BC REST comes standard with ADF Security protection. This ensures authenticated access to REST resources and also provides authorization support. Configuration is straightforward; developers need to run through a wizard and enable authentication/authorization support in the project (Figure 26):

baranovskis-jcs-jet-fig026
Figure 26

The wizard updates configuration file entries and the REST service becomes protected. During the authentication phase, it relies on the WebLogic security provider to verify if the user can be authenticated in the system. This makes ADF Security implementation flexible and compatible with various security solutions – default authenticator, SQL based authenticator, Active Directory, etc.

Initial authentication from the client can be done using basic authentication. But we don’t want to keep the username/password values on the client for the subsequent calls in the same session. The solution is to return the current session ID to the client and use it for the next calls. To return the session ID to the client, you can include it into the custom response parameter.

There must be a defined custom filter class to intercept requests to the ADF BC REST servlet (Figure 27):

baranovskis-jcs-jet-fig027
Figure 27

The custom filter can be mapped with the ADF BC REST servlet, which permits the intercepting of requests (Figure 28):

baranovskis-jcs-jet-fig028
Figure 28

We are interested only in the first request, where the custom method to login is called. Our filter will be invoked only if authentication is successful. We set the custom response parameter only if the call is done to the custom method (POST) and if the request contains an Authorization header. No subsequent calls will include the Authorization header; requests will be done with the session ID (Figure 29):

baranovskis-jcs-jet-fig029
Figure 29

Data Fetch and Range Paging

ADF BC supports range paging for data fetch from DB. This allows us to minimize the number of rows fetched, without fetching any rows not requested by the client (Figure 30):

baranovskis-jcs-jet-fig030
Figure 30

Range paging is also supported by ADF BC REST. The client can request to return only a specific page of rows. Here is an example of such a request:

invoicing/rest/v0/Invoices?limit=5&offset=20&totalResults=true

We request to return five rows from position twenty. The number of total results must be included in the response to calculate the total number of pages. This allows us to build client-side tables with pagination support (Figure 31):

baranovskis-jcs-jet-fig031
Figure 31

Custom Methods and Business Logic

One of the strong parts of ADF BC REST is the option for custom methods implementation in Entity, View and Application Module classes. Logic coded in custom methods can process user input, update/create data and perform complex queries. Custom methods implemented in the View object can be exposed through the ADF BC REST interface. To be exposed in ADF BC REST, the method should be defined in the client interface (Figure 32):

baranovskis-jcs-jet-fig032
Figure 32

Here is an example of such a method (Figure 33):

baranovskis-jcs-jet-fig033
Figure 33

Custom methods in ADF BC allow to move complex logic from the client to the backend and call it from the client through the API. In this example, we are creating a new invoice based on the invoice template. The invoice template data is fetched first, based on fetched rows; invoice rows are created and stored in DB.

Custom methods for REST access are defined in the ADF BC REST resource wizard (Figure 34):

baranovskis-jcs-jet-fig034
Figure 34

If you take a look into the ADF BC REST resource source code, the structure looks very similar to ADF page definition. This makes it easy for an ADF developer to understand how REST service is defined, and even to adjust some of the properties directly in the source code (Figure 35):

baranovskis-jcs-jet-fig035
Figure 35

Validation

ADF BC provides a list of built-in validation rules. Besides built-in rules, it also allows for the building of custom validation rules in Java or Groovy. This proves to be very useful, because ensures data integrity: invalid data can’t be submitted if the validation rule doesn’t succeed. Part of the validation rules can be implemented on the client side. But it makes sense to duplicate the key validation rules in the backend, too – just to ensure the client won’t abuse the service and submit invalid data, bypassing client-side checks. Complex validation rules—where you need to fetch arrays of data and compare various elements—should be done on the client side. However, there is no silver bullet; it all depends on the requirements.

Here is an example of a validation rule in ADF BC:

baranovskis-jcs-jet-fig036
Figure 36

Invoice date is validated not to be after the payment due date. If a validation error happens, an error code will be reported to the client, where appropriate error text can be displayed.

Oracle Java Cloud Service Configuration and Deployment

The diagram below describes our Oracle Cloud usage. The development phase is done using Oracle Developer Cloud Service (DCS). This includes wiki, issue tracking, agile sprint, Git source control and deployment automation. Development and production runtime works on Oracle Java Cloud and dependent DB Cloud instances (Figure 37).

baranovskis-jcs-jet-fig037
Figure 37

Figure 38 illustrates the number of issues in the sprint report from Oracle Developer Cloud Service:

baranovskis-jcs-jet-fig038
Figure 38

JCS Instance Setup

For us, as a development team, Oracle Cloud provides huge benefits in terms of infrastructure setup and administration. We don’t need to care about installing and configuring a WebLogic server. With Oracle Cloud we need only type a couple of parameters, wait around 15 minutes while the instance background process completes – and we’re good to go. Before, infrastructure setup could take days.

The current Oracle Cloud environment supports the latest ADF release (12.2.1.2). We are using this release to run ADF BC REST backend services. Version is specified in the first step of the cloud instance creation wizard (Figure 39):

Our production instance runs on a 1 CPU, 7.5 GB RAM instance. We found such a configuration is enough for our development environment and for production usage. Oracle Cloud JCS instance delivers very good throughput, but for on-premise setup you would probably need more compute resources to handle the same amount of load (Figure 40):

baranovskis-jcs-jet-fig039
Figure 39

Our production instance runs on a 1 CPU, 7.5 GB RAM instance. We found such a configuration is enough for our development environment and for production usage. Oracle Cloud JCS instance delivers very good throughput, but for on-premise setup you would probably need more compute resources to handle the same amount of load (Figure 40):

baranovskis-jcs-jet-fig040
Figure 40

In around 15 minutes, a JCS instance will be created -- quick and simple (Figure 41):

baranovskis-jcs-jet-fig041
Figure 41

Security Setup

ADF BC REST service resource permissions are assigned using Oracle Enterprise Manager in a JCS instance.

Permissions are mapped to Application Roles (Figure 42):

baranovskis-jcs-jet-fig042
Figure 42

Application Roles are mapped to Groups. Group and User mapping is defined in JCS instance authenticator storage (Figure 43):

baranovskis-jcs-jet-fig043
Figure 43

This makes security setup a straightforward process.

Manage Server Setup and Internet Access

We run two different environments on the same JCS instance – development and production. These environments must be isolated from each other. For this reason we have two independent DB schemas, dedicated for development and production, as well as two separate managed servers. This allows us to simplify maintenance because we can manage the development instance independently from the production instance (Figure 44):

baranovskis-jcs-jet-fig044
Figure 44

For the development environment, we are using a default managed server. For the production environment, we have created a new managed server ourselves.

A new managed server in a JCS instance can be created exactly in the same way as on premise, through Enterprise Manager UI. Make sure to specify a unique port:

baranovskis-jcs-jet-fig045
Figure 45

Select machine:

baranovskis-jcs-jet-fig046
Figure 46

The new managed server is up and running. In addition to default setup of admin and one managed server, we have an additional managed server configured (Figure 47):

baranovskis-jcs-jet-fig047
Figure 47

There are additional steps to complete to allow Internet access to the new managed server. You must go to the Oracle Cloud instance administration console and select Access Rules from the menu (Figure 48):

baranovskis-jcs-jet-fig048
Figure 48

Create a new access rule and provide a managed server port, along with the destination name. Once the rule is applied, the managed server should instantly become available for Internet access (Figure 49):

baranovskis-jcs-jet-fig049
Figure 49

JCS Instance Maintenance

The JCS instance comes with a handy monitoring metrics screen. You can learn about instance performance from different metrics (Figure 50):

baranovskis-jcs-jet-fig050
Figure 50

The JCS instance is configured with alert rules. If one of the metrics becomes true, the cloud instance administrator is notified by email (Figure 51):

baranovskis-jcs-jet-fig051
Figure 51

With the JCS instance running, we don’t need to bother about backups. There are incremental backups happening daily and full backups once a week. The backup schedule is configurable and can be changed by an administrator (Figure 52):

baranovskis-jcs-jet-fig052
Figure 52

Patches are also applied automatically, although some require manual intervention. You can run a report first that checks if a patch can be applied without errors. The patching process is controlled by a click of the button—there is no need to run any scripts yourself, as the system does all the dirty work for you (Figure 53):

baranovskis-jcs-jet-fig053
Figure 53

Oracle Cloud monitors degradation of service and takes action. During our production runtime, we once received an email about slow performance (Figure 54):

baranovskis-jcs-jet-fig054
Figure 54

We were pleased to receive a confirmation email when the performance incident was automatically resolved (Figure 55):

baranovskis-jcs-jet-fig055
Figure 55

 

About the Author

Oracle ACE Director Andrejus Baranovskis is CEO and technical architect with Red Samurai Consulting, based in Lithuania. Red Samurai was the receipient of the 2017 Oracle PaaS Partner Community Award for Outstanding Java Cloud Service Contribution, the 2015 Oracle Fusion Middleware Partner Community Award for Outstanding ACM/BPM Contribution, among other awards. A prolific blogger, Andrejus shares his considerable technical expertise on his blog.


This article has been reviewed by the relevant Oracle product team and found to be in compliance with standards and practices for the use of Oracle products.