Any web application contains boiler plate text: text that is not part of the enterprise data from web services or databases that is manipulated by the end users using the application but that is part of the application definition itself. Text that is shown as prompt, title, message, hint text and in other ways. Developers can sprinkle the boilerplate text all throughout the application, in pages, JavaScript and other code sections. But they should not do that. Changing boiler plate text is a frequent requirement from the business. Having all boilerplate text in a central location makes such changes a lot easier. Additionally, many organizations require applications to be multi-lingual: different groups of users speak different languages and want to have the application support them in their own language. That means boiler plate text is not just defined once in that central location, but once for every language the application needs to support.
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 – https://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.
Resources
Download the ADF application discussed in this article: ADFFacesWithResourceBundle_Step2_DatabaseBacked.
Previous article, introducing resource bundles in ADF applications: https://technology.amis.nl/2012/08/09/introduction-to-resource-bundles-in-adf-applications-for-centralizing-management-of-boilerplate-text .
Other articles in this series:
-
Introduction to Resource Bundles in ADF applications for centralizing (management of) boilerplate text
-
Implement resource bundles for ADF applications in a database table
-
Supporting multiple languages in ADF applications backed by resource bundles – and programmatically controlling the JSF locale
-
Adding customization (or context sensitivity) to boilerplate text from database backed resource bundles in ADF applications
-
Refresh resource bundle from within the ADF application – to absorb changes in database backed bundles
-
Live update of Resource Bundle from within running ADF application
-
Live resource bundle entry editing in a generic way through declarative component and UI component tree manipulation
-
Creating reusable ADF Library with generic live resource bundle editing functionality and reusing it in any ADF application
Other articles in this series:
-
Introduction to Resource Bundles in ADF applications for centralizing (management of) boilerplate text
-
Implement resource bundles for ADF applications in a database table
-
Supporting multiple languages in ADF applications backed by resource bundles – and programmatically controlling the JSF locale
-
Adding customization (or context sensitivity) to boilerplate text from database backed resource bundles in ADF applications
-
Refresh resource bundle from within the ADF application – to absorb changes in database backed bundles
-
Live update of Resource Bundle from within running ADF application
-
Live resource bundle entry editing in a generic way through declarative component and UI component tree manipulation
-
Creating reusable ADF Library with generic live resource bundle editing functionality and reusing it in any ADF application
Hi,this article helped me a lot in 11g when i was learning about translations.
Now, with 12c i´m doing it again for webservices, but i´m stuck rigth after the AMtest. I can´t get to the method in AMImpl from the class serving as webservice.
Any ideia how?
Thanks
Hi,
Thank you for the valuable article.
Is it so that DB table with translations would be read into memory for each user session?
BR,
Alex
Hi Alex,
This data is loaded just once – into an application wide JVM level cache.
Lucas
Hi Mr Jellema, Great article! I’m interested in implementing this for our ADF 12c application. One question I had is if we implement a database-driven resource bundle, what would be the impact on the developers in terms of design time? Would JDev behave oddly?
Thanks so much!