Using External REST Services with JDeveloper Part 3

In this blog we look how we can use an external REST service with JDev 12.2.1.2. To make things more interesting we don’t use an ADF based REST service and we look how to get nested data into the UI.

For this sample we like to create an application which allows to search for music tracks and show the results in a table or listview. To get the music data we use a REST service and to display the data we use ADF faces application.

In Part 1 we create the application and the project for the REST Data Control. In Part 2 we started creating the UI using the REST Data Control. In this final part we are enhancing the UI by using nested data from the REST Web Service and add this as column to the search result table. Before we start we look at the use case again.

Use Case

Before we begin implementing something which uses the external REST service we have to think about the use case. We like to implement a music title search using the external MusicBrainz REST service. A user should be able to enter a music title or part of a music title and as a result of the search she/he should get a list of titles, the artist or artists, the album and an id.

 

Handling nested Data

The use case demands that we add the artist and the album the music track is on to the same table. A look at the table in it’s current layout, make this understandable.

First of all we need to identify the dat a we want to add to the table in the response we get from the service.

Let’s investigate the JSON data, part of it, we get from the service for the search for the track ‘yesterday’


 

{
   "created": "2017-08-02T12:42:48.815Z",
   "count": 5985,
   "offset": 0,
   "recordings": [
       {
           "id": "465ad10d-97c9-4cfd-abd3-b765b0924e0b",
           "score": "100",
           "title": "Yesterday",
           "length": 243560,
           "video": null,
           "artist-credit": [
               {
                   "artist": {
                       "id": "351d8bdf-33a1-45e2-8c04-c85fad20da55",
                       "name": "Sarah Vaughan",
                       "sort-name": "Vaughan, Sarah",
                       "aliases": [
                           {
                               "sort-name": "Sarah Vahghan",
                               "name": "Sarah Vahghan",
                               "locale": null,
                               "type": null,
                               "primary": null,
                               "begin-date": null,
                               "end-date": null
                           },
...
                       ]
                   }
               }
           ],
           "releases": [
               {
                   "id": "f088ce44-62fb-4c68-a1e3-e2975eb87f52",
                   "title": "Songs of the Beatles",
                   "status": "Official",
                   "release-group": {
                       "id": "5e4838fa-07f1-3b93-8c9d-e7107774108b",
                       "primary-type": "Album"
                   },
                   "country": "US",

I marked the info ne need in blue in the data above. We see that the artist name is inside a list of name ‘artist_credit’ and that there can be multiple artists inside the ‘artist_credit’. This is a typical master/detail relationship.

The same is true for the album name which is an attribute inside a list of ‘releases’. The big question now is how do we get the nested data into the table as column.

When we expand the MusicBrainz Data Control we see the same structure we saw in the JSON data

So, the data is there, we only need to get to it. The data is structured like a tree and ADF is capable of accessing data in a tree structure (e.g. using an af:tree component). However, we like to use a simple table and don’t want to use a af:tree or af:treeTable. To get to the data, we first have to add the nested structure to the recordings binding we already use to display the current two columns of the table.

Right now we see the first level of the tree, the ‘recodrings’. Click the green ‘+’ sign to add the next level ‘artist_credit’

Add all attributes to the right side

As the artist name is still one level down, click the green ‘+’ sign again and add the ‘artist’ level

And shuffle the id and name attribute to the right side

Finnally we need to add the ‘releases’ level to get to the album name. For this select the ‘recordings’ level (the first) and click the green ‘+’ sign

And shuffle the id, title and track_count to the right side

Now all related data we need can be accessed via the ‘recordings’ binding.

We start with the artist column. Select the af:table in the structure window and open hte properties window

Click the green ‘+’ sign twice in the columns section to add two columns

Select the first added column (score in the image) and change the display label to ‘Artist’ and the component To Use’ to ‘ADF Output Text’. The second added column we change the display label to ‘Album’ and the ‘Component To Use’ again to ‘ADF Output Text’

We change the ‘Value Binding’ in the next step.

To get to the data for the artists we need to traverse two levels of sub rows. First level is the ‘artist_credit’, the second level is the artist itself. Here we have to keep in mind, that there can be more than one artist. In this case we have to join the names into one string for the table. As the ‘artist_credit’ itself can occur more than once, at least that’S what the data structure is telling us, we use an iterator to get the data.

The value property points to the current row and selects the ‘artist_creadit’. Each item we get from this iterator we access via the var property. So the item inside the iterator can be addressed as ‘artists’.

The artists can be one or more so we need another iterator to get to the artist data.

<af:iterator id="i2" value="#{artists.artist}" var="art" varStatus="artStat">

The value property for this iterator points to the artist we got from the outer iterator and is addressed as #{artists.artist}. To access attributes inside the artist data structure we use the var property and set it to ‘art’.

Now we have to somehow joint multiple artist names together if a track has more than one artist. The MusicBrainz Web Service helps us here by providing a ‘joinphrase’ which can be used to build one string for all artists. This ‘joinphrase’ can be .e.g a ‘&’ or a ‘,’. The full column code for the artist looks like

<af:iterator id="i2" value="#{artists.artist}" var="art" varStatus="artStat">

Here is some sample data for a search for the track ‘Something Stupid’ (to make it more readable I removed some attributes

"recordings": [
 {
  "title": "Something Stupid",
  "artist-credit": [
   {
    "joinphrase": " duet with ",
    "artist": {
     "name": "The Mavericks",
    }
   },
   {
    "joinphrase": " & ",
    "artist": {
     "name": "Raul Malo",
    }
   },
   {
    "artist": {
     "name": "Trisha Yearwood",
    }
   }
 ]

This data will be translated into the artist: “The Mavericks duet with Raul Malo & Trisha Yearwood”.

For the album column it’s easier. This too needs an iterator, but we don’t have to go down another level and we don’T have to join the data we get from the iterator. The column code for the album looks like

<af:iterator id="i1" value="#{row.artist_credit}" var="artists">
 <af:iterator id="i2" value="#{artists.artist}" var="art"
                    varStatus="artStat">
   <af:outputText value="#{art.name}#{artists.joinphrase}" id="ot5"/>
 </af:iterator>
</af:iterator>

The whole table for the search results look like

With this the page is ready and we can run the application. After start we see the page

Now entering a search term ‘something stupid’ into the search field will show

or trying the search with ‘dave’ will show

This concludes this mini series about how to use external REST Services and build an ADF UI from it.

The source code for this sample can be loaded from GitHub BlogUsingExternalREST. The sample was done using JDeveloper 12.2.1.2 and don’t use a DB.

Using External REST Services with JDeveloper (Part 2)

In this blog we look how we can use an external REST service with JDev 12.2.1.2. To make things more interesting we don’t use an ADF based REST service and we look how to get nested data into the UI.

For this sample we like to create an application which allows to search for music tracks and show the results in a table or listview. To get the music data we use a REST service and to display the data we use ADF faces application.

In Part 1 we create the application and the project for the REST Data Control. In Part 2 we start create the UI using the REST Data Control. Before we start we look at the use case again.

Use Case

Before we begin implementing something which uses the external REST service we have to think about the use case. We like to implement a music title search using the external MusicBrainz REST service. A user should be able to enter a music title or part of a music title and as a result of the search she/he should get a list of titles, the artist or artists, the album and an id.

Implementing the UI

In Part 1 we implemented the REST Data Control which we now use to build a small UI. Let’s look at the REST Web Service Data Control in the JDeveloper IDE

Above we see the data control ‘MusicBrainzJSONDC’ with it’s only resource recording, the input parameter names ‘query’ and the return data structure which was created using the sample JSON data we used when creating the REST Web Service Data Control.

When we query the resource we get back a complex data structure which give us information about how many results where found for the query and a list of ‘recordings’ which holds the artist names and the album names as ‘releases’.

To build the result table which should show the title id, the artist or artists and the album we have to go through all the nested data.

Setting up the search page

We start by adding a view the unbounded task flow adfc-config.xml which we name ‘MunicBrainz’ and create the page with a quick layout from the list

Make sure that you have selected to use ‘Facelets’! This will create a starter page with the layout selected. When the page is created it opens up in JDev like

We add an outputText component to the header and set the value to ‘MusicBrainz Test’

The resulting code looks like

For the layout we want to archive (search part and table to show the results) we need another grid row in the panelGridLayout. We drag a GridRow component from the ‘Component palette’ onto the panelGridLayout component in the structure window. You can use the source window too if you like. Dropping a new gridRow in the design isn’t recommended as it’s difficult to control the point where to insert the component.

Now we adjust the height of the rows and set the first row to 50 pixel, the second one to 100 pixel and leave the remaining height to the third gridRow:

Next we add the panelFormLayout holding the search field and the button to search for music tracks. For this we simply drag the ‘recording(String)’ operation from the MusicBrainzJSONDC data control onto the second grid row and drop it as ‘ADF Parameter Form’

we get a dialog showing us the methods parameter. Here we can bind the field to any other data field we like. However, in this case we leave it as is and just click OK

The framework wires everything up for us and we get the page as

Here we change the text on the button to ‘Search’

To see how things are wired up we look at the pagedef for the page

Here we see the method ‘recording’ and can expand it by clicking on the pencil icon

Where we see the details like where the parameter ‘query’ gets it’s value from (#{bindings.query.inputValue}). The ‘query’ binding is defined right above the recording method:

When we select the binding for ‘query’ wee see that the binding points to a variable defined in the pagedef (see Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef) which holds the value the user enters into the field. The recordings binding and the other stuff we talk about later.

Next up is creating the table with the results returned from the method call. For this we drag the recordings from the methodReturn binding onto the page and drop it as ADF Table into the third gridRow

To get the next dialog

Where we remove every attribute but the ‘id’ and the ‘title’ by selecting the rows and clicking the red ‘x’ icon. We set the row selection to single and make the table ‘read only’

The resulting page looks like

If we run the application now the UI comes up, but we’ll get an exception

Why’s that?

If we look into the servers log we see the error:-


<oracle.adf.view> <Utils> <buildFacesMessage> <ADF: Adding the following JSF error message: JBO-57001: Invocation of service URL used in connection failed with status code 400 Unable to parse search:tnum:.> 
oracle.adf.model.connection.rest.exception.RestConnectionException: JBO-57001: Invocation of service URL used in connection failed with status code 400 Unable to parse search:tnum:.
    at oracle.adf.model.connection.rest.RestConnection.getResponseCheckingStatus(RestConnection.java:783)
    at oracle.adf.model.connection.rest.RestConnection.getResponse(RestConnection.java:629)
    at oracle.adfinternal.model.adapter.ChildOperation.getJerseyResponse(ChildOperation.java:1167)
    at oracle.adfinternal.model.adapter.ChildOperation.makeServerCall(ChildOperation.java:977)
    at oracle.adfinternal.model.adapter.JSONChildOperation.invokeOperationInternal(ChildOperation.java:2056)
    at oracle.adfinternal.model.adapter.ChildOperation.invokeOperation(ChildOperation.java:542)
    at oracle.adf.model.adapter.rest.RestURLDataControl.invokeOperation(RestURLDataControl.java:247)
    at oracle.adf.model.bean.DCBeanDataControl.invokeMethod(DCBeanDataControl.java:512)
    at oracle.adf.model.binding.DCInvokeMethod.callMethod(DCInvokeMethod.java:269)
    at oracle.jbo.uicli.binding.JUCtrlActionBinding.doIt(JUCtrlActionBinding.java:1742)
    at oracle.adf.model.binding.DCDataControl.invokeOperation(DCDataControl.java:2371)
    at oracle.adf.model.bean.DCBeanDataControl.invokeOperation(DCBeanDataControl.java:628)
    at oracle.adf.model.adapter.AdapterDCService.invokeOperation(AdapterDCService.java:316)
    at oracle.jbo.uicli.binding.JUCtrlActionBinding.invoke(JUCtrlActionBinding.java:803)
    at oracle.jbo.uicli.binding.JUMethodIteratorDef$JUMethodIteratorBinding.invokeMethodAction(JUMethodIteratorDef.java:175)

 


Which doesn’t tell us more. What we see is that an ‘invokeMethod’ is the root cause. The reason is that when the pages loads, the iterators in the executable section of the pagedef are fired. As we saw we have two executables and those are giving us the errors.

As the field is empty the recordings method is called without a query parameter. If you mimic this in Postman with the query

http://musicbrainz.org/ws/2/recording/?fmt=json&query=

we get

Exactly the same error, only this time as html.

To solve this problem we have to avoid calling the service without a parameter. This can easily be archived by adding an expression to the executable RefreshCondition property

This we have to both executables in the pagedef. After that running the application will get us

 

This ends part 2 of this series, due to the length and the number of images in this post. The remaining part 3 will cover how to use the nested data and to add it to the search result table and provide the link to the sample application.

JDeveloper: Advanced Skin Technique

This post is about an advanced technique to change the look and feel of an ADF application. Changes to the look & feel are normally done via a skin which you use to change descriptors which are used by the ADF components. The general technique to do this is described in many blogs and articles like ADF Faces Skin Editor – How to Work with It and the official documentation at Oracle ADF Skin Editor.

In this blog we look at an advanced technique which helps to change the look and feel of components like af:query and pf:panelCollection which you can’t change using the normal available descriptors. In the below image you see the Skin Editor showing the ADF components skin descriptors.

selection_985

Use Case

In this use case we work with the af:panelCollection component. This component is used to wrap af:tree, af:treeTable and af:table components to provide additional functions. From the documentation of af:panelCollection

A panel component that aggregates collection components like table, treeTable and tree to display standard/application menus, toolbars and statusbar items.

The default top level menu and toolbar items vary depending on the component used as the child of the panelCollection.

  • For table, tree and treeTable, the default top level menu item is View.
  • For table and treeTable with selectable columns, the default top level menu items are View and Format.
  • For table and treeTable, the default toolbar item is Detach.
  • For table and treeTable with selectable columns, the default top level toolbar items are Freeze, Detach and Wrap.
  • For tree and treeTable, if the pathStamp facet is used, the toolbar buttons Go Up, Go To Top, Show as Top also appear.

The component allows us to switch off some function

Value Turns off
statusBar Status bar
viewMenu ‘View’ menu
formatMenu ‘Format’ menu
columnsMenuItem ‘Columns’ sub-menu item
columnsMenuItem:col1,col20 Columns with column ID: ‘col1’ and ‘col20’ inside ‘Columns’ sub-menu
freezeMenuItem ‘Freeze’ menu item
detachMenuItem ‘Detach’ menu item
sortMenuItem ‘Sort’ menu item
reorderColumnsMenuItem ‘Reorder Columns’ menu item
resizeColumnsMenuItem ‘Resize Columns’ menu item
wrapMenuItem ‘Wrap’ menu item
showAsTopMenuItem Tree/TreeTable ‘Show As Top’ menu item
scrollToFirstMenuItem Tree/TreeTable ‘Scroll To First’ menu item
scrollToLastMenuItem Tree/TreeTable ‘Scroll To Last’ menu item
freezeToolbarItem ‘Freeze’ toolbar item
detachToolbarItem ‘Detach’ toolbar item
wrapToolbarItem ‘Wrap’ toolbar item
showAsTopToolbarItem Tree/TreeTable ‘Show As Top’ toolbar item
wrap ‘Wrap’ menu and toolbar items
freeze ‘Freeze’ menu and toolbar items
detach ‘Detach’ menu and toolbar items

As a sample the image below shows a normal af:panelCollection (upper half) and an af:panelCollection with the view menu and the toolbar switched off (lower half)

selection_979

Looking at the possible things to switch off we don’t see anything to switch off the ‘Query by Example’ (QBE) icon. There is no feature toggle to turn this function on or off. An easy way to get rid of the icon would be to make the table not filterable. However, if we like the table to be filterable but don’t want to show the icon to switch the feature off, we have to use an advanced skin technique.

What can we do to get rid of the icon in the tool bar?

The idea is to use a skin or special css to hide the icon or the container which holds the icon. To find the container we first inspect the page in the browser using the browsers ‘Developer Tools’ which you can reach by hitting F12 in your browser. Below you see Chrome 55 with activated ‘Developer Tools’

Selection_211.jpg

The image shows the toolbars QBE image as selected element on the page (left red rectangle) and the style classes which are in use for this element (right red rectangle). The names ‘.xfo’ and ‘.xfr’ are the names of the style classes. They are minimized to reduce the download size of the page, but they are not ‘readable’. 

The first thing to do is to make the names ‘readable’ for us. We need to know which skin selector generated the style class. For this we set a context parameter in the web.xml file

 <context-param>
 <param-name>org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION</param-name>
 <param-value>true</param-value>
 </context-param>

Setting this parameter to true will show us the clear names. The image below shows the same selection only this time with the real names

selection_212

One other nice feature of the ‘Developer Tools’ is that you can inspect elements by just hover over them on the page. This allow us to easily find the element we want to hide via css. Click on the icon marked in hte below image

selection_213

and move the mouse cursor over the page. You see the HTML and the active styles of the element under the cursor. This feature we use to find an element which holds the icon we want to hide and which we can address via css .

selection_214

CSS allows us to address elements inside a skin selector.  For this you need to know the skin selector, the tag or container and it’s ID inside the selector you want to address. In the image above we see the ID of the icon container we want to hide as “id=’pc1:_qbeTbr'” and the container or tag itself which is a ‘div’. The skin selector is the af|panelCollection. With this information we can can change the style attached to the ‘div’ container with the id ‘*_qbeTbr’  in the af|panelCollection as

af|panelCollection div[id$='_qbeTbr'] {
    display: none;
}

This we can add to our skin.css file. However, if we just add it this way it’s changing all af:panelCollection in our application.  If we want this only to be active for specific af:panelColletion we can add a style class name like

af|panelCollection.myPCClass div[id$='_qbeTbr'] {
    display: none;
}

Now we can add the stale class name ‘myPCClass’ to the af:panelCollection when we like the QBE icon not to be shown

 <af:panelCollection id="pc1" styleClass="myPCClass">
   <f:facet name="menus"/>
   <f:facet name="toolbar"/>
   <af:table value="#{bindings.EmployeesView1.collectionModel}" ...
   ...
 <af:panelCollection id="pc2" featuresOff="detachToolbarItem viewMenu">
 <f:facet name="menus"/>
 <f:facet name="toolbar"/>
 ...

will generate this UI output

selection_217

 

As we see, the QBE icon is gone. In the original page we have placed two af:panelCollection components. As you added the new style class only to one of them, the other QBE icon is still visible.

Extending

You can use hte same technique for other complex ADF components like af:query. Here you can style the save button which normally not  supported.

Download

You can download the sample which is build using JDev 12.2.1.2.0  and uses the HR DB schema from GitHub BlogAdvancedSkin

JDev12c: Searching an af:tree

On the JDev & ADF OTN space I got a question on how to search an af:tree and select and disclose the nodes found matching the search criteria.

Problem description

We like to search an af:tree component for string values and if we find the value we like to select the node where we found the string we searched for. If the node where we found the string is a child node we disclose the node to make it visible.

Final sample Application

I started with building a sample application and show the final result here:

selection_935

We see a tree and a check box and a search field. The checkbox is used to search only the data visible in the tree or the whole data model the tree is build on. The difference is that you build the tree from view objects which can hold more attributes than you like to show in the tree node. This is the case with the sample tree as we see when we search for e.g. ‘sa’ in the visible data

selection_936

When we unmark the check box and repeat the search we get

selection_937

As you see we found another node ‘2900 1739 Geneva’ which doesn’T have the searched string ‘sa’. A look into the data model, the row behind this node shows

selection_938

We see that the street address which we don’t show in the node has the search string. To show that the search works for every node we set the search field to ‘2’ and get hits in different levels

selection_939

The sample application can be downloaded from GitHub. For details on this see the end of this blog.

Implementation

Now that we saw the running final application let’s look at how to implement this. We start by creating a small ADF Fusion Web Application. Is you like to you can start by following the steps given in  Why and how to write reproducible test cases.

Model Layer

Once the base application is created we setup the data model we use to build the tree. For this sample we use ‘Regions’, ‘Countries’ and ‘Location’ of the HR DB schema. To build the model we can use the ‘Create Business Components from Table’ wizard and end up with

selection_942

As you see I’ve renamed the views. The names now show what you’ll see when you use them. We only have one top level view object ‘RegionsView’ which will be the root of our tree in the UI. The child view are used to show detailed data.

View Controller

For the view controller layer we start by a simple page from the ‘Quick Layout’ section

selection_943

Now we add a title and add an af:splitter to the content area. Here we set the width of the first facet to 250 px to have enough room for the search field. We start with building the af:tree from the data control by dragging the ‘RegionsView’ from the data control onto the content area and dropping it as af:tree

Here we don’t select to show all attributes available but only a few.  Later we see that we can search the whole data model and not just only the visible data. Finally we bind the tree to a bean attribute to have access to the tree from the bean when we have searched it. This is a pure convenience, we could search the component tree each time we need the component to avoid the binding to a bean attribute.  When we create the bean we name it ‘TreeSelectionBean’ and set its scope to ‘Request’.  The bean will end in the adfc-config.xml

selection_950

the final code for the af:tree looks like

<af:tree value="#{bindings.RegionsView.treeModel}" var="node"
selectionListener="#{bindings.RegionsView.treeModel.makeCurrent}"
rowSelection="single" id="t1"
binding="#{TreeSelectionBean.tree}">
  <f:facet name="nodeStamp">
    <af:outputText value="#{node}" id="ot2"/>
  </f:facet>
</af:tree>

Now we create two pageDef variables as java.lang.String to hold the search string and the selection for the check box. If you need more information on how to create pageDef variables see Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef.

selection_949

In the first facet we add a check box and an af:inputText inside an af:panelGroupLayout and bind the value properties to the pageDef variables as

<af:panelGroupLayout id="pgl2" layout="vertical">
  <af:selectBooleanCheckbox text="node only" label="Seach" id="sbc1"
value="#{bindings.myNodeOnly1.inputValue}"/>
  <af:inputText label="Search for" id="it1" value="#{bindings.mySearchString1.inputValue}"/>
  <af:button text="Select" id="b1"
actionListener="#{TreeSelectionBean.onSelection}"/>
</af:panelGroupLayout>

The final thing to do is to wire the button to a bean method which does all the hard work. In the code above this is done with an actionListener which is pointing to the same bean created for the tree binding.


<span></span>public void onSelection(ActionEvent actionEvent) {
<span></span>JUCtrlHierBinding treeBinding = null;
// get the binding container
<span></span>BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
<span></span> // get an ADF attributevalue from the ADF page definitions
<span></span> AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("mySearchString1");
 String node = (String) attr.getInputValue();

// nothing to search!
 // clear selected nodes
<span></span> if (node == null || node.isEmpty()){
<span></span> RichTree tree = getTree();
<span></span> RowKeySet rks = new RowKeySetImpl();
<span></span> tree.setDisclosedRowKeys(rks);
 //refresh the tree after the search
<span></span> AdfFacesContext.getCurrentInstance().addPartialTarget(getTree());

return;
 }

<span></span> // get an ADF attributevalue from the ADF page definitions
<span></span> AttributeBinding attrNodeOnly = (AttributeBinding) bindings.getControlBinding("myNodeOnly1");
<span></span> String strNodeOnly = (String) attrNodeOnly.getInputValue();
<span></span> // if not initializued set it to false!
<span></span> if (strNodeOnly == null) {
<span></span> strNodeOnly = "false";
 }
<span></span> _logger.info("Information: search node only: " + strNodeOnly);

<span></span>//Get the JUCtrlHierbinding reference from the PageDef
<span></span> // For JDev 12c use the next two lines to get the treebinding
<span></span> TreeModel tmodel = (TreeModel) getTree().getValue();
<span></span> treeBinding = (JUCtrlHierBinding) tmodel.getWrappedData();
<span></span> // For JDev 11g use the next two lines to get the treebinding
<span></span> // CollectionModel collectionModel = (CollectionModel)getTree().getValue();
<span></span> // treeBinding = (JUCtrlHierBinding)collectionModel.getWrappedData();
<span></span> _logger.info("Information tree value:" + treeBinding);

//Define a node to search in. In this example, the root node
 //is used
<span></span> JUCtrlHierNodeBinding root = treeBinding.getRootNodeBinding();
 //However, if the user used the "Show as Top" context menu option to
 //shorten the tree display, then we only search starting from this
 //top mode
<span></span> List topNode = (List) getTree().getFocusRowKey();
<span></span> if (topNode != null) {
 //make top node the root node for the search
<span></span> root = treeBinding.findNodeByKeyPath(topNode);
 }
<span></span> RichTree tree = getTree();
<span></span> RowKeySet rks = searchTreeNode(root, node.toString(), strNodeOnly);
<span></span> tree.setSelectedRowKeys(rks);
 //define the row key set that determines the nodes to disclose.
<span></span> RowKeySet disclosedRowKeySet = buildDiscloseRowKeySet(treeBinding, rks);
<span></span> tree.setDisclosedRowKeys(disclosedRowKeySet);
 //refresh the tree after the search
<span></span> AdfFacesContext.getCurrentInstance().addPartialTarget(tree);
 }

In line 4-7 we get the value the user entered into the search field. Lines 9-19 check if the user has given a search string. If not we clear the currently selected nodes from the tree by creating a new empty RowKeySet and setting this to the tree.

If he got a search string we check if we should search the visible data only or the whole data model. This is done by getting the value from the check box (lines 21-28). Now we data from the tree (lines 30-37).

One thing we have to check before starting the search is if the user has used the ‘show as top’ feature of the tree. This would mean that we only search beginning from the current top node down (lines 39-49).

The search is done in a method

private RowKeySet searchTreeNode(JUCtrlHierNodeBinding node, String searchString, String nodeOnly)

this we pass the start node, the search string and a flag if we want to search the whole data model or only the visible part. The method returns a RowKeySet containing the keys to the rows containing the search string (line 51-52). This list of row keys we set to the tree as selected rows (line 54). As we would like to disclose all rows which we have found, we have to do one more step. This step uses the row key and traverses upward in the tree to add all parent node until the node is found where we started the search (line 53-55). This is necessary as you only see a disclosed child node in a tree if the parent node is disclosed too. For this we you a helper method (line 54) and set the row keys as disclosed rows in the tree.


 /**
<span></span> * Helper method that returns a list of parent node for the RowKeySet
<span></span> * passed as the keys argument. The RowKeySet can be used to disclose
 * the folders in which the keys reside. Node that to disclose a full
<span></span> * branch, all RowKeySet that are in the path must be defined
 *
<span></span> * @param treeBinding ADF tree binding instance read from the PageDef
 * file
<span></span> * @param keys RowKeySet containing List entries of oracle.jbo.Key
<span></span> * @return RowKeySet of parent keys to disclose
 */
<span></span> private RowKeySet buildDiscloseRowKeySet(JUCtrlHierBinding treeBinding, RowKeySet keys) {
<span></span> RowKeySetImpl discloseRowKeySet = new RowKeySetImpl();
<span></span> Iterator iter = keys.iterator();
 while (iter.hasNext()) {
<span></span> List keyPath = (List) iter.next();
<span></span> JUCtrlHierNodeBinding node = treeBinding.findNodeByKeyPath(keyPath);
<span></span> if (node != null && node.getParent() != null && !node.getParent().getKeyPath().isEmpty()) {
 //store the parent path
<span></span> discloseRowKeySet.add(node.getParent().getKeyPath());
 //call method recursively until no parents are found
<span></span> RowKeySetImpl parentKeySet = new RowKeySetImpl();
<span></span> parentKeySet.add(node.getParent().getKeyPath());
<span></span> RowKeySet rks = buildDiscloseRowKeySet(treeBinding, parentKeySet);
<span></span> discloseRowKeySet.addAll(rks);
 }
 }
<span></span> return discloseRowKeySet;
 }

This concludes the implementation of the sear in a tree.

Download

The sample application uses the HR DB schema and can be downloaded from GitHub

The sample was build using JDev 12.2.1.2.

 

Quo vadis ADF?

Last week I attended DOAG Konferenz & Ausstellung in Nürnberg Germany. The DOAG (Deutsche ORACLE-Anwendergruppe e.V.) is the biggest German Oracle user group. The conference covers all Oracle products and technologies, way too much to name them all.

As my personal center of gravity is middle-ware and here ADF and the surrounding technologies, I attended lot’s of sessions about middle-ware, cloud, ADF, MAF and JET. The big picture of Oracle becoming a cloud company is getting clearer.

The way developers currently are working on premise with their products migrating to the cloud is getting clearer. There where about 4-5 sessions which gave explicit advice when to use which technology and what problems might arise mixing them. I’ll cover the main three here.

Frank Nimphius started with a session ‘The Future of Application Development Welcome to your new Job’ where he summarized areas of future of application development as

  • “Server-less” deployment
  • [Micro] [Cloud] Services
  • REST & JSON
  • Mobile centric
  • API first
  • Multi channel
  • Artificial Intelligence
  • Cloud Native Development
  • JavaScript
Future Application Development Summary 1

Future Application Development Summary 1

Future Application Development Summary 2

Future Application Development Summary 2

and defined different job roles around this like

  • Citizen (Low Code) Developer
  • Mobile Developer
  • Service Developer
  • Architect
  • Line of Business Manager

Each role using different technologies to fulfill the tasks. This should open spaces for new and old developers

Mobile Job Roles

Mobile Job Roles

Duncan Mills tackled the bear from a different perspective. In his session ‘Standing at Crossroads’ (Oracle ADF and Oracle JET) he pointed out the differences between ADF and JET

Oracle ADF Oracle JET
Support 5 + 3 + unlimited, no backport limitations Major release every 6 month, backports only to previous version
API are stable No guarantee of API stability
Could or on premis Cloud
Metadata focused Code focused
Full stack solution Client only solution
Has to „own“ the page Can be used „anywhere“

However, there are things both have in common, as Duncan states:

“Don’t assume the you have to go to JET to look ‘modern'”

“Don’t assume that JET will automatically be more perfomant”

There are more things you have to take into account before making a decision between ADF and JET like

  • Transaction and Services: here you have to check if your services and data model can support a stateless model. Same for your UI which handles the interaction with the user. One thing to note too is that using JET will produce less client – sever traffic.
  • Need to shape the services for the convenience of the UI: paging data, pre-computation, attribute reduction and mega endpoints

If you plan to mix ADF and JET there are a couple of things which should make you think twice:

  1. No session sharing between ADF and JET
  2. ADF and JET can’t use the same cache
  3. No shared transaction
  4. Separate timeouts
  5. geometry management
  6. Drag & drop not possible between ADF and JET
  7. Different maintenance and different libraries
  8. Different popup’s and glasspane

Summary is that there are plenty of reasons not to mix ADF and JET. If you want to mix ADF and JET in a project you should stick to module level and not mix them on one page.

duncan_doag5

The decision for ADF or  JET should take these points into account.

Shay Shmeltzer attended the German Oracle (ADF) Developer Community meeting on the DOAG and we ask him to talk about this topic ‘The Future of Developer Frameworks’.

shay2_doag1 Shay started by giving a main difference between ADF and JET:

“ADF is a framework, JET is a toolkit”

meaning that ADF allows development in all tires (MVC) whereas JET is only a client technology. Using JET you still have to have a back-end which generates the needed REST services. Here ADF comes into the picture again.

“ADF hides the complexity of the technology from the developer” 

True, building a REST service from an exiting ADFbc model is very easy and allow shaping the service too. Besides ORDS (Oracle REST Data Service, a tooling which allows to develop modern REST interfaces for relational data in the Oracle Database ) this is the easiest way I know.

During the Q&A of his talk we specifically ask him how Oracle sees the future of ADF as some rumors are that ADF is dead. Shay answered (loud and clear):

“ADF isn’t dead!”

Oracle is using ADF heavily in the SaaS products and will going on to do so. There are areas where building UI with JET is preferred (not in SaaS), but here the points mentioned by Duncan Mills are always considered.

My personal opinion is that ADF is alive will be used in the future, but there are options now which allow developers to choose different technologies in certain areas. Using ADF in the model layer and working with relational data bases, create REST or SOAP services with ease is a big plus. For the UI there are use cases where JET will be used, but ADF has its share too.

Summary of Day 3 at Oracle Open World 2016

Started with the (early) morning keynote ‘Oracle OpenWorld Tuesday Morning Keynote‘ hosted by Bhanu Murthy B. M., Safra Catz, Hon. Chief Minister Shri. Devendra Fadnavis and Thomas Kurian.

As the keynote and it’s content is covered all over the media already I won’t add to this. Oh, one thing I like to say is that the ‘live’ demos did not really look live to me. Would you risk that your ‘live’ demo is going to hell because of some technical problem with Thomas Kurian on stage?


Next on my list for today was ‘Agile Development and DevOps Done Even Faster with Oracle IaaS and PaaS‘ by Michael Lehmann, Suhas Uliyar and  Siddhartha Agarwal. This session talked about agile development in the cloud using IaaS, PaaS and Microservices together with DevOps tools like Docker.

First a Cloud Navtive Architecture was introduced:

img_bqnk2w

Cloud Native Architecture

 Multiple services working together to build the cloud native architecture
img_-68tuak

Services for the Cloud Native Architecture

The practical part was a sample which showed how to build, deploy, or manage mobile-fronted, API-first autoscaling application, a microservice build on Node.js here, live on stage. New here is htat you can use the Management Cloud Service to introspect the microservice to see how it runs on your environment. The just build service then is consumned by anohter app (mobile using MAX) to visualize the data.

The final dashboard build for the mobile app, it took only about 20 minutes to build and deploy:

img_20160920_113941

Dashboard for the Mobile Application

and the final detailed architecture of the application:

Detailed Architecture

Detailed Architecture


Next on my Cloud program was ‘Development Operations in the Cloud: A Use Case and Best Practices‘ by Greg Stachnick and Jeff Stephenson. They talked about best practices using the Cloud Services to develop applications from the modern DevOps point of view.

img_20160920_123652

Modern DevOps

The case study was about the development of the Developers Cloud Service itself, neat!

img_20160920_124923

Developers Cloud Service Outline

This is a big project which is running completely in the cloud. Here is an image that shows a code review screen (sorry for the poor quality)

img_20160920_125529

Code Review

After accepting the changes the changes are pushed back to the mail line, triggering the next integration cycle in the continuous integration system. The typical cloud developers life is

IMG_20160920_125920.jpg

Day  in the Life of a Devloper

and the day of a manager

to summarize these points

img_20160920_131605

Summary

This summary hit the nail on the head. I’ve bin a contractor in many projects, always asking for more machines or more power. I would be happy if I could spin up another machine to do some testing instead of waiting for some other things to finish using the machine I wait for.


Before my day is over there are two sessions about ADF and JDeveloper to attend. First was Shay Schmeltzer with ‘Oracle Application Development Framework and Oracle JDeveloper: What’s New‘ which reveals what’s coming up in the world of ADF and JDeveloper. Shay started with the short history of ADF and JDev

img_20160920_160536

which is even longer if you count JBO to it too, which started 1999. Impressive. The session was more about features which are new in JDev 12.2.1 and JDev 12.2.1.1, both versions are out quite some time.  So, nothing new for seasoned ADF developers at the beginning.

Not so well known are ADF Business Components Triggers which are more known by Forms developers. They allow to do things right before or after some DB events fires.

IMG_20160920_161311.jpg

ADF BC REST Services and REST DataControl are better known if you work in the cloud or with mobile applications:

Remote Regions where introduced with JDev 12.2.1 but needed a patch to make them run (fixed in 12.2.1.1):

img_20160920_162051

Remote Task Flows:

img_20160920_162302

UI stuff like responsive support through templates (Tablet First), Massonry Layout and matchmediaqueries:

IMG_20160920_162555.jpg

Lots of new and changed data visualization components:

and finally to sum things up, other enhancements behind the scenes:

IMG_20160920_163029.jpg

For the future we can expect more and easier support for REST services and writing Groovy code. The biggest change will be the integration of JET Composite Components into ADF pages. JET Composite Components are an equivalent to ADF Declarative Components. You can build components from using other components, add properties to them to influence their behavior. Composite Components fire events which you can use to interact. Not sure how this will work, other that in the end you have HTML. Bad thing is that there is not even a time frame for this. More details in hte next section.

Anyway, ADF is not dead! There will be future development and enhancements in JDeveloper and ADF.


Final session for this long day ‘Oracle Development Tools and Frameworks: Which One Is Right for You?‘ by Shay Shmeltzer (again) and Denis Tyrell. As some of the features are not available at the moment the ‘Safe Harbor’ statement comes to play. So if you see something which you don’t find in the available version, you have to patiantly wait for it. No time frame given 😦

Shay summarized the different frameworks ADF, MAF, JET and ABCS and pointed out their key features. As the frameworks are well known I spare most details. As promised I give more detail about the Oracle JET Composite Components.

Sample JET Composite Components

Sample JET Composite Components

Key features of JET Composite Components and there basic structure is shown below

(Coming soon!) The composite components end up together in a Tenant Component Catalog where the components can be filtered by their characteristics

 

Which late will be extended so that components are available from different channels

img_20160920_183844

In the end there will be Project Visual Code provides a low code environment

Project Visual Code

Project Visual Code

After this deep dive into JET Composite Components I present the summary of the session which shows which development framework is used for which development

At the end of the session Shay and Denis answered some question which are noteworthy. I Cant remember all question but tried to summarize the key points from the answers:

  1. Oracle focuses on JET as the future development environment Future focus on jet. Why? ADF is already feature rich and the developer don’t ask for much more.
  2. Developers want more client side development. Demand on server generated UI is going to decline.
  3. JET will get offline capabilities! This can’t be done easily with ADF.
  4. JET allows faster exchange of libraries. JavaScript developers tend to rewrite their UI faster then ADF developers (see yesterdays summary where Geertjan Wielenga made the same point).
  5.  Public Component Catalog is only public to a point. You have to submit components which then will be vetted by someone before other users can use them.
  6. Cloud IDE (writing code in the cloud) will have JavaScript capabilities
  7. ABCS (Application Builder Cloud Service) is not available on premise right now
  8. For declarative JET development look at ABCS. ABCS allows to get the underlying JET code (save as) so you can look at the code and change it, e.g. to use it elsewhere.

DOAG DevCamp2016: Oracle Development Cloud Service Hands On (Part 2)

In part 1 of this series we talked about the Oracle Development Cloud Service (DCS) in general terms and what we plan to do. This part describes the migration of an application developed for an earlier version of JDeveloper to version 12.1.3 and how to move it into the cloud.

As a test case we use the sample application provided by the Rapid Development Kit which shows a sample on how to easily develop modern, scalable applications using the Alta UI. The image below shows the landing page of the application with the splash screen. The running application can be seen at http://140.86.8.75/AppsCloudUIKit/faces/Welcome

In Part 1 we already downloaded the source of the application, created the DCS project, assigned users to the project and initialized the GIT repository for the application in the DCS. The next step is to migrate the application which was designed using JDeveloper 11.1.1.9.0 to JDeveloper version 12.1.3 which we use in the DCS.

Before we start we checkout a new branch named ‘develop’ from the GTI repository. This allows us to work outside the ‘master’ branch. When we finished the migration we can merge the changes back to the master. This resembles the GIT Flow pattern (see ‘The Git Experience (Part 4)‘).

Migrating is as simple as to open the project in your local JDeveloper 12.1.3 and let JDeveloper do an automatic migration. There are some things which have to be changed in the sources as JDeveloper can’t do them automatically.

  1. We check the libraries used in each of the projects of the AppsCloudUIKit workspace. Make sure that there are no red marked libraries as this would mean that the library is not available in the current defined libraries. If we see one of those (e.g. JSF1.2 which is JSF2.1 in 12.1.3) we need to find an equivalent library for 12.1.3 and choose this instead.
  2. We compile each project and correct any errors we find in the compile window. There are some warnings which we let go for the moment. They tell us that the UI uses some tags or components which have been deprecated in JDeveloper 12.1.3. The components are still available but we should exchange them with the new components in the future. When we compile the projects we have to follow a specific order, the dependency of the project. There is a common project ‘UIKitCommon’ which is used in all other projects. This project holds the foundation of the application. Once the project compiles we have to create an adfLibrary from it which is used in the other projects. For this we right click on the project and select ‘Deploy’->’adflibUIKitCommon…’ and follow the instructions.
  3. We need to setup the data used for the application. The application doesn’t use a DB in this version. All data is created and served via POJO Java classes. All of them reside in the ‘DemoData’ project. We compile this project and create an ADF library from it like we did for the ‘UIKitCommon’ project.
  4. We compile and deploy (to adfLibrary) the other projects in this order: ‘DemoCRM’, ‘DemoHCM’, ‘DemoFIN’ and finally ‘DemoMaster’. The ‘DemoMaster’ project create an EAR File which can be deployed to a standalone server.

After this we can run the application in our local server integrated in JDeveloper and see if it works (see the image above). Once this is verified we save all changes in the GIT repository and push them to the cloud based remote GIT Repository. This is like working with any other remote GIT repository, no difference in usage. After this the landing page of the DCS project shows the trail of work as in the image below.

Using the collaboration features

One really nice thing about the DCS is the integrated collaboration features like a wiki page, an issue tracker like Jira and an agile board where we can plan sprints to track the progress of the project.

We create a wiki page to collect all decisions made during development and generating documentation this way. This will help members to understand the project and how they are supposed to work with the project. New members added to the project at a later point in time can use this wiki to understand the project and how to work with the team.

The image below show the start wiki page of the project

and add some basic information about the project. Later we add more info about who we changed the project and how to setup the build system.

The wiki supports cascading pages too. We add a page describing the build system to the project. This allows other team members to efficiently use the build system on the DCS. We talk about details of the build system and how to use it in the next part.

Agile Development

The DCS supports agile development. The tab ‘Agile’ opens a sprint planning view to the project. This is a very neat feature. Teams can use this to plan their tasks and track their progress. Here we can create issues (tasks, feature or issues) which first end up in the bag log. We can create sprints and assign the issues to sprints.

We can work like in e.g. Jira, we drag issues from the backlog to the sprint

to add the issue to the sprint

If you like you can change the agile board, e.g. add progress states

Finally we can start the sprint by defining the start and end date. Once a sprint is started we can look at the active sprint to see the tasks in their different states. This view allows drag & drop to make it easy to change the status of a task.

Once all tasks are finished we can complete the sprint.

A look at the ‘Issues’ tab shows the finished work.

All this works out of the box. As a teaser I added a couple of images from the DCS team feature when they are integrated in JDeveloper 12.2.1

When the DCS supports JDeveloper 12.2.1 the integration to the agile board and issue tracker is as simple as logging into the DCS. No hassle setting up a team server and all other needed software and their adapters.

This concludes the second part of the series. The next part reveals details about the build system.

Developer Cloud Service: Continuous Integration with JDeveloper 12.2.1

The last blog showed that the Oracle Developer Cloud Service is now available for JDeveloper and ADF 12.2.1 (Developer Cloud Service with JDeveloper 12.2.1 available). The missing part is the connection of the DCS to the newly created JCS for version 12.2.1. This we show in the blog.

The ground work, how to set up a build system for the DCS has been shown in Fasten your seat belts: Flying the Oracle Development Cloud Service (3 – Take Off – ROTATE). We now have to find out which environment variable to use for the 12.2.1 installation. At the time I wrote the mentioned blog there where only environment variables for 11.1.1.7.1 and 12.1.3.0 available. Looking at the documentation Using Hudson Environment Variables we find that the variables

  • ORACLE_HOME_SOA_12_2_1=/opt/Oracle/MiddlewareSOA_12.2.1/jdeveloper
  • MIDDLEWARE_HOME_SOA_12_2_1=/opt/Oracle/MiddlewareSOA_12.2.1
  • WLS_HOME_SOA_12_2_1=/opt/Oracle/MiddlewareSOA_12.2.1/wlserver

Are the right ones (and the only ones which point to 12.2.1). In the application.properties file (from the ‘… Take Off…’ blog) we exchange

# Don't change anything below!
 oracle.home=${env.ORACLE_HOME_12C3}
oracle.commons=${env.MIDDLEWARE_HOME_12C3}/oracle_common
middleware.home=${env.MIDDLEWARE_HOME_12C3}
install.dir=${env.ORACLE_HOME_12C3} 

with

# Don't change anything below!
oracle.home=${env.ORACLE_HOME_SOA_12_2_1}
oracle.commons=${env.MIDDLEWARE_HOME_SOA_12_2_1}/oracle_common
middleware.home=${env.MIDDLEWARE_HOME_SOA_12_2_1}
install.dir=${env.ORACLE_HOME_SOA_12_2_1} 

This change will use the JDeveloper 12.2.1 to run ojdeploy and configure the application to run on a WebLogic Server 12.2.1. This should do the trick and we can use the DCS build system to create application using ADF 12.2.1. As the application I used for the ‘Fasten your seat belts…’ blog series was pretty simple I like to show the result using the application I used for a presentation at the DOAG DevCamp2016, named AppsClouUIKit. You can read all about this application in a blog I wrote here DOAG DevCamp2016.

The application was build using JDeveloper 11.1.1.9.0 and has been migrated during the DevCamp to 12.1.3. This was the DCS version which was available at the time of the DevCamp. The first task is to migrate the source to 12.2.1 by creating a new branch in the GIT repository for the new 12.2.1 version.

We Clone the repository and create a new branch 12_2_1 which we use to build the AppsCloudUIKit for 12.2.1. As we are now running JDeveloper 12.2.1 we can use the Team-Server to get the sources from the DCS GIT repository

But we can use any other GIT client to get it. As this is covers in other blogs I’ll skip the details here. In the end we have this branch tree

Where the green marked local branch 12_2_1 is the one we are working on.

After changing the application.properies as shown above we can run the build using ant on the local machine

By selecting the ‘deploy’ target.

The result is an EAR file in the deploy folder

Setting up the build job

Let’s check-in the changes and setup the build in the DCS. Here are the steps for the build job

With this we can build the application to get the result

Setting up the Deployment

The final task is to set up the deploy task to deploy the application on the JCS_12_2_1. When we select the ‘Deploy’ tab we see the existing deployment configuration for the 12.1.3 JCS.

For the JCS 12.2.1 we created a new JCS instance with a different IP (public). Before we can create a new configuration for the 12.2.1 JCS instance we have to allow the Hudson user access to the JCS. This process is described in detail at Deploying an Application from Oracle Developer Cloud Service to Oracle Java Cloud Service

It’s absolutely necessary to get the Oracle Developer Cloud Service SSH public key and add this key to the JSC 12.2.1 instance as authorized key. Please follow the instructions given in the link above to do so.

After this is done we can create a new deployment configuration

Start filling in the dialog by giving the configuration a name. Next we create a new ‘Deployment Target’

In the dialog fill in the public IP address from the new JC 12.2.1 and select SSH Tunnel. The user name and password is the one you selected when you created the JCD instance. Test the connection and close the dialog by clicking ‘Use Connection’

Finally we can complete the Deployment dialog

We choose ‘On Demand’ here which let us specify which job/Build and artifact to use. A click to ‘Save and Deploy’ closes the dialog and the artifact will be deployed to the JCS 12.2.1. The URL to open the application is AppsCloudUIKit 12.2.1

And we should see

Developer Cloud Service with JDeveloper 12.2.1 available

I almost missed that Developer Cloud Service has been updated to 12.2.1. Great news as we now can use JDeveloper 12.2.1 to access the agile capabilities like

  • Interact with Tasks/Issues in JDeveloper
  • Leverage the Team view in JDeveloper (tasks, builds, and code repositories)
  • Connect to DevCS and its projects from inside JDeveloper
  • Create Agile boards and manage sprints in Developer Cloud Service
  • Associate code commits with specific tasks
  • Monitor team activity in the Team Dashboard
  • Handle Git transactions

For more information about how JDeveloper and the DCS are integrated watch this video ‘Agile development with Oracle JDeveloper and Oracle Developer Cloud Service’.

This was possible since last year. So, what’s new?

New is that the JCS is also available in 12.2.1 and that we can use the whole continuous integration scenario. For this we have to configure a 12.2.1 JCS instance which then can be used for deployment. When we select to create a new instance of a JCS we see the new wizard which allows us to select a WebLogic Server 12c in version 12.2.1

On the ‘Edition’ page we don’t find anything new so we skip it and go to the Details page where we specify the needed information for the service, database configuration, backup and the WebLogic user

After getting the confirmation page we create the new service and finally after a short time we see the new service

A look at the Enterprise Manager of the new service shows the new login page

and after logging in the new 12.2.1 Enterprise Manager

It look modern and fresh. However, this is not what this blog is about. I installed my ADF Version Web Service BlogAdfVersionWS to check which ADF version is running in this instance. Selection the modules we find the test point on the right side of the Web Service

After selecting the test point we select to run the ‘GetVersion’ service

and get

That’s right what we expect when running ADF 12.2.1!

Next time we see how to change the build and deployment part of the DCS to work with the JCS 12.2.1.

Naviagting an af:table in pagination mode from a bean

A question on the JDeveloper and ADF OTN forum asked about how to navigate to a specific page of an af:table in pagination mode. As of JDeveloper 11.1.1.7.0 adf tables can be rendered in scroll mode or in pagination mode where only a specific number of rows are visible in the table.

af:table in pagination mode

To navigate the pages there is a small navigation toolbar below the table which allows to enter a page number or to navigate to the previous, next, first or last page.

The problem to solve is how to navigate the paginated table from within a java bean?

The table doesn’t offer any navigation listeners or methods you can bind bean methods to. Luckily there is the RangeChangeEvent one of the FacesEvents which can e used to notify a component that change in the range has taken place.

All we have to do to navigate the table in pagination mode is to calculate the needed parameters

  • oldStart: The previous start of this UIComponent’s selected range, inclusive
  • oldEnd: The previous end of this UIComponent’s selected range, exclusive
  • newStart: The new start of this UIComponent’s selected range, inclusive
  • newEnd: The new end of this UIComponent’s selected range, exclusive

We add an input field to the page which allow us to enter a page number and a button which we use to call an action listener in a bean.

The running application looks like

Running application

Another button is used to calculate the index of the selected row in the whole rowset, the index on the page and the page number. The row index and the index of the row on the page are zero based, page numbers start with 1. Let’s look at the code:

public void onGotoPage(ActionEvent actionEvent) {
BindingContainer bindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry();
// get number of page to goto
AttributeBinding attr = (AttributeBinding) bindingContainer.getControlBinding("gotopage1");
Integer newPage = (Integer) attr.getInputValue();
if (newPage == null) {
return;
}
// page one starts at index 0 so subtract 1 from the pagen number
newPage--;
DCIteratorBinding iter = (DCIteratorBinding) bindingContainer.get("EmployeesView1Iterator");
// calculate the old and new rages for the RangeChangeEvent
int range = iter.getRangeSize(); // note both the table and we take the page size from the iterator's RangeSize
int oldStart = iter.getRangeStart();
int oldEnd = oldStart + range;
int newStart = newPage * range;
int newEnd = newStart + range;
// find the table
UIViewRoot iViewRoot = FacesContext.getCurrentInstance().getViewRoot();
UIComponent table = iViewRoot.findComponent("t1");
// build the event and fire it
RangeChangeEvent event = new RangeChangeEvent(table, oldStart, oldEnd, newStart, newEnd);
((RichTable)table).broadcast(event);
// update the table
AdfFacesContext.getCurrentInstance().addPartialTarget(table);
}

Line 2-8 we get the new page number we want to navigate to. Line 9-10 we subtract 1 from the given number as the page is zero based internally. In Line 11 we get the iterator which we need to get the range size and the start of the current range (lines 13-15). These values are oldStart and oldEnd. Lines 16-17 we calculate the new start range as page to go multiplied with the range. The newEnd parameter is the newStart pus the range size.
In lines 18-20 we get to the table component on the page. Then we create the RangeChangeEvent and broadcast the event to the table component in lines 21-23. Finally we ppr the table to see the change in the UI.

To show how to calculate the other way around, to get from the selected row in a table to the index on the page, the page number and the index in the rowset we added another button ‘GetPageOfSelectedRow’which calls a listener in the same bean which builds a string with the needed information.

public void onGetCurrentPage(ActionEvent actionEvent) {
BindingContainer bindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding iter = (DCIteratorBinding) bindingContainer.get("EmployeesView1Iterator");
// calculate index and page number. Index is zero based!
int currentRowIndex = iter.getRowSetIterator().getCurrentRowIndex();
_logger.info("CurrentRowIndex: " + currentRowIndex);
int currentPage = currentRowIndex / iter.getRangeSize();
currentPage++;
_logger.info("Current Page:" + currentPage);
int indexOnPage = (currentRowIndex % iter.getRangeSize());
_logger.info("Current index on Page:" + indexOnPage);
// get an ADF attributevalue from the ADF page definitions
AttributeBinding attr = (AttributeBinding) bindingContainer.getControlBinding("selectedRow1");
StringBuffer sb = new StringBuffer();
sb.append("row index overall: ");
sb.append(currentRowIndex);
sb.append(" row index on page: ");
sb.append(indexOnPage);
sb.append(" Page: ");
sb.append(currentPage);
attr.setInputValue(sb.toString());
}

To get the index of the selected row in the whole rowset we need the iterator and get the RowSetIterator from it. The rowSetIterator method getCurrentRowIndex() returns the index of the current row (line 5). The current page is calculated by dividing the current index through the range size (line 7). The final information is the index of the selected row on the page which is calculated as the current index modulo the range size (line 10). The rest of the listener build a string out of this information and writes it to a pageDef variable which is referenced in an outputfield on the page.

<af:outputText value="#{bindings.selectedRow1.inputValue}" id="ot8" partialTriggers="b2"/>

Here are some images from the sample application.

The sample application is build using JDev 12.1.3 and uses the HR DB schema. The sample can be downloaded from  Github