JDev 12.2.1.3: Creating a shared skin jar (Part 2)

In part 1 or the series we created a simple skin, built an ADF-Library from it and tried to reuse it by deploying it to a WebLogic Server. This approach failed. In this part, we try another option to share a jar with

Sharing the skin with other applications

To share the skin with other application we can

  1. Create an ADF Library
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server
  2. Create a normal jar
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server

For this blog the way we want to use if 2b. This allows to create the skin once, deploy it to a server and use it in every other application. In the next paragraph, we try out option 1a to show the problems when reading resources from a jar file.

Using a shared skin

Option 2b

Here we create a jar file containing the skin and additional resources like images and deploy it directly to a WebLogic Server as a shared library. The advantage is, that other applications can use the skin and other resources directly and that the jar can be versioned to allow different versions of the same jar on the server.

Option 1a which we discussed in the previous chapter doesn’t work for images. However, the documentation ‘Deploying a Custom Skin File in a JAR File’ and Frank Nimphius pointed it out in e.g. 86. Reading boilerplate images and icons from a JAR or How-to share skin definition files across applications how the jar file must be structured to allow the resource servlet to read the resources. The essential sentence is

‘All image resources and CSS files must also be under the META-INF directory.’

In his article, Frank suggested using the command line jar tool to create the jar. I show how to use JDev to create the jar with the needed structure and how to deploy it to a server as a shared library.

A sample application is used to use the skin and to show an image load from the jar.

The building plan for a skin in a shared Library which can be deployed to a WebLogic server is given in the article as:

To implement the shared library approach, developers need to change their existing skin definition so it can be deployed in a JAR file. The steps for this include

– Creating a META-INF directory – Creating a trinidad-skins.xml file that defines the skins deployed with the JAR file

– Creating an META-INF/adf sub directory for images and icons served from the JAR file

– Changing the image reference in the CSS to include the “adf” directory, which makes sure images and icons are handled by the ADF Faces resource loader, which can read resources from JAR files

– JAR the META-INF directory to create the library file

Looking at the current project for the skin we see a different layout

In the article Frank instructed to create the needed folders yourself and copying or moving the files to the new structure, then to use the command line to build a jar from the structure.

I’ll show how this can be done with a special deployment descriptor from within the project. The image below shows the needed layout of the final jar file.

To transform the folder structure present in JDev to the needed structure of the final jar, we create a new deployment descriptor in JDev

In image 4 we see the first part of the solution: here we set the path inside the jar to ‘MEAT-INF’. This will guarantee the structure we need. Then we add another contributor to the list (public_html) to get everything we need into this folder. Then we use the ‘Filters’ node to select all content we need skin part

Next part is to create another path in the jar for the metadata of the skin

We add another file group for the resources

Now we can deploy the jar using the new deployment descriptor

And the jar file is created in the deploy folder. It holds all files in the right folders

Finally, we can deploy this jar to the WebLogicServer. In this case, I use the integrated WLS, but it can be any stand-alone WLS too.

The error message you see on the 7th image can be ignored. It only tells you that the library can’t be deployed as an application but only as a shared library. This is exactly what we want to do 🙂

Now the jar file is deployed on the WLS as a shared library and can be used for every application on this server.

We use the existing application from part one to consume the jar skin from the shared library and show the images deployed with the jar.

Before we go any further, we have to remove the ADF Library we added to show the problem from the project. For this open the project properties and select the ‘Library/Classpath’ node and remove the ‘ADFLibrary’ entry

The page should now look like no skin is used at all.

As we already added a skin (with the ADF Library) we don’t have to do this again. However, we have to add a library reference to configure the application to use the shared library deployed on the server. For this, we open the application descriptors and edit the ‘weblogic-application.xml’ file by double clicking the entry in the application resources section

In the ‘Shared Library Reference’ section, we add a reference to the now deployed shared jar ‘blogsharedskin’

Saving everything we don’t see any change to the page design, as the library isn’t part of the application yet. Starting the application we get

Just what we liked to see. The images are visible, checking the page with Chrome’s Dev Tools shows that the images are correctly loaded

This proves that the shared library with the skin and the images are working correctly.

To make the skin visible in JDev during development, we can add the jar we developed to the server a library. We create a library

and make sure the ‘Deploy by default’ is NOT set. Adding the library to the project

will make the skin visible in design mode

The unset checkmark prevents the jar from being packed into the WAR or EAR file. It’s just used in the IDE. That you can’t see the images is normal as there is no full server to serve the images to the design view.

Summary

In this mini-series, I showed the problem when creating a skin as ADF Library and trying to share it on a Weblogic Server. Then I showed how to create a deployment descriptor for the skin and other resources and how to deploy the resulting jar to a WebLogic Server.

The sample application can be downloaded from BlogSharedSkin. The sample was created by using JDev 12.2.1.3 but the same technique can be used in any 12.2.1.x JDev version. There is no database connection needed.

Advertisements

JDev 12.2.1.3: Creating a shared skin jar (Part 1)

In earlier versions of JDev, skins have been created either by pure code or by using the free Skin Editor. However, since JDev 12.2.1.x the skin editor has been integrated into JDeveloper itself.

A couple of questions in the ODC JDeveloper space are about how to create a skin with JDev which can be deployed as a shared library to a WebLogic Server. I gave this a try and it turned out, that you can build an ADF library jar from a skin project but you can’t use images to this jar which you might want to use in the application.

In this blog, I’ll show how to create a skin with resources like images and how to build a jar file from the skin together with the images and deploy it aa s shared jar to a WeblogicServer.

Building a skin project

The first part is to build a small skin project. The project we use to create a minimal skin, just to show that the skin is changing something. Then we add some images to the skin which we want to use in the application which uses the skin. Such images can e.g. used on an af:button component.

We start by creating a new application as an ‘ADF Fusion Web Application’

As we don’t need the created model project we delete it completely

If you get another dialog, telling you that you can’t undo the action, answer ‘Yes’ to delete the project. Now you should see a workspace with just the one project:

Know that we have a project we add a skin and e.g. add some skin selectors to change to the color of the button text. For this, we right click the ‘Web Content’ folder in the project and select ‘New from Garaly’ and then select ‘ADF Skin’ from the ‘JSF/Facelets’ node and fill in the basic information:

This will create the needed css file and the descriptors which define our skin (trinidad-config.xml and trinidad-skins.xml).

We open the sharedskin.css file if it’s not open already and switch to source mode. Here we add two simple skin selectors

which are changing the color of the text of a button and a link. You can add more sophisticated selectors but for this blog, it’s enough to show the working skin. To make it more interesting, and because that’s the real reason for this blog, we add some images to the skin which we like to use in the application using the skin. We add the images into a new folder like shown below

The reason for this structure is, that to read the images from the jar in the consuming application, we need a special resource loader. In case of ADF it’s the resource servlet which listens to the URL pattern ‘/adf/’’. This servlet is installed automatically for ADF Web Applications and is configured in the web.xml file

The final task for the skin project is to create a jar file which we can use in other applications. The easiest way to get such a jar is to create an ADF Library deployment descriptor. Open the project properties of the skin project and select the ‘Deployment’ node

And click the ‘New Profile’ icon, select to create an ADF Library Jar

and click ‘OK’. The remaining dialogs you can just click ‘OK’ or ‘Finish’.

To create the library we have to execute the descriptor by right-clicking on the project and selecting ‘Deploy’ and choosing the ‘sharedskinadflib’

This will create the jar in the ‘deploy’ folder of the project.

Sharing the skin with other applications

To share the skin with other application we can

  1. Create an ADF Library
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server
  2. Create a normal jar
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server

For this blog, we want to use option 2b. This allows to create the skin once, deploy it to a server and use it in every other application. In the next paragraph, we try out option 1a to show the problems when reading resources from a jar file.

Using a shared skin

Option 1a

We start with option 1a, just to show the problem when we try to read a resource from a jar. We build another ADF Fusion Web application and add the skin as ADF library from a ‘File System Connection’ which we create and let it point to the ‘deploy’ folder

Right-click on the ‘sharedskinadflib’ and add it to the new sample project. This will make the skin available to the application. To use the skin we have to add a skin to the application like we did to create the skin project. The difference is that we now choose the shared skin as the base skin

Creating a new page and adding a button and/or a link to the page we see the new style introduced by the ‘sharedskin’

So, the shared skin is working. Well, yes, but what about the images we added to the ‘sharedskin’?

Let’s try to add one to the button. In the property editor, we select the icon property of the button and click ‘Edit’ to get

However, we don’t see any image in the whole project. As we know where we put the images (or we can look into the sharedskinadflib) we can just add the path to the image like ‘skins/sharedskin/adf/images/home.png’ and we see the image

Running the application we get the page with the button but don’t see the image

Using DeveloperTools we see that the resource couldn’t be found. Inspecting the button element we see

The path to the image is not found 😦

If we change the address of the image to ‘/adf/images/home.png’ to use the resource servlet we still get an error

The reason is that the resource servlet expects the resources in a different path inside the jar. Every resource which should be read from a jar should be in a folder named ‘META-INF’.

The ADF library did not put the images into the META-INF folder

The problem is that we can’t change the layout of the ADF Library. When you create an ADF Library there is no option to make any changes to the content of the jar.

The conclusion is that using a skin in an ADF Library is problematic if there are other resources which you need to share.

To be continued…

In the final part 2 of the series, we see how the skin can be shared with other applications.

JDeveloper: Info about the clicked cell in an af:table

JDeveloper allows to easily create tables with the af:table component. The table allows easy access to the selected row or rows. However, if you are interested in which cell of a table has been clicked, ADF needs some tweaking. This blog is about how to tweak an af:table to get exactly this info.

Use Case

You like to know which cell in an af:table a user has clicked, e.g. to get some detailed information about the clicked item or cell in the selected row. The sample I show get the information about the current row, and column of the cell and the value of the cell clicked. The final sample will show the info like

How to do it?

The normal af:table component doesn’t give information about the cell a user has clicked on. The ADF pivot table offers this but is complex to use.

We use JavaScript in form of a clientListener to intercept the click on a cell and a serverListener to call a bean method to get more data on the cell. This article 011. ADF Faces RC – How-to use the Client and Server Listener Component shows how to use clientListener and serverListener in detail.

As we are interested in the selected cell, we add a clientListerer to each af:outputText which shows the column value in the af:table which fires on the click event. The clientListener calls a JavaScript method. In the JavaScript method, we build a payload of the UIComponent which is used to show the column value and the column name of the cell. To get this information we have several possible ways:

  1. We can use our knowledge of the DOM tree and get the column via the parent of the component which fired the event. The parent component should be the af:column.
  2. We add a client attribute to the component which shows the cell value adding the column name from the af:column as EL

In this sample, we choose the second solution. With this information, we call the serverListener from the JavaScript method. The serverListener method is implemented in a request scope bean and uses the information passed to get the details about the clicked cell we show in the UI.

Implementation

The sample uses the HR DB schema and only needs one table, Employees in this case. We create a simple page with the table in read-only mode, sortable and filterable. As you see in the image above the table is just build be dragging the Employees VO onto the page and drop it as a read-only table.

Now we add a clientListener and a serverListener to each outputText component which is used to show the cell value

In the image above we see the listener for two columns. In addition, we add an af:clientAttribute with the name ‘columnName’ which we pass the EL of the af:column headerText property.

Next, we add an af:resource component to the af:document where we specify the JavaScript for the clientListener method ‘clientCellSelectionCall’. We use a JavaScript file to code the method. We could have added the method to the page directly, but if we want to reuse the pattern, it’s better to use a JavaScript file

The file is located in the public_html folder (Web Content) in a subdirectory ‘javascript’

The method code is

The click event on the af:outputText component triggers a call to the javascript method ‘clientCellSelectionCall’ (via the clientListener) with the source of the event, the af:outputText component. The method reads the clientAttribute added (line 3) and calls the serverListener of type ‘cellSelection’. This event is defined by the af:serverListener on the af:outputText. The component which triggered the event and the column name added as client attribute are passed to the serverListener.

The serverListener is a bean method defined in a request scope bean on the af:outputText component as

method="#{TableCellSelectionBean.handleTableCellSelection}"

In the bean, the method looks like

public void handleTableCellSelection(ClientEvent event) {
  // get payload which is the ui component which fired the event
  UIComponent ui = (UIComponent)event.getParameters().get("payload");
  // get the column from the event which is sent too
  String column = (String)event.getParameters().get("column");
  RichOutputText rt = (RichOutputText)ui;
  // get current row key
  DCBindingContainer bindingContainer = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
  DCIteratorBinding binding = bindingContainer.findIteratorBinding("EmployeesViewIterator");
  Row currentRow = binding.getCurrentRow();
  Key key = currentRow.getKey();
  // compile info about clicked cell
  String out = "Payload:" + ui + "
  column: "+ column + "
  val: " + rt.getValue() + "
  key: "+key.toString();
  logger.info(out);
  setCellInfo(out);
}

Here we get the component which triggered the event (as payload) and the name of the column. Using this information we can get e.g. to the value of the column (via the UI component). The row of the cell we get via the current row of the iterator. With this information, we get the key of the row. We can get much more information here, like historical data about the current employee’s salary, if the salary cell was clicked.

We just create a string from the information which we show in the UI to the user

Here are some images of different cells clicked in the UI:

Download

You can download the sample, which was built using JDeveloper 11.1.1.9, from GitHub BlogTableCellSelection. The sample uses the HR DB schema.

JDev 12c: Debug Application Module Tester (BC4JTester) Problems

When you develop ADF Web Application you often use the ADF ApplicationModule Tester (BC4J Tester) to quickly test your business components data model and your self-written code in any EntityObject, ViewObject or ApplicationModule. For more information about how to do this look at JDeveloper & ADF: Use the Application Module Tester (BC4J Tester) to Test all your BusinessLogic.

Users who use one of the latest JDeveloper versions 12.2.1.1.0 and newer may have noticed, that the BC4J Tester application starts without an error, but doesn’t show the dialog. I run into this a couple of times lately and decided to dig into this problem. On the Oracle Development Spaces, I saw some threads about this too.

The reason for this behavior is that any EO, VO or other methods in the application module have an error, which can’t be found during compile time.

Use case

To show the effect, we start with a simple Workspace and a model project which only has one ViewObject in the Application Module’s data model

We implement a small use case where we want to see the total salary of all rows retrieved by the query behind the VO. Without any added where clause we get the total salary of all employees. If we add a filter e.g. by DepartmentId=90 we only get the total salary of all employees of department 90. Here are some images of the final running model in the BC4J Tester

Implementation

OK, so how do implement this use case?

We do this by adding a transient attribute to the EmployeesView and use a SQL default expression to do the calculation

sum(Employee.SALARY) OVER (PARTITION BY NULL ORDER BY NULL)

In the image below we see the definition of the transient attribute in the ViewObject

Problem

This should do the trick. However, when we try to test this in the BC4J Tester we get

In the log window, but no dialog where we see the application module. We don’t get any hint about what went wrong. The tester is up and running, but we don’t see anything.

Shay Shmeltzer mentioned in one of the ODC threads, that the reason for this is that there is an error in the application module (ViewObject, EntityObject or AM method). As the only thing we added is the SQL statement for the transient attribute, it’s clear that the statement must have an error. It’s simply a missing ‘s’ character, as the DB table we use is named ‘Employees’ and not ‘Employee’. So the correct statement is

sum(Employees.SALARY) OVER (PARTITION BY NULL ORDER BY NULL)

This will solve this problem and the BC4J Tester will start up and show (see the images above). But what if we added more things to multiple objects?

How to find the error then?

Older versions of JDev, the BC4J Tester did show an error message which showed the error and made solving the problem easy. Here is an image of the same application running using JDev 12.1.3.0.0

Solution

I did not manage to get the same output using JDev 12.2.1.1.0 or newer, but you can get the same message in the message window.

For this, you need to start the BC4J Tester with the java option

-Djbo.debugoutput=console

The option is added in the model projects ‘Run/Debug’ option in the project’s properties

Whenever you start the BC4J Tester and don’t get any dialog, you can assume that there is an error in the application module. To find out what the problem is, add the java option to the model project and you get the detailed information in the log window.

Show Comma Separated String as Detailstamp in Table

In this blog I’ll show how to implement a very specific use case which was asked on the Developer Community (the renamed OTN).

Use case

A table has a comma-separated list of values which are FK for another data store which might be from a different table or WebService. So the list of value can’t directly be related to the master table as an association or view link.

Here is a sample look of such a table or view

The resulting table in the UI should show this data as table and in the detailStamp facet show the data for each number in the list like

Implementation prerequisites

The use case is implemented using JDeveloper 11.1.1.7.0 and uses the HR DB schema.

To make it easy we use the normal HR DB and create a view to show the department together with a comma-separated list of all employeeId, EmpList in the above table. For this we use the SQL code:

CREATE OR REPLACE FORCE VIEW "HR"."DEPT_EMPS" ("DEPT_ID", "DEPARTMENT_NAME", "MANAGER_ID", "LOCATION_ID", "EMP_LIST") AS 
 SELECT DEPT.DEPARTMENT_ID,
 DEPT.DEPARTMENT_NAME,
 DEPT.MANAGER_ID,
 DEPT.LOCATION_ID,
 cclist.EMP_LIST
 FROM DEPARTMENTS DEPT,
 
 ( SELECT EMP_LIST.DEPARTMENT_ID,
 LTRIM (
 MAX (SYS_CONNECT_BY_PATH (EMPLOYEE_ID, ', '))
 KEEP (DENSE_RANK LAST ORDER BY curr),
 ', ')
 AS EMP_LIST
 FROM (SELECT ccr.DEPARTMENT_ID,
 ccs.EMPLOYEE_ID EMPLOYEE_ID,
 ROW_NUMBER ()
 OVER (PARTITION BY ccr.DEPARTMENT_ID
 ORDER BY ccs.EMPLOYEE_ID)
 AS curr,
 ROW_NUMBER ()
 OVER (PARTITION BY ccr.DEPARTMENT_ID
 ORDER BY ccs.EMPLOYEE_ID)
 - 1
 AS prev
 FROM EMPLOYEES ccs, DEPARTMENTS ccr
 WHERE ccs.DEPARTMENT_ID = ccr.DEPARTMENT_ID) EMP_LIST
 GROUP BY EMP_LIST.DEPARTMENT_ID
 CONNECT BY prev = PRIOR curr
 AND EMP_LIST.DEPARTMENT_ID = PRIOR EMP_LIST.DEPARTMENT_ID
 START WITH curr = 1
 ORDER BY EMP_LIST.DEPARTMENT_ID) cclist
 WHERE DEPT.DEPARTMENT_ID = CCLIST.DEPARTMENT_ID;

And the data store for the employees is created as a DB view too:

 CREATE OR REPLACE FORCE VIEW "HR"."EMP_LOOKUP_VW" ("EMPLOYEE_ID", "NAME", "EMP_ID_CH") AS 
 SELECT emp.EMPLOYEE_ID,
 emp.FIRST_NAME || emp.LAST_NAME,
 TO_CHAR(emp.EMPLOYEE_ID)
 
 FROM EMPLOYEES emp;

There is no association or viewlink between the two views. This data model came together with the original test case I used to implement the use case.

Implementation

The implementation starts with generating a normal ADF Web Application. Details on how to do this you find at Why and how to write reproducible test cases.

In the model layer we add EO/VO for the two views we generated to end with the following data model

And the model project

How to implement the use case

Before we start to make changes, let us think about the solution. The problem is that we have a string with a list of values which we want to show as single elements in the detail stamp of a table. We need to split the string into pieces (the employeeId) and iterate over them in the detail stamp of the table.

To iterator over a data collection, we can use a CollectionModel or an ArrayList which JDev converts internally into a CollectionModel.

In this solution, we are adding the array if employee Ids to the DeptEmpsEO as a transient attribute. This will make the array available for each row as part of the row itself.

Step 1

We add a transient attribute to the DeptEmpsEO like

null

We set the type as Object as JDev 11.1.1.9 can handle SQL Array types only. In our case, we build an ArrayList from the string.

Next, we create the Java implementation class for the EO

In the generated class we exchange the getEmpArray() method with

 

The method gets the comma-separated list and splits it into an employeeId and adds them into an ArrayList of Numbers (oracle.jbo.domain.Number). The resulting arrayList is returned.

As we have added a new attribute to the EO we have to add this attribute to the VO too:

Step 2

As we later only have employee IDs, we need a method to look up more data for the employee. Remember, the data might be from different sources so there is no association or viewlink available we can use like in normal ADFbc applications. We have to build a lookup view for this (or is the data is from a WebService we use this WebService).

We use the second DB view build at the beginning from which we created the EO EmpLookupVw and the VO EmpLookupVwView.

The data this VO presents is very simple as it’s just the employeeId, the first name, and the last name.

In this VO we create a view criteria to lookup a single employee by its Id like

Which uses a bind variable of Integer type

To make this available in the DataControl, we add the VO as special Vo to the ApplicationModule

 

 

 

Now the VO named EmpLookupById always add the viewCriteria when executed. All we have to do is to add the integer parameter for the employeeID we are looking for. In the DataControl we can use the ‘ExecuteWithParam’ method from the VO and set the parameter

Step 3: ViewController

The test case comes with some pages in the ViewController project. However, we use a fresh jspx page to show the solution. We create a new page by dragging a ‘View’ component onto the adfc-config.xml (the main unbounded task flow) and create the file by double-clicking it. In the dialog, we select a ‘Quick Layout’

 

Once the page shows up in the design view we add an af:outputText for the caption and drag the DeptEmpsVO1 view object from the data control onto the center facet of the layout and select to create an ‘ADF Read-only Table’. We show all attributes (if you like you can deselect the EmpArray attribute).

This is the base of the work. Now we enable the ‘Detail Stamp’ facet of the table. In this facet we want to iterate over the comma-separated list of employees.

We add an af:panelGroupLayout into the facet with vertical layout. Inside the panelGroupLayout we add an af:iterator which we use to iterate over the array of employeeId we created in the row as attribute EmpArray. ADF is intelligent and converts the array into a collection. This collection can be used as other collection. We define a name for the variable we can use for each single element of the collection like ‘emp’. Using an af:outputText we can print out the value of the current element of the collection like

<af:iterator id="i1" value="#{row.EmpArray}" var="emp">
                    <af:panelGroupLayout id="pgl2" layout="horizontal">
                      <af:outputText value="- #{emp} -”/>
…

What’s missing is that we like to get more information than just the Id of the employee. So we add another EL to the af:outputText which points to a bean method which will retrieve this additional information. The final facet looks like

  <f:facet name="detailStamp">
 <af:panelGroupLayout id="pgl1" layout="vertical">
 <af:iterator id="i1" value="#{row.EmpArray}" var="emp">
 <af:panelGroupLayout id="pgl2" layout="horizontal">
 <af:outputText value="- #{emp} - #{viewScope.EmpBean.empNameById}" id="ot8"/>
 </af:panelGroupLayout>
 </af:iterator>
 </af:panelGroupLayout>
 </f:facet>

Or as an image:

Finally, we have to create the bean with the method to retrieve the additional data. We create a Java class in the view folder

 

 

 

End add this bean class as managed bean to the adfc-config.xml

Before we go into writing the method to retrieve the data, we have to add the EmpLooupById.ExecuteWithParams method to the pageDef to make it available in the page. The easiest way to do this is to open the EmpLookupById Vo in the Data Control and open the ‘Operations’ node. Select the ExecuteWithParams method and drag it onto the page. We can drop it anywhere as ‘ADF Button’

 

 

 

In the final dialog, we add the value of the parameter as ‘#{emp}’ which is the EL to access the current employeeId when we iterate the array.

This will create the needed pageDef entries. As we don’t need the button, we switch to source mode and delete the button from the page. This will keep the pageDef entries.

Now we open the bean class and write the method to get the additional data for an employee as

Lines 25-38 we get the method binding from the pageDef.

Line 39 retrieves the current value of the employeeId by evaluating the EL ‘#{emp}’ by calling

Lines 40-42 set the value (the employeeId) as parameter to the methods bindId parameter

Lines 43-48 execute the method and check for any errors. If there is an error, the stack trace is printed into the log and the method return “Not found!”.

Lines 50-56 if the call the ExecuteWithParams method returns without an error, the current row of the EmpLookupById VO points to the employee we are looking for. We get the iterator from the pagedef and from the currentRow we read the ‘Name’ attribute. The value of this attribute we return and it will be printed in the af:outputText in the page.

Running the page now will produce

You can download the test case from GitHub BlogCommaSeparatedListDetail. It uses the HR DB schema with some additional DB views created. The scripts are part of the workspace. The application is developed using JDev 11.1.1.7.0.

Query and Filter an af:listView

Most of the time we use tables to show tabular data to users. However, JDev and ADF allow for other components like the af:listView to be used to show such data to the user in a more modern way.

The image above shows the normal display of data when an af:query is used together with a table to show the result.

A more fancy, modern look we get if we use a af:listView to show the results as this allows us to style the data

Use case 1

We like to use an af:query to search for employees and show the result in a styled af:listView.

Implementation 1

This is pretty easy as we only have to use an af:listView as the result component of the af:query

And to exchange the af:table with an af:listView. Or you build the page by first dropping an af:query onto the page (without table) and then add the af:listView

Then you get the wizard to layout the list

This will give you a basic layout which can be styles in JDev as

The final result is

which looks more modern. One thing the af:table give you out of the box is the second use case.

User Case 2

We like the af:listView to be able to be filter the result like the af:table can.

Implementation of second use case

Easy you think? Well, the af:listView component doesn’t provide any filter out of the box. There isn’t even a filterModel like there is for an af:table.

So, how do we get this implemented. The idea is to use a af:table component but only use the filter provided by the af:table. The remaining parts like table data, possible scroll bars and status bar or scrollbars we remove.

We start by dragging the EmployeesView1 from the data control onto the page again.

And drop it after the closing af:panelHeader and before the af:listView as ‘ADF Table’

In the image you see that I have removed some available columns. Before we go to hide the part of the table we don’t need, we make the table work together with the af:query and the af:listView. When we use the af:query the table shows the right detail (auto PPR triggers the refresh of the table). However, if you have queried for the ‘Purchasing’ department and then enter an ‘s’ into the ‘First Name’ filter field of the table and hit enter, you get

As you see, the table shows the right result (2 rows) but the listView still shows all employees of the Purchasing department.

To make it work, we need to add a partialTrigger to the listView which points to the table. This way each time the table changes the listView will too.

Save all changes and refresh the page. Now if you enter a value into a filter field and hit enter, the listView will update too.

After the page works we have to get rid of the data below the header of the table. This is easy to accomplish by styling the table. We only need the filter field and the header below the filter fields so that we know which field filters which data. Simply set the maxHeight of the table to the exact height of the the two components. You can use your browser’s developer tools (F12) to measure the height. In my sample it’s 65px. So, setting the tables inlinestyle to

max-height: 65px;

will hide everything below the filter and the header

If you like you can create a skin and create a style class and use this style class instead of setting the max-height directly to the inlineStyle of the table. A nice addon is that the table header sorting is working too for the listView.

You can download the sample from gitHub BlogFilterListView. The sample is build using JDev 12.2.1.3 and uses the HR DB schema. The principle can be used in other JDev versions too.

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.

Blog Using External REST Servies (Part 1)

Using External REST Services with JDeveloper 12.2.1.2 (Part 1)

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. Part 2 we will create the UI using the REST Data Control.

Setting up an external REST services

Let’s start be selecting a REST service which is available for public use without the need to get a key first. We use such a service to make it easier for you to run the sample and to look at the code. If we would need a key to use the API, you would need to register yourself with the service before you can run the sample.

There are a couple of such REST services like Spotify, iTunes or MusicBrainz which offer search APIs for music data as public REST service. Spotify we have to eliminate from the list as this service requires an API key since Mai 2017, meaning that it’s not public available without you register yourself before using it. ITunes REST API allows public access and the data structure returned is very simple. The result for a search get you everything in a flat structure. This will make things easy, too easy 🙂

For this sample where we like to show how to work with more complex data structures returned by a REST service. So, the final vote for this blog goes to MusicBrainz (MusicBrainz Rest API).

MusicBrainz

Musicbrainz REST API comes in different versions (V1 and V2). The current version v2 is what we are interested in as V1 is already deprecated. The documentation tell us, that the service is an XML style REST service. However, there is a JSON style REST service available too. This JSON style RSET service is what we use for the sample.

Before we implement the REST service calls we need to find out how to search for the data we like to show. For this a tool like Postman is a great help. Postman allows you to enter calls to REST services in a browser like UI. You can set all kind of headers, e.g. below we see a sample of the Postman UI (in the result a couple of sub structures are folded to show the relevant data). The query searched for recordings named ‘yesterday’ and asked for a result in JSON format:

To learn more about the possible searches refer to Web Service Search.The data structures and their meaning are described in in the MusicBrainz Data Structure.

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.

Creating a REST Web Service Project

After we looked at the REST Service and the data it returned we have identified the data we need to get from the REST service. The first step is to create a project which communicate with the REST service.

We create a normal Fusion Web Application which will create a ADF model project and a view controller project. If you need a script on how to do this you can look at Writing Reproducible Test Cases: Why and How. The model project we don’t need for this sample. You can delete it or just leave it empty.

For the access to the MusicBrainz data we create a new REST Web Service Project inside the application:

Name it and go through the rest of the wizard

Before we create the web service data control, we need to create a REST Connection from the resource pallete we create a new IDE connection of REST type

Then we later need data returned from the REST service which the wizard uses to produce the data structure. A simple way to get such data is to use e.g. Postman to call the REST service:

Copy the result (all of it!) and save it to a file. Next we create a new Web Service Data Control from the gallery

Select the ‘WebService Data Control (SOAP/REST)’

And fill out the wizard. Select the REST connection created before

In the next image click on the green ‘+’ sign

And change the path to ‘/ws/2/recording’, select JSON as data format and checkmark the GET method to enter ‘recording’ into the field.

In the next screen we need to select ‘Parse from Sample Code’ and copy the content of the file we saved from Postman into the textarea

And finally test the Web Service Data Control

The finish the wizard. Now we can test the data contron by finding the DataControl.dcx file in the project and right click on it. Choose ‘Run’ from the context menu:

In the dialog window right click hte data control and choose ‘Operations’

Fill in the fields and click the execute button

The result should look like

You can copy the return value into an editor to fully see it. If you don’t get the successful result, check the steps against the ones in the blog.

This concludes part 1 of this series. In part 2 we develop the UI for the application using the Web Service Data Control we created in this part. The source of the sample can be downloaded from GitHub. The link to it will be provided with part 2.

JDeveloper: How to setup and use a converter

JDeveloper: How to setup and use a converter

In this post I show how to setup the server side part of a converter and how to use it in an application. Converters can have a client side too and all af:converter do have one. For a nice sample on what you can do with client side converters see ADF: Smart Input Date Client Converter. The big difference is that the client side converter is done on the client side with JavaScript and no server round trip is done for the conversation.

Why are converters needed at all?

Sometimes the data you get from a source like the database table is not in a format you like to show to the user. Common cases are showing strings in special formatting, e.g. social security numbers or phone numbers. You can use converters to show the content of clob and blob columns in the UI too.

The ADF framework provided some converters out of the box:

These can be used without the need to program anything.

What is missing from the out of the box converters is one which can be used to format a string.

One thing to remember that the new format should only be used in the UI to show the data in a specific format. You normally don’t want to store it in this special format.

We create a converter which exchanges each uppercase character ‘B’ in a string with the string “-Z-”. The sample is not very useful, but it shows what can be done with converters.

Use Case

A string can contain any character. However when the string is shown on the UI there should be no ‘B’ visible. Instead of the ‘B’ we should show ‘-Z-’. This should only be done when the string is visible on the UI. When the string is stored in the db or some other place it should be stored with the ‘B’.

Implementation

I used JDev 11.1.1.7.0 for this sample, which is the oldest JDev version I have access to. The steps to create a converter should be almost equal in all versions, but I deliberately choose the oldest JDev I have so that other users with other version should have no problem migrating this sample to their version.

The final sample can be downloaded from GitHub at BlogConverterSample.

Model Project

We start by creating a fresh ADF Web Application. If you want a detailed description on how to do this, you can follow Writing Reproducible Test Cases: Why and How. For the model part I only use one DB table, the EMPLOYEES table. The resulting model project looks like

We don’t need to make any change to the generated project. This model project is only created to show that the converter works on data read from the DB table too.

ViewController Project

For users interested in more details about converters, please read the doc at http://docs.oracle.com/cd/E48682_01/web.1111/b31973/af_validate.htm#BABGIEDH. To start with the converter, we create a java class in the ViewController project and name it MyB2ZConverter.java. As package we choose ‘de.hahn.blog.convertersample.view.converter’

As the class will be a converter we have to implement the javax.faces.convert.Converter interface. For this you click on the green ‘+’ sign and can search for the right interface

This process will create the java class and two methods

These are the methods we have to implement for our use case. The first method ‘getAsObject’ is called when the data from the UI is send to the server for further processing. The ‘getAsString’ method is called when data from a storage (DB, bean property or pagedef variable) is going to be rendered to the UI.

As our use case is to exchange every “B” with the string “-Z-” we can implement the getAsString method easily by replacing every “B’ with “-Z-”. The method has three parameters, the current FacesContext which you can use to write messages, the UIComponent for which the converter is called and finally an Object representing the data which we want to convert. The result of the conversion must be a String. The resulting method look like

/** Method to get the string representation of hte object to use in the UI
* @param facesContext current facesContext
* @param uIComponent component which was used to deliver the data
* @param object data from storage to be converted
* @return sting to use for in the UI
*/
public String getAsString(FacesContext facesContext, UIComponent uIComponent, Object object) {
  if (object != null) {
    String ret = object.toString().replaceAll("B", "-Z-");
    return ret;
  } else {
    return null;
  }
}

After the check if the object to convert is null (in this case there is nothing to do), we use the String.replaceAll(…) method to search for ‘B’ and replace it with “-Z-”.

Keep in mind that the first parameter to the replaceAll method is a regular expression (see String.replaceAll(java.lang.String, java.lang.String)).

Now, if the data from the UI is send back to the model layer, it has to be converted back into the original format. So we have to do the conversion backwards by replacing all “-Z-” with “B” in the getAsObject(…) method:

/** Method which get the data from a uiComponent and should return it in the format we like to store in the DB (or elswhere)
* @param facesContext current facesContext
* @param uIComponent component which was used to deliver the data
* @param string data from the ui component
* @return object to use for further work (e.g. storage in the DB)
*/
public Object getAsObject(FacesContext facesContext, UIComponent uIComponent, String string) {
  if (string != null) {
    String ret = string.replaceAll("-Z-", "B");
    return ret;
  } else {
    return null;
  }
}

The result will be an Object which will be passed back to the model layer. If you don’t implement the getAsObject(…) method and just return the third parameter as resulting object, you would change every data in the back end to the new format. This may be your intention, but most often you don’t want to do this. It would mark every row of data dirty you have visited without any user interaction. This is because you pass different data back to the model than you read from it.

The last step to do is to register the custom converter in the faces-config.xml file of the ViewController project. Open the faces-config.xml file in JDev and select the ‘Converter’ tab

Click the green ‘+’ sign to get the an empty row in the converter section. Go to the property window and you see

Where we click on the ‘…’ button on the right end of the ‘Class’ field. We get the search for a class dialog where we look for the MyB2ZConverter class

Select the class and enter an ID fro the converter. This ID will be used in the UI to tell a component to use this converter.

Finally the converter section look like

UI Page

Now we can use the converter in a page or fragment. We start with a simple page where we define a inputText field and a button to submit the content of the field to see the converter working.

In the adfc-config.xml we add a JSPX page named ‘index’

And this page uses a quick layout as seen here

We add a title and the inputText field, a button to submit the data and two outputText fields to show what the converter has done to the data. The page layout looks like

or in code

If you like to copy the code use the following representation:

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
 xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
 <jsp:directive.page contentType="text/html;charset=UTF-8"/>
 <f:view>
 <af:document id="d1">
 <af:form id="f1">
 <af:panelStretchLayout topHeight="50px" id="psl1">
 <f:facet name="top">
 <af:outputText value="Converter Sample" id="ot1" inlineStyle="font-size:x-large;"/>
 </f:facet>
 <f:facet name="center">
 <af:panelGroupLayout layout="scroll" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" id="pgl1">
 <af:inputText label="Enter String" id="it1" value="#{bindings.myInput1.inputValue}">
 <f:converter converterId="B2ZConverter"/>
 </af:inputText>
 <af:commandButton text="refresh" id="cb1"/>
 <af:outputText value="current data: #{bindings.myInput1.inputValue}" id="ot2"/>
 <af:outputText value="current data with converter: #{bindings.myInput1.inputValue}" id="ot3">
 <f:converter converterId="B2ZConverter"/>
 </af:outputText>
 <af:commandButton text="Converter with DB Data" id="cb2" action="emp"/>
 </af:panelGroupLayout>
 <!-- id="af_one_column_header_stretched" -->
 </f:facet>
 </af:panelStretchLayout>
 </af:form>
 </af:document>
 </f:view>
</jsp:root>

Hint: you might notice another component, a button which is later used to navigate to a second page. This is described later.

For the inputText field we need to store the data a user enters. For this we can either use a DB table, a bean property or a pagedef variable. We use a pagedef variable (more on see see Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef) which we bind to the value property of the inputText component (value=”#{bindings.myInput1.inputValue}”). The converter is setup by adding an f:converter tag like

<af:inputText label="Enter String" id="it1" value="#{bindings.myInput1.inputValue}">
  <f:converter converterId="B2ZConverter"/>
</af:inputText>

The converterId points to the ID defined in the faces-config.xml file. Running the page will show

Enter ‘Hello’ into the field and clicking outside the field (so that it looses the focus) will show

As we see, the two outputText fields don’t show anything as the data in not submitted jet. Clicking the ‘refresh’ button submits the data and the converter goes to action

Well, as the input did not have any ‘B’ nothing changes. So lets us add another word ‘Beta’ and click outside the inputText

As we did not submit the data to the server, we still see ‘Hello Beta’ and the outputText fields show ‘Hello’ both. Now click the ‘refresh’ button to get

The inputText has changed to the new format where the “B” is exchanged with the “-Z-”, however the outputtext ‘current data’ still shows the ‘Hello Beta’. The reason for this is that the data send to the binding layer was converted back using the getAsObject(…) method which exchanged the “-Z-” with “B”.

This implements the use case described at the beginning.

Now, to show that the same converter works with data from a DB table as well we add another two pages to the adfc-config.xml. One showing the employees in a read only table with a link on the employeeId which navigates to the employee details in a form.

The navigation to the second use case is done with the button mentioned earlier (‘Converter with DB data’)

Clicking on the button will show a table with employees where the EMail column was used to add the converter

The column tag looks like

<af:column sortProperty="#{bindings.EmployeesView1.hints.Email.name}" filterable="true" sortable="true"
    headerText="#{bindings.EmployeesView1.hints.Email.label}" id="c3">
  <af:outputText value="#{row.Email}" id="ot5">
    <f:converter converterId="B2ZConverter"/>
  </af:outputText>
</af:column>

Like with the inputText we just add a f:converter tag with the right ID “B2ZConverter”. With this use case we see why the getAsObject(…) method should undo the formatting. You don’t want to store the Email like this. You only want to show it this way, but not overwrite the correct Email fro the employee. You can check the DB data and see that the Email is still stored with the “B” and not the “-Z-”

To verify this we can click the link in the first column to goto the detail page of the selected employee

Again, we see the ‘Email’ in the new format and the original data ‘NO CONVERTER Email’ in the normal data. The tags used for this are

  <af:inputText value="#{bindings.Email.inputValue}" label="#{bindings.Email.hints.label}" required="#{bindings.Email.hints.mandatory}"
      columns="#{bindings.Email.hints.displayWidth}" maximumLength="#{bindings.Email.hints.precision}"
      shortDesc="#{bindings.Email.hints.tooltip}" id="it1">
    <f:validator binding="#{bindings.Email.validator}"/>
    <f:converter converterId="B2ZConverter"/>
  </af:inputText>
  <af:panelLabelAndMessage label="NO CONVERTER #{bindings.Email.hints.label}" id="plam1">
    <af:outputText value="#{bindings.Email.inputValue}" id="ot2"/>
  </af:panelLabelAndMessage>

When using the binding for the Email without the converter we see the data as it’s stored in the DB. Using the converter we see the converted data.

The sample was build with JDeveloper 11.1.1.17.0 using the HR DB schema. You can download the sample from GitHub BlogConverterSample.zip