Implement resource bundles for ADF applications in a database table
Java (Web) applications typically make use of a built in structure for centralizing (and internationalizing) boiler plate text; it is called Resource Bundle. In a previous article – http://technology.amis.nl/2012/08/09/introduction-to-resource-bundles-in-adf-applications-for-centralizing-management-of-boilerplate-text – I described how resource bundles can be introduced into ADF applications (or basically and JSF application) to centralize (management of) boiler plate text, using file based resource bundles. This article you are currently reading continues from that article.
The purpose of this article is to demonstrate how you can implement your resource bundle using a database table instead of a file.
Managing database backed resource bundles can be a lot easier than managing their file based equivalent – through a simple edit page for example, used by an application administrator or business representative. Besides, with resource bundles in the database, changes can be applied without having to go through full redeployment of the application. Using a few simple steps including a table, a PL/SQL package, an ADF BC Application Module with a custom method in the client interface, a generic PageDefinition with one operationBinding for that custom method and a Java Class that extends the standard Java ListResourceBundle in order to leverage the database, you will quickly see the mechanism come together.
In later articles I will discuss how to support not just different boilerplate text items for different languages but also different sets of boiler plate text for user groups that vary by location, role, department, age or personal preference. We will discuss how to refresh a resource bundle in running application and finally we will see how we can create an in-line resource bundle editing mechanism that allows users to manipulate the boiler plate text in context in a running application.
Steps for implementing the resource bundle in a relational database table
1. Let us first create a simple database table that will contain the resource bundle entries for our application.
it is the relational equivalent of the resource bundle in property file format: key and value, with the locale added in there as well to distinguish between different languages.
2. Instead of having the application define the SQL to execute for retrieving the resource bundle entries, I have decided to introduce a package that provides an API to the application.
This I consider a good practice – keeping SQL out of the application, thereby making life simpler for the ADF developers and adding additional decoupling that allows for optimizations behind the API without impacting the ADF application. In this case the function get_resource_bundle_entries will take the locale – Java term for language and optionally region – as input and return a Ref Cursor. The structure of the records returned by this cursor is (key, value).
The package implementation:
3. In ADF applications, a common way to interact with the database is of course through ADF BC. If we already have an Application Module, we can extend that one with a method for retrieving the contents of the resource bundle. In this case, we first need to add an Application Module to the Model project. Note: you can just as well use JPA (EclipseLink or Hibernate) or even create your own code that retrieves a Data Source can calls the PL/SQL package.
Create an Application Module – leveraging a database connection to the schema in which the package is created. Accept all defaults.
On step 4, mark the checkbox to generate the Application Module Class:
When the ApplicationModule is created, open the editor. Select the Java tab. Click on the link that takes you to the AppModuleImpl class:
Add the following method:
This method retrieves the database transaction from the ApplicationModule super class. It constructs a Callable statement to call out to the package. The package returns a Ref Cursor which maps to a JDBC ResultSet. This ResultSet is iterated through and all records are turned into Map entries – key/value pairs. The statement is closed, the Map is returned.
Back in the application module editor, click on the edit icon to start editing the client interface:
Add the newly added method to the client interface of the application module to make that method available across the data binding bridge:
Click OK. The Java tab in Application Module editor now looks like this:
and the Data Control that the ViewController project will use, now also exposes the operation getResourceBundle.
Insert a few resource bundle entries into the table:
Then run the Application Module to test the new method:
Click on the AppModule node. Enter the value en as the locale and press the execute button:
The results should be visible and the values you inserted into the database should be among them:
4. Create a generic PageDefinition with an operationBinding for getResourceBundle
The ViewController project contains the resource bundle. At this moment, the implementation is based on a property file, but before too long we will switch to a Resource Bundle that is implemented by a Java Class. And that Java Class needs to get hold of the resource bundle entries from the database. It will do so using the DataControl AppModuleDataControl (and indirectly the ApplicationModule and the underlying PL/SQL package). Programmatic access to an operation exposed by a Data Control can be done through a generic page definition that contains an operation binding for that operation. This page definition is accessed at run time from whatever Java code.
The easiest way to create a generic page definition is by creating a JSF page called Generic.jspx, dragging the operation from the Data Control palette to the page
- thus creating the page definition and the operation binding at the same time
and then deleting the page and removing the reference to the page from the file DataBindings.cpx:
5. Create a Java Class based Resource Bundle, backed by resource bundle entries from the database
Although it is common to see Resource Bundles based on files, they can just as easily be based on Java Classes. Any class for example that extends ListResourceBundle (see http://docs.oracle.com/javase/tutorial/i18n/resbundle/list.html) which requires the implementation of the getContents() method that returns an array of arrays (Object)
can be used as Resource Bundle.
Create a class called DatabaseResourceBundle in package nl.amis.view.bundles. Have the class extend ListResourceBundle and implement the required method:
Change the getContents() method to have it return one or multiple of the keys used in PageOne:
6. Registering a Java Class based Resource Bundle is very similar to registering a file based resource bundle. The fully qualified name of the class replaces the fully file-path-qualified name of the properties file in the faces-config.xml file:
or in source code:
7. Run the page
Not surprisingly, two resource bundle entries have been lost – and two are still there, this time returned from Java.
8. Hook up the DatabaseResourceBundle class with the actual database content.
The getContents() method is changed: it either returns the already initialized contents, or it first initializes the contents. The initResourceBundle() method is not tied to either database or ADF Binding – it relies on a method getResourceBundle(locale) to return a Map with the resource bundle entries and it will then prepare the Object stored class member:
The real action is in the method getResourceBundle(). This method dives into the ADF Binding framework, gets hold of the Generic Page Definition and inside it of the getResourceBundle() operation binding that it will execute for the required locale:
Note: at this moment the implementation is still a little rough around the edges. This means for example that the page on which the database backed resource bundle is first accessed should have a Page Definition associated with it – otherwise there will not be a binding context to access the GenericPageDef in.
9. Run the page
Two resource bundle entries are retrieved from the database (two others have yet to be added).
The next articles will discuss support for multiple languages as well as multi-context resource bundles and on line editing for resource bundles.
Download the ADF application discussed in this article: ADFFacesWithResourceBundle_Step2_DatabaseBacked.
Previous article, introducing resource bundles in ADF applications: http://technology.amis.nl/2012/08/09/introduction-to-resource-bundles-in-adf-applications-for-centralizing-management-of-boilerplate-text .
Other articles in this series:
Other articles in this series:
- Introduction to Resource Bundles in ADF applications for centralizing (management of) boilerplate text
- Personalize Resource Bundles in ADF applications with JHeadstart 10.3.2 run time
- Context Sensitive Resource Bundle entries in JavaServer Faces applications – going beyond plain language, region & variant locales
- Testing i18n features of JSF applications – Forcing a refresh of the Resource Bundle(s)
- ADF – Implement a 'Delete this record' icon (action) in an ADF Table
- ADF: (Automatic) Partial Page Rendering across Taskflows
- ADF client-side architecture – Select All
- On the Integrity of Data
- ADF DVT: Analyzing Financial Position of the European Football (Soccer) Leagues using Treemap
- ADF DVT – Scaling TreeMap components for comparisons across masters and categories
- ADF DVT: Using the Tree Map visualization component – to compare relative sizes and distributions
- ADF DVT: Using the Timeline component to visualize the recent history of an RSS feed
- ADF: (re-)Introducing Contextual Events in several simple steps
- ADF DVT Speed Date: Interactive Bubble Graph
- Out of the box usage of ADF DVT Scheduling Gantt Chart to report Database Query Results using stacked bar charts per time period