Change Application Configuration at Run Time through a Properties File (Part 2)

In this second part of the mini series we look into the problem left over from part 1.

We left the task to change the location and name of the property file we read when a configuration property is needed by the application. You my ask why we need to change the path at all. The answer is that most administrators won’t allow you to copy a file to any location on a server. They normally don’t allow access to a production server at all. You can ask them to put put the configuration file to a location of their choice and then configure this path during deployment of the application. This is exactly what we do in this blog.

In the first part we finished the sample application which read a property file from a location we can set via a context parameter in web.xml. The question now is, how to change this parameter during deployment of the application. The answer to this is to use a Deployment Plan.

Typically, deployment plans are created by developers along with the associated application files, then distributed to the administrator or another deployer, who updates the plan for a particular environment (such as staging, testing, or production). The deployment plan is stored outside of an application archive or exploded archive directory. We store the initial plan in the ViewControllers src/META-INF folder as BlogReadConfigFile_Plan.xml.

<?xml version='1.0' encoding='UTF-8'?>
<deployment-plan xmlns="http://xmlns.oracle.com/weblogic/deployment-plan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd">
    <application-name>BlogReadConfigFile</application-name>
    <variable-definition>
        <variable>
            <name>ResourceFileLocation</name>
            <value>/tmp/readconfigfile.properties</value>
        </variable>
    </variable-definition>
    <module-override>
        <module-name>BlogReadConfigFile_BRCFViewController_webapp.war</module-name>
        <module-type>war</module-type>
        <module-descriptor external="false">
            <root-element>web-app</root-element>
            <uri>WEB-INF/web.xml</uri>
            <variable-assignment>
                <name>ResourceFileLocation</name>
                <xpath>/web-app/context-param/[param-name="de.hahn.blog.readconfigfile.FILENAME"]/param-value</xpath>
                <operation>replace</operation>
            </variable-assignment>
        </module-descriptor>
    </module-override>
</deployment-plan>

There are three notable parts in the plan. The first is the application name which is the same as you set under application properties in the deployment descriptor


The second section is the variable-definition section. Here we define variables which we later can use in the other parts of the descriptor as reference. Without a variable definition we can’t change a thing in the plan.
We name our variable ‘ResourceFileLocation’ and set the value to any appropriate location we like. This location don’t have to exist on the target server. We change it later anyway.
The third part is the module-overwrite where we specify which part of the of any descriptor, which is part of the deployment, we like to change.
It’s essential to name all parts exactly as they are named in the descriptors in your application.

The module name is the name of the war file we build, the type is war, as we build a war artifact. The root element describes which section of the deployed application we want to change and the URI exactly where the descriptor is located relative from its root.
The interesting part is the variable-assignment where we specify the variable name defined in the variable-definition section and which element of the defined module we want to change. This is done with a XPath expression:

<xpath>/web-app/context-param/[param-name="de.hahn.blog.readconfigfile.FILENAME"]/param-value</xpath>

which describes the location of the context parameter in web.xml with the name “de.hahn.blog.readconfigfile.FILENAME” and set it’s value to the variable value.
The operation tells what we want to do. As we want to replace the value we choose REPLACE as operation.

To make the setup complete we have to specify the BlogReadConfigFile_Plan.xml in the application properties

EAR Deployment Profile Properties

EAR Deployment Profile Properties


For more info about how to use deployment plans refer to the documentation at How to Use Deployment Plans

We can now deploy the application as usual and run the application from within JDeveloper. To show how it’s done when you deploy the application to a stand alone server we first create the ear file, then start the integrated WLS in JDevloper and open the admin console to deploy the application

Deploy the Application

Deploy the Application


which will create the ear file as we see in the log window

[06:33:59 PM] ----  Deployment started.  ----
[06:33:59 PM] Target platform is  (Weblogic 12.x).
[06:33:59 PM] Running dependency analysis...
[06:33:59 PM] Building...
[06:33:59 PM] Deploying 2 profiles...
[06:33:59 PM] Wrote Web Application Module to /data/development/ENTW_12.1.3.0.0/QT/BlogReadConfigFile/BRCFViewController/deploy/BlogReadConfigFile_BRCFViewController_webapp.war
[06:33:59 PM] Wrote Enterprise Application Module to /data/development/ENTW_12.1.3.0.0/QT/BlogReadConfigFile/deploy/BlogReadConfigFile.ear
[06:33:59 PM] Elapsed time for deployment:  1 second
[06:33:59 PM] ----  Deployment finished.  ----

Next step is to open the admin console and to deploy the ear file


If we run the application we see that the application tries to read the properties file in the log window
Running Application

Running Application


We now want to change the properties file the application is using. For this we change the location and name of the properties file we configured in the web.xml file and change the content of the properties file:

The UI has not changed, however, after you click the refresh Properties button and look into the log output you see
Application Tries to Read Properties from New Location

Application Tries to Read Properties from New Location


Please note that the location of the properties file has changed. If we had the file at the position defined in the deployment plan, the application would have read the properties from there.

Change Application Configuration at Run Time through a Properties File (Part 1)

Often users ask how to change some configuration properties, e.g. a reprot definition file or endpoint of a web service, art runtime of the application. This is not an easy task as such configuration normally is deployed together with the application as part of the ear file. This however can’t be changed easily.

There are different possible solutions, like providing a Mbean which then can be used in the weblogic servers admin console to change values. A sample for this approach can be found e.g. here Creating Mbeans(JMX) in ADF Application and accessing them from jrockit mission control.

In this blog I show a different approach which uses a configuration file which can be changed externally. The values read from the file are properties (key value pairs). If we make changes to the file they are reflected during run time without the need to restart the application. Keep in mind that this approach does not work will on a clustered system as there are multiple servers with multiple file locations which have to change. One way to overcome this is to set the location on e shared file system which can be accessed from all servers.

To implement this use case we first have to think about how we get the path to the configuration file and it’s name to load it during run time.

To get the path the the configuration file we use a context parameter which we define in the web.xml file. The reason for this is that we need to change this parameter depending on the system we deploy the application too. In addition you can’t make predictions like where an administrator likes to put the configuration file.

Context Parameter in web.xml

Context Parameter in web.xml

To load the properties we can use java default Properties class which loads properties from a stream. The bit and bytes can be found in the source of the work space which is available GitHub (see link at the end of the post).

One thing to notice is that this post uses Apache Commons-IO version 2.4. This update make one other change necessary in the weblogic-application.xml file.

  <prefer-application-packages>
    <package-name>org.apache.commons.io</package-name>
  </prefer-application-packages>

This entry allows the application to use the included commons-io jar to be loaded before the already available commons-io jar, of an older version, in WebLogic server 12.1.3.

After setting the parameter let’s write a sample page where we show some of the properties on the page, then change the configuration reset the properties and see the changes on the page.
For the implementation we use an application scope bean. The reason is that the configuration parameter should be available application wide. There is no need to keep this info per session. If you read the configuration file for every parameter you can use a request scope bean.

In a live system I would not recommend using this approach (reading the configuration file every time) because it produces a bottleneck reading the file over and over again. Instead I would call a method periodically or as the result of a user action like button click.

OK, let’s create a configuration file in the WEB-INF folder or if you like in any other folder. Once the file is created we copy it into a temporary folder on the system (/tmp on mine) and read it from there.


Now we need an application scope bean

The bean has a getProperties method which checks if the proprieties are already read or if not read the context parameter from web.xml to read the file from the given position.

    /**
     * This method savely get the properties from a file if the file can be read, otherwise it return an empty properties object
     * @return Properties object read from file of empty properties object if hte file was empty or could not be found
     */
    public Properties getProperties() {
        if (properties == null) {
            FileInputStream fileInputStream = null;
            try {
                // read context parameter
                String initParameter = FacesContext.getCurrentInstance().getExternalContext().getInitParameter(PROPERTYFILE_PARAMETER);
                logger.info("Read properties from " + initParameter);
                // try to read the file
                File file = FileUtils.getFile(initParameter);
                fileInputStream = FileUtils.openInputStream(file);
                properties = new Properties();
                properties.load(fileInputStream);
                logger.info(properties.size() + " properties successfully read.");
            } catch (IOException ioe) {
                logger.warning("Error: Property file could not be read!", ioe);
                properties = new Properties();
            } finally {
                if (fileInputStream != null)
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
        }
        return properties;
    }

    /**
     * Reset the read properties so that the next try to read a property ready the file again
     */
    public void readPropertiesAgain() {
        logger.info("Reset properties!");
        properties = null;
    }

    /**
     * Method to return the version information of the configuration file.
     * this information is compiled from the keys PROPERY_NAME and PROPERTY_VERSION
     * @return version information read from the file
     */
    public String getPropertyVersionInfo() {
        String property = getProperty(PROPERTY_NAME);
        String property_2 = getProperty(PROPERTY_VERSION);
        String res = "Unkown";
        if (property != null && property_2 != null) {
            res = property + " " + property_2;
        }
        logger.info("Properyinfo: " + res);

        return res;
    }

The second method is used to reset the local storage of the properties, so that the next time a property is read the whole file will be read again. The third method is used to get the version information of the configuration file which is build as the concatenation of two properties.

On a page we add a button to reset the properties in the application scoped bean. After this the configuration file will be read again.

<af:button text="Read Configuration again" id="b1"
 actionListener="#{ReadPropertyFileBean.rereadActionListener}"
 partialSubmit="true"/>

Finally we add an outputText component to the page which uses an EL to read the PropertyVersionInfo from the application scoped bean ‘ReadPropertyFileBean’

<af:outputText id="ot6" inlineStyle="font-size:small;" 
 value="#{ReadPropertyFileBean.propertyVersionInfo}"
 partialTriggers="b1"/>

When we run the application we see the inital screen like

Initial index.jsf

Initial index.jsf


we change the configuration file
Changed Configuration File

Changed Configuration File


and reset the properties via a click on the button
Changed index.jsf

Changed index.jsf

This concludes part 1. In the final 2nd part we see how to change the fixed path set as context parameter in web.xml during deployment. This allows us to deliver a properties file together with the application but let the administrator decide where to put it on the server.

The work space for this sample can be downloaded from GitHub BlogReadConfigFile Release 1.0 or get the zipped workspace of this release 1.0
The sample is build using JDeveloper 12.1.3 and uses the HR DB schema.

The Git Experience (Part 4)

In this part of the ‘Git Experience’ series we are looking at GitFlow. GitFlow is a branching model which helps you and your company to structure your work in a way which is understandable and has proven it’s value in many projects. I don’t want to copy all information given in the link about GitFolw but only use the image from the blog post:

GitFlow Model

GitFlow Model

What we see in this image is a timeline of development with releases, hot fixes, development and feature branches and how they work together. We like to use this model to structure the work of the development. The development needs to set up the software for releases which are delivered to the customer or an internal server. Then there is the need to supply hot fixes if a release version has a major bug. Nevertheless development has to develop for the next release, probably breaking the task into smaller pieces we call features. To keep this features in our repository as well we use feature branches which are merged back to the development branch once they are ready and tested.

The development branch is the grapevine for the development. Feature branches as well as release branches are started from the development branch. Once a release is ready the release branch is merged and tagged on the master branch and merged to the development branch.

IMPORTANT: you never work directly on the master branch!

In the last part ‘The Git Experience (Part 3)’ we started a new repository on GitHub which we use in this part to introduce GitFlow on it. There are several ways to do this. You can use the command line and execute the git commands from there. Or you use shell scripts to put multiple git commands as a unit of work together and call the script to e.g. start a new feature branch. The last and least complicated way is to use a tool which already has set up the scripts for you and gives you a nice GUI to work with.

We follow the last suggested way and use a tool with graphical user interface. As JDeveloper does not support the usage of GitFlow with a GUI we use an external tool like ‘SmartGit’ or ‘Source Tree’ which both come with a graphical user interface supporting GitFlow. For the remainder of this blog we use SmartGit as it’s available for Windows and Linux operating systems. It’s free for non commercial use.

Once we started SmartGit we can add our local repository to be shown in SmartGit. Don’t be confused this with cloning a repository. Cloning fetches a remote repository from a remote server and creates a local copy of it on your pc. As we already have the local repository on out machines we just add the local repository.

For those of you how did not create the repository in the last part you can clone it from my GitHub server repository using ‘https://github.com/tompeez/BlogReadConfigFile.git&#8217; as url for the clone command.

After this SmartGit looks the same as the last image after adding the repository. You now can play with the SmartGit UI (or any other too you are using). One thing I like to bring to attention is the ‘Log’ button. Clicking this button opens a new window which shows the timeline of all commits.

Right now we only see two nodes which were created during creation of the repository.

Let the fun begin: Introduce GitFlow to the project

Now that the local repository is up in the tool of choise, lets introduce GitFlow to it. For this we click the GitFlow Button, select the ‘Full’ radio button and leave the rest of the options as is.

This will add another branch to the repository named ‘develop’

Repository after GitFlow Introduction

Repository after GitFlow Introduction

However, this  new branch is not the current branch as the ‘master’ branch is still marked current. Please also note the different color of the GitFlow button. In this shape it starts a HotFix as the master branch is the current branch and all hot fixes are started from the master branch, or better a release tag on the master branch.

GitFlow Button: Start HotFix

GitFlow Button: Start HotFix

We change this by double clicking the develop branch to get

and see the GitFlow button changes to a different default action, ‘Start Feature’ as we are now working on the develop branch. Before we start our first feature we take a look at the GitHub remote repository:

GitHub Timeline

GitHub Timeline

As we see the remote repository still only have one branch ‘master’. This is a lesson we have to learn fast. Everything we do, we do only locally. The remote repository doesn’t know about our work until we tell about it or push our changes to the remote repository.

Let’s push the ‘develop’ branch to the remote repository by clicking the Push button

Last thing to do is to do some house keeping on the GitHub side. Here we set the ‘develop’ Branch as ‘default’ branch.

Now we are ready to start our first feature. Remember that the feature branch is only created on the local repository and not automatically pushed to the remote GitHub repository. If you like the feature branch to be visible in the remote repository you have to push it there after creating it.

We create a feature ‘Feature_1′ (you should choose a more meaningful name!):

As we see the new branch is the current working branch. We now make some changes e.g. adding a header above the table and then look at the changes in the SmartGit and GitHub UI.

We add a panelGroupLayout to the top facet of the panelStretchLayout to add a header telling us what we see and another text telling that this was added with ‘Feature-1′. This is just for the time we are playing with the GitFlow features. We later can safely remove this second text.

As we see, all tools showing the same changed files. The interesting thing is that we see a changed index.java file. The only change we made was to add something to the index.jsf file!

Well, this second change was not intended but is the result of a setting in JDeveloper which adds a property to a backing bean for every component we add to the index.jsf file. Before we remove this nonsense setting let’s save the changes to our repository and look at the different tools:

In the first image we see an interesting info: ‘Outgoing (1)’. This means that SmartGit knows that this branch isn’t connected to the remote repository and can’t seen there. This isn’t really necessary as Git is a distributed version control system, but other users can’t get to this branch if the PC holding it isn’t available (due to network restrictions or because it’s offline).

After this the new branch is visible and tracked in GitHub. Now we can remove the not needed nor wanted index.java backing bean.

Why is it there in the first place?

If we create a new view in a task flow we have the option to activate the automatic component binding to a backing bean in this dialog

Automatic Component Binding

Automatic Component Binding

Well, it’s either a bug in JDev 12.1.3 or a saved configuration I made to investigate something else which uses automatic component binding. Les’s assume the latter and remove this setting.

Now we have to remove line 76 in the index.jsf file, fix the bindings in index.jsf by replacing them (find: binding=\”#{backingBeanScope.backing_index..*\”), remove the bean from adfc-config.xml and finally remove the index.java file from the project

Now we can compile the source, test the application and then save the changes in the repository. Don’t forget to push the changes to the remote repository!

If you use SmartGit to commit the changes you can commit and push in one command by clicking the ‘Commit & Push’ button in the dialog. The final timeline in SmartGit looks like

SmartGit Timeline

SmartGit Timeline

Time to wrap everything up. We made some changes and now are finished with our feature. We now finish the feature in SmartGit by clicking the GitFlow button and follow the dialog

The second image shows the options we have to finish a feature. Here we can decide to remove the feature branch completely or, as we do, keep it for later. As features are not used by GitFlow to build a release or hot fix on them, there is generally no need to keep them after they are finished and merged back into the development branch.

The final thing to make the circle is to build a release from the current development branch.

We add a release note part to the README.MD file to make the release visible in the file too. Now we commit the change (not shown) and finish the release

In image 2 we can set the options we want to use to finish the release. The one we change from the default is that we like to keep the release branch so that we can see it in the timeline. This is not necessary as you can’t do anything with the branch (beside cherry picking :)). The last image shows the SmartGit timeline where we see all commits and the different branches used. This show look like the GitFlow image we started this blog with.

This finishes part 4 of the blog. The repository (and it’s branches) and be cloned or loaded from GitHub.

The Git Experience (Part 3)

Almost a year ago I posted the second part of my git experience series (JDeveloper 11.1.1.6.0: The Git Experience (Part 2)). In this part I announced a third part which should handle working with remote git repositories.

I totally forgot about this. Only after a question on OTN JDeveloper and ADF forum I looked up the older blogs about git. Since the last post a new JDev version 12.1.3 has arrived so I decided to use this new version to continue the series.

In this part we are talking about setting up a remote repository. As remote git server I use GitHub which allows you to easily set up an account for non commercial use without any cost. Please read the all about the process on the GitHub Help web page.

Before we begin we have to talk about security accessing the remote repository. There are multiple possible connection strategies. The more common are ssh and https. Both are supported by GitHub, https is the recommended.

In this blog I use https to authenticate and work with GitHub, all you need to remember is your GitHub username and it’s password. I tested ssh too but like https better as you don’t have to handle the ssh keys on every pc you use (and I use up to 10 different machines).

I assume you have built yourself an account on GitHub which you can use. If you don’t want to create one yourself you can use the repository of this blog (link available at the end of the blog). Creating a new remote repository yourself can’t be done without an account. For those of you part 4 of the series which will be published in a couple of days will talk about how to do real work with remote repositories and introduce GitFlow as work template.

Let’s start with a typical workflow. We want to implement a new application using JDev 12.1.3 and want to use git as version control system. As the development is done on multiple locations, the repository should be accessible for all team members 24/7. This exclude a repository on a local pc which others might not be able to reach (e.g. if it’d down).

We generate a new ADF Fusion Web Application. I spare the walk through this process. As name of the new application workspace we choose BlogReadConfigFile, every other name you like is OK too.

Once the new application workspace is ready we like to put it under git control. We first do this locally by using the ‘Team->Version Application…’ menu. See the gallery below fro the whole process.

Notice the ‘.git’ folder which holds all information about the git versions of the application on your local pc. The fresh created local repository can be used already to make changes and commit them to the local repository. Other team members can clone the repository from your pc if they have access to it.

As the new workspace is under version control we can and should open a new window in JDev via menu ‘ Window->Team->Version’ to get a view of the version information available in JDev:

Open Version Window

Open Version Window

Expanded Version Window

Expanded Version Window

As you’ll notice all remote nodes in the tree are empty. The local master branch is the current branch we are working on, visible through the green badge. Now we want to make this local repository available to a remote git server GitHub. First problem is that JDev 12.1.3 (and all other version I know of) don’t support creating a remote repository. We can add existing remote repositories by right clicking to the ‘Remotes’ node and ‘add remote…’

Add a Remote Repository

Add a Remote Repository

Add Remote Dialog

Add Remote Dialog

As you see you can only add the URL to the remote repository but can’t create it. So we have to create the remote repository on GitHub first. Read ‘Setup a new Repository’ to find out how to do this. Make sure you make it a public repository and leave the check box to init the repository with a README.md file empty for the moment.

Create Repository in GitHub

Create Repository in GitHub

You can add default ‘.gitignore’ fie and a licence via this dialog too. We use a ‘.gitignore’ file from other JDev projects as the default java one doesn’t know about some of the artifacts used by JDev. After finishing the dialog you get the information from GitHub how to get data into the new repository.

How to add Data

How to add Data

If you decide to use ssh instead of https you can click the button on the left side of the URL and the info switches accordingly.

As we already have our local repository we use the second method ‘…or publish an existing repository from the command line’. For this we open a command shell and open the location of the workspace (‘/data/development/ENTW_12.1.3.0.0/QT/BlogReadConfigFile’ on my machine). Copy the commands from the second option into the command shell. You are asked for your credentials to login into GitHub, after that the local repository is pushed to GitHub.

Command Lines to Push Data to Repository

Command Lines to Push Data to Repository

When we look at the GitHub web page we see the data from the workspace

Finished Remote Repository

Finished Remote Repository

As mentioned at the button of the page we should add a README.md file. This can be done directly on the web page or you can add such a file on your local pc and push it to the repository. I like to do this through the web page as it copies the description from the creation of the repository into the file.

Add README.md

Add README.md

Now if we look at the Versions window in JDev we see that the remote repository is visible in JDev too.

Updated Version Window

Updated Version Window

Dev knows about the changes as they all in the ‘.git’ folder in the workspace. Right now command line git , JDev git and third party tools like SmartGit, SourceTree or mysygit (or Tortoise Git on windows) play well together. You can use them interchangeably.

As we have added the README.md file using the web page, out first action is to refresh the workspace to get the README.md file into our local repository. For this we use the menu ‘Team->Git->Pull…’, work through the wizard and check the file system after the work has finished.

This concludes this part 3 of the series. You can clone the repository from GitHub. Stay tuned for the next part where we introduce GitFlow and start working on the repository.

Handling images/files in ADF (Part 5)

I received a couple of questions regarding the handling of the images directly after upload for the sample application done in part 1-4.

    Part 1 gives an overview of the sample application I’m going to build and how to set it up
    Part 2 shows how to upload a file, store it and download it back to the client
    Part 3 implements two techniques to show the data (image) on the user interface
    Part 4 backport of the sample to JDeveloper 11gR1
    Part 5 implements a technique to show the uploaded file right after upload without the need to commit first

The sample application finished in part 3 (part 4 is a backport to JDev 11gR1 only) has one minor glitch: it doesn’t display an uploaded image directly to the user after uploading it. The user has to commit the data after insert or update of an image before the image becomes visible. Users like to see the newly uploaded image before committing the row. This allows the users to cancel the change or select and upload another image. In this 5th part of the series we implement this.

Before we start to implement let’s talk about how to implement this enhancement. Why isn’t it possible to upload the image data into the blob and then just show the image from the blob via the servlet (see part 3)?
The problem is that the BlobDomain uses a stream to read the data uploaded from the user. This stream can only be read after the BlobDomain is saved, meaning after the commit.

The solution we implement in this part stores the uploaded data (inserted or updated) in a temporary file on the server. Then the server uses the image data from the temporary file to visualize the data. This sounds easy enough, however there is some house keeping to do to make it work.

First we have have to find a place (folder) where we can store the uploaded data until it’s stored in the db or the operation is canceled. Then we need to distinguish which data to show from the servlet (file or blob). Finally we have to clean up the temporary file when we are done.

Lets dive into the implementation. We start from the application at the end of part 3. As the current JDeveloper version is 12.1.3 we do the implementation in this version. The first task is to migrate the old application to 12.1.3. This is done automatically when opening the old work space in JDev 12.1.3 by answering the ‘OK’ to the migration popup. Nothing need to be done here. However, when you download  the work space you’ll notice some clean up I did, like changing the old af:commandButton to the new af:button.

One thing to notice is that the Apache Commons-IO version is updated to 2.4. This update made one other change necessary in the weblogic-application.xml file.

  <prefer-application-packages>
    <package-name>org.apache.commons.io</package-name>
  </prefer-application-packages>

This entry allows the application to use the included commons-io jar to be loaded before the already available commons-io jar, of an older version, in WebLogic server 12.1.3.

Here are the steps we take to implement the tasks:
1) Save the uploaded data to a temporary file as well as to the blob. This is done for convenience. It’S possible to store the data first in the temporary file and only copy it to the BlobDomain when the user commits the changes.
We implement a new java class UploadBlob which holds the BlogDomain and the path to the temporary file. This class also allows to test if a temporary is available.

package de.hahn.blog.uldl.view.types;

import oracle.jbo.domain.BlobDomain;

/**
 * This type class holds the BlogDomain and a path to a temporary file holding the uploaded image data
 */
public class UploadBlob {
    /**
     * Holds the uploaded data
     */
    BlobDomain dataBlob;

    /**
     * Path to the temporary file if availabe
     */
    String tempFile;

    /**
     * C'tor.
     */
    public UploadBlob() {
        super();
        tempFile = null;
        dataBlob = null;
    }

    /**
     * Gets the status of the temporary file
     *
     * @return true if a temporary file is available, false otherwise
     */
    public Boolean getTempFileAvailabe() {
        return (tempFile != null ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * @param inageBlob
     */
    public void setInageBlob(BlobDomain dataBlob) {
        this.dataBlob = dataBlob;
    }

    /**
     * Gets the BlobDomain holding the uploaded data
     * @return
     */
    public BlobDomain getDataBlob() {
        return dataBlob;
    }

    /**
     * Sete the path to the temporary file holding the uploaded data
     * @param tempFile path to the temporary file
     */
    public void setTempFile(String tempFile) {
        this.tempFile = tempFile;
    }

    /**
     * Getter for path to temp file holding the data of the uploaded data
     * @return path to the temporary file holding the uploaded data
     */
    public String getTempFile() {
        return tempFile;
    }
}

2) Use this class in the ImageBean.java class where the uploaded data is read. This happens in the valueChangeListener uploadFileValueChangeEvent(ValueChangeEvent valueChangeEvent).

    /**
     * @param valueChangeEvent
     */
    public void uploadFileValueChangeEvent(ValueChangeEvent valueChangeEvent) {
        // The event give access to an Uploade dFile which contains data about the file and its content
        UploadedFile file = (UploadedFile) valueChangeEvent.getNewValue();
        // Get the original file name
        String fileName = file.getFilename();
        // get the mime type
        String contentType = ContentTypes.get(fileName);
        // get the current roew from the ImagesView2Iterator via the binding
        DCBindingContainer lBindingContainer = (DCBindingContainer) BindingContext.getCurrent().getCurrentBindingsEntry();
        DCIteratorBinding lBinding = lBindingContainer.findIteratorBinding("ImagesView2Iterator");
        Row newRow = lBinding.getCurrentRow();
        // set the file name
        newRow.setAttribute("ImageName", fileName);
        // create the BlobDomain and set it into the row
        UploadBlob blob = createBlobDomain(file, Boolean.TRUE);
        newRow.setAttribute("ImageData", blob.getDataBlob());
        // set the mime type
        newRow.setAttribute("ContentType", contentType);
        String tmp = (blob.getTempFileAvailabe() ? blob.getTempFile() : null);
        setTemporaryFileVar(tmp);
        UIComponent ui = (UIComponent) valueChangeEvent.getSource();
        // PPR refresh a jsf component
        ui = ui.getParent();
        AdfFacesContext.getCurrentInstance().addPartialTarget(ui);

    }

Instead of reading the data into the BlobDomain a changed method createBlobDomain is called (line 18). the method now returns an instance of the new class UploadBlob. Below is the code of the new method:

    private UploadBlob createBlobDomain(UploadedFile file, Boolean createTempFile) {
        // init the internal variables
        InputStream in = null;
        OutputStream outTmp = null;
        UploadBlob blobDomain = null;
        OutputStream out = null;
        File tempfile = null;
        logger.info("Starting to create UploadBlog from data...");
        try {
            logger.info("... create BlobDomain...");
            blobDomain = new UploadBlob();
            // Get the input stream representing the data from the client
            in = file.getInputStream();
            // if a temporary file should be created , we do this first as we can't get
            // data data back from the blob until we commit the row. in the next step we
            // write the upload data to a temp file and then copy it into the blob
            if (createTempFile) {
                logger.info("... Creating temporary file...");
                File tempdir = FileUtils.getTempDirectory();
                String ext = FilenameUtils.getExtension(file.getFilename());
                if (!ext.isEmpty()) {
                    ext = "." + ext;
                }
                logger.info("... set extension to " + ext + "...");
                tempfile = File.createTempFile("upl", ext, tempdir);
                logger.info("... " + tempfile.getAbsolutePath() + "...");
                // set path to temporary file
                blobDomain.setTempFile(tempfile.getAbsolutePath());
                FileOutputStream fileOutputStream = FileUtils.openOutputStream(tempfile);
                logger.info("... copy data to temporary file...");
                IOUtils.copy(in, fileOutputStream);
                in = FileUtils.openInputStream(tempfile);
                logger.info("... set inputstream for blog to temporary file...");
            }
            // create the BlobDomain datatype to store the data in the db
            blobDomain.setInageBlob(new BlobDomain());
            // get the outputStream for hte BlobDomain
            out = blobDomain.getDataBlob().getBinaryOutputStream();
            // copy the input stream into the output stream
            logger.info("... copy data to BlobDomain ...");
            /*
             * IOUtils is a class from the Apache Commons IO Package (http://www.apache.org/)
             * Here version 2.0.1 is used
             * please download it directly from http://projects.apache.org/projects/commons_io.html
             */
            IOUtils.copy(in, out);
            logger.info("... Finished OK");
        } catch (Exception e) {
            logger.severe("Error!", e);
            if (tempfile != null) {
                // delete temp file on exception but don'T throw one if there is another exception
                logger.info("Deleted temporary file " + tempfile.getAbsolutePath());
                FileUtils.deleteQuietly(tempfile);
            }
        }
        // return the filled BlobDomain
        return blobDomain;
    }

Depending on the new boolean parameter passed to the method a temporary file is created and the uploaded data is first saved to the temporary file. After that the data is copied from the temporary file into the BlobDomain. At this point the path to the temporary file is saved in the new class for later reference. In case of an exception the temporary file is removed.
Finally in line 22 and 23 of the value change listener we check if a temporary file was generated and we set the path to it to a pageDef variable (see Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef). For this we use the code below.

    /**
     * Set the temporary file name into a page variable for later use
     * @param name
     */
    private void setTemporaryFileVar(String name) {
        // set pathto temporary file to page variable
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("TemporaryFile1");
        if (attr != null) {
            attr.setInputValue(name);
        }
    }

The variable is used in the af:image component in the editImage.jsff fragment

                   <af:image source="/render_image?id=#{bindings.ImageId.inputValue}&tmp=#{bindings.TemporaryFile1.inputValue}" id="i1"
                              shortDesc="#{bindings.ImageName.hints.tooltip}" inlineStyle="width:200px;" partialTriggers="cb3" visible="true"/>

here the path to the temporary file is passed to the servlet as second parameter ‘tmp’. In lines 24-27 of the value change listener we send a ppr to the parent component of the af:image to show the now uploaded image.

Another thing to do is to cleanup after the user either cancel or commit the changes. This is done in the cancel_action() or the commit_action() in the ImageBean. Here we call the deleteTemporaryFile() method which checks the existence of a temporary file and deletes it.

    /**
     * delete the temporary file if is present
     */
    public void deleteTemporaryFile() {
        String tempfile = getTemporaryFileVar();
        removeTemporaryFile(tempfile);
        setTemporaryFileVar(null);
    }

3) The final part of the implementation is done in the servlet which is used to get the data back to the client. This is simple as we read the second parameter passed to the servlet. If it’s not empty we always read the image data from the temporary file. If the parameter is empty the servlet gets the data by reading the row from the DB and read the data from the blob. Here are the relevant parts from the servlet:

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        StringBuilder sb = new StringBuilder(100);
        String appModuleName = "de.hahn.blog.uldl.model.facade.ULDLAppModule";

        sb.append("ImageServlet ").append(appModuleName);

        try {
            // get parameter from request
            Map paramMap = request.getParameterMap();
            oracle.jbo.domain.Number id = null;
            String tmporaryFilePath = "";
            if (paramMap.containsKey("id")) {
                String[] pVal = (String[]) paramMap.get("id");
                id = new oracle.jbo.domain.Number(pVal[0]);
                sb.append(" id=").append(pVal[0]);
            }
            // check if we find a temporary file name. In this case we allways use this!
            if (paramMap.containsKey("tmp")) {
                String[] pVal = (String[]) paramMap.get("tmp");
                tmporaryFilePath = pVal[0];
                sb.append(" tmp=").append(pVal[0]);
            }

            OutputStream outputStream = response.getOutputStream();
            InputStream inputStream = null;
            BlobDomain image = null;
            String mimeType = null;
            // no temporary file path given, read everything from DB
            if (tmporaryFilePath.isEmpty()) {
                // get method action from pagedef
                BindingContext bindingContext = BindingContext.getCurrent();
                DCBindingContainer amx = bindingContext.findBindingContainer("de_hahn_blog_uldl_view_image_dummyPageDef");
                JUCtrlActionBinding lBinding = (JUCtrlActionBinding) amx.findCtrlBinding("getImageById");
                // set parameter
                lBinding.getParamsMap().put("aId", id);
                // execute method
                lBinding.invoke();
                // get result
                Object obj = lBinding.getResult();
                ImageAccessViewRow imageRow = (ImageAccessViewRow) obj;

                // Check if a row has been found
                if (imageRow != null) {
                    // Get the blob data
                    image = imageRow.getImageData();
                    mimeType = imageRow.getContentType();
                    // if no image data can be found and no temporary file is present then return and do nothing
                    if (image == null) {
                        mLogger.info("No data found !!! (id = " + id + ")");
                        return;
                    }
                    inputStream = image.getInputStream();
                } else {
                    mLogger.warning("No row found to get image from !!! (id = " + id + ")");
                    return;
                }
                sb.append(" ").append(mimeType).append(" ...");
                mLogger.info(sb.toString());
            } else {
                // read everything from temporary file path
                mimeType = ContentTypes.get(tmporaryFilePath);
                File file = FileUtils.getFile(tmporaryFilePath);
                FileInputStream fileInputStream = FileUtils.openInputStream(file);
                inputStream = fileInputStream;
            }

            // Set the content-type. Only images are taken into account
            response.setContentType(mimeType + "; charset=utf8");
            IOUtils.copy(inputStream, outputStream);
            if (tmporaryFilePath.isEmpty()) {
                // cloase the blob to release the recources
                image.closeInputStream();
            }
            inputStream.close();
            // flush the outout stream
            outputStream.flush();
        } catch (Exception e) {
            mLogger.log(Level.WARNING, "Fehler bei der Ausführung: " + e.getMessage(), e);
        } finally {
            mLogger.info("...done!");
        }
    }

The gallery below shows the new work flow.

The work space for part 5 can be downloaded from the ADF EMG Sample side BlogUploadDownload_12.1.3V4.zip.
Or if you are in GIT you can get the work space from GitHub BlogUploadDownload_12.1.3V4

Lazy Initalizing Beans

A questions on the OTN JDeveloper & ADF forum asked how to initialize attributes with data in a bean before showing the attributes.
The first solution which comes to mind is to initialize attributes and data in the constructor of the bean. This however is not a wise thing to do as you can’t predict when the constructor is called and in which phase of the life cycle you are (as it depends on the scope of the bean).
Here you can use a solution I call lazy initializing the data of a bean. It’s based on the assumption that once the page tries to get an attribute from the bean, the life cycle already has done all other initializations (like bindings). Now if we defer the init of the bean data until a (or the first) getter is called we are save.
To implement this we set an attribute to null and check if it’s null in the getter of the attribute. If we find it’s still null, we init the data and set the attribute to a not null value. Here is the sample code:

package de.hahn.blog.lazyinitbean.view.beans;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import javax.servlet.ServletContext;

import oracle.adf.model.BindingContext;
import oracle.adf.share.logging.ADFLogger;

import oracle.binding.AttributeBinding;
import oracle.binding.BindingContainer;

public class LazyInitBean {
    private static transient final ADFLogger LOGGER = ADFLogger.createADFLogger(LazyInitBean.class);

    private String myName;

    public void setMyName(String myName) {
        LOGGER.info("set data: " + myName);
        this.myName = myName;
    }

    public String getMyName() {
        // lazy init the data only when it's null
        if (myName == null) {
            LOGGER.info("init data through layz init");
            initData();
        }
        return myName;
    }

    public LazyInitBean() {
        LOGGER.info("LazyInitBean: c'tor");
    }

    private void initData() {
        LOGGER.info("data intialized");
        // this method inits the beans attributes (only one here)!
        myName = "just init myself";
        //Get ServlerContexct
        FacesContext ctx = FacesContext.getCurrentInstance();
        ServletContext servletContext = (ServletContext) ctx.getExternalContext().getContext();
        // get the binding container
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("myTestValue1");
        if (attr != null) {
            String old = (String) attr.getInputValue();
            attr.setInputValue("NEW DEFAULT VALUE");
            LOGGER.info("LazyInitBean: setnew default value to 'NEW DEFAULT VALUE' old: " + old);
        } else {
            LOGGER.info("LazyInitBean: bindings not present!");
        }
    }

    /**
     * Force the init of the beans attributes
     */
    public void resetData() {
        LOGGER.info("LazyInitBean: reset!");
        // setting the myName to null causes a re initialization
        myName = null;
        // you can call initData() here too;
        initData();
    }

    public void buttonListener(ActionEvent actionEvent) {
        LOGGER.info("Action initData");
        resetData();
    }
}

Now, whenever in a page or fragment the getter to the bean parameter is called (this can be the getter to every property of a component which can have EL) the bean is checkes if the attribute is already initialized and if not calls the initData() method in the bean. If a value is present, the getter returns the value. The initData() method show that attributes in the binding layer can initialized this way too. This is shown with the myTestValue1 attribute which is defined as pageDef variable. You can overwrite the text in the inputText and when you click the button ‘Clear Data’ the data is initialized again removing the data from the inputText and setting it back to the initial values.
Below is a sample of a fragment which uses the bean to lazy initialize the attribute.

<?xml version='1.0' encoding='UTF-8'?>
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:panelGridLayout id="pgl1">
        <af:gridRow height="100%" id="gr1">
            <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                <!-- Content -->
                <af:panelGroupLayout id="pgl2" layout="vertical">
                    <af:outputText value="Lazy Region" id="ot1" inlineStyle="font-size:large;"/>
                    <af:inputText label="Name:" id="it1" value="#{backingBeanScope.LazyInitBean.myName}" partialTriggers="b1 b2"/>
                    <af:outputText value="Hello: #{backingBeanScope.LazyInitBean.myName}!" id="ot2" partialTriggers="b2 b1"/>
                    <af:button text="ShowMessage" id="b2"/>
                    <af:button text="Clear Data" id="b1" actionListener="#{backingBeanScope.LazyInitBean.buttonListener}"/>
                    <af:inputText label="MyTestValue" id="it2" value="#{bindings.myTestValue1.inputValue}" partialTriggers="b1"/>
                </af:panelGroupLayout>
            </af:gridCell>
        </af:gridRow>
    </af:panelGridLayout>
</ui:composition>

Be aware of the fact, that this solution depends on the component(s) which calls the getter which in due course init the data. As the getter methods of the components are called in the order they appear in the component tree you have be careful which getter you use. It should be the first if you want to setup everything up front, but use the last if you want to load data at the end of the page render cycle (e.g. to get more data from a web service but already see the page).

In a comment Aino Andriessen mentioned that you can use the @PostConstruct annotation on a method which then will be called whenever the bean is constructed after all other injections are done. That is the difference to lazy init method. The method is called every time after all other injections are done. If you only want to setup data if the data is really needed this is not possible this way. Nevertheless, to make the sample application show the @PostConstruct method too, I added a method postconstructMethod() in the bean, which writes a log message.

    @PostConstruct
    public void postconstructMethod() {
        LOGGER.info("PostConstruct Called!");
        // init everything here which can be done quickly and is needed to init UI components before showing them
        // or call initData() from here
    }

You can download the sample workspace from the ADF-EMG Sample Project page of get it from GitHub. The sample does not need a DB. The samples are updated to show the @PostConstruct technique.

JDev 12.1.3: Using Parent Action to Navigate Tabs of a af:panelTabbed from Inside a Region

This blog is based on a question in the OTN JDeveloper and ADF forum. The Question was how to navigate from one selected tab to the next tab when the af:showDetailItem in the tab is a region and the button to navigate is inside the region.

We implement two cases, the first is the easy one where the button to navigate is in the page holding the af:panelTabbed. The second one uses a button is inside a bounded task flow which is shown in the af:showDetailItem in a tab to navigate the af:panelTabbed.

We start with creating a new ‘ADF Fusion Web Application’ from the gallery. We only change the application name and the path of the application, otherwise we can just use the default values. The sample is simple and doesn’t need a model project or connection to a DB. You can download the finished workspace using the link provided at the end of the post.

We skip all the needed steps and going right into creating the starting page which holds the af:panelTabbed. It has three af:showDetailItem and a af:Button to navigate the tabs directly from the page. This button implements the first use case.

Start Page with Outer Navigation

Start Page with Outer Navigation

The button has a listener attached which is implemented in a viewScope bean ‘NavigateTabBean’. The listener implements the needed logic to navigate from the selected tab to the next tab. If the last tab is reached the first tab is selected.

    private static ADFLogger _logger = ADFLogger.createADFLogger(NavigateTabBean.class);
    private static final String PANELTAB = &quot;pt1&quot;;

    /**
     * Eventhandler to navigate to the next tab in a af:panelTabbed
     * @param actionEvent event which called the listener
     */
    public void naviGateButtonAction(ActionEvent actionEvent) {
        UIComponent ui = getUIComponent(PANELTAB);
        if (ui == null) {
            _logger.info(&quot;PanelTab component not found!&quot;);
            return;
        }
        if (!(ui instanceof RichPanelTabbed)) {
            _logger.info(&quot;Component is not an af:panelTabbed&quot;);
            return;
        }

        RichPanelTabbed rpt = (RichPanelTabbed) ui;
        int childCount = rpt.getChildCount();
        List&lt;UIComponent&gt; children = rpt.getChildren();
        for (int ii = 0; ii &lt; childCount; ii++) {
            UIComponent uiSDI = children.get(ii);
            if (uiSDI instanceof RichShowDetailItem) {
                RichShowDetailItem rsdi = (RichShowDetailItem) uiSDI;
                if (rsdi.isDisclosed()) {
                    //close current tab
                    rsdi.setDisclosed(false);
                    //calculate next tab to disclose as next_tab_index = (current_tab_index + 1) % number_of_tabs
                    int kk = ii + 1;
                    int jj = kk % childCount;
                    _logger.info(&quot;old disclosed tab: &quot; + ii + &quot; new disclodes tab: &quot; + jj);
                    RichShowDetailItem newSDI = (RichShowDetailItem) children.get(jj);
                    //open new tab
                    newSDI.setDisclosed(true);
                    AdfFacesContext.getCurrentInstance().addPartialTarget(rpt);
                    return;
                }
            }
        }
    }

    // find a jsf component
    private UIComponent getUIComponent(String name) {
        FacesContext facesCtx = FacesContext.getCurrentInstance();
        return facesCtx.getViewRoot().findComponent(name);
    }

    public void nextTab() {
        naviGateButtonAction(null);
    }

The logic in the action listener first searches for the af:panelTabbed in the viewRoot and gets the number of children from it. Each child is one of the af:showDetailItem representing a tab. Then we iterate over the child list and search the currently disclosed tab. We close this tab and the next tab in the list gets disclosed. If the currently selected tab is the last in the list, the first tab is disclosed (see the comments in the code section).

To Implement the second use case, the one we really want to talk about, we first need to implement three bounded task flows which we later use as regions in the tabs.

Bounden Task Flow with Parent Action

Bounden Task Flow with Parent Action

The image shows the bounded task flow for one tab. The other bounded task flows are build in the same way and are just showing different text. The reason for this is that you normally would use different regions aka different task flows in the tabs. We could have used only one bounded task flow with a parameter to change the text shown in the fragment. In the sample you’ll find this implemented for tabs 4 and 5.
The region is simple and only shows one fragment which has a button to navigate to the next tab and a test to distinguish the regions when navigating. The whole magic is the parent action in the bounded task flow. This parent action executes a navigation case ‘nextTab’ in the parent task flow.

Unbounded Task Flow with Start Page

Unbounded Task Flow with Start Page

In the image above we the the unbounded task flow which is the parent of the bounded task flow. Here a wild card rule navigates to a method call activity ‘selectNextTab’ using the navigation case ‘nextTab’ we entered to the parent action of the regions.
The method action calls the ‘nextTab()’ in the managed bean from the code section above. All this method does is to call the action listener which is called from the af:Button of the start page (Start.jsf). As the action listener needs an ActionEvent as parameter, which we don’t use in the code we pass ‘null’ when we call the listener from the method call activity.

This concludes the implementation. Here are some images from the running application

The sample application can be downloaded form ADFEMG Sample Project.

A version of the software build with JDeveloper 11.1.1.7.0 can be downloaded from GitHub

JDev 12.1.3: Use Default Activity Instead of the Deprecated Invoke Action

Since JDeveloper 12.1.3 the invoke action used in earlier version has been deprecated. Users still using the old invoke action to load data on page load should migrate their code to using the default activity in a bounded task flow instead. This article describes how to use the executeWithParams method as a default activity in a bounded task flow (btf) to load data to be shown in a region. For this we implement a common

Use Case:
in a text field the user enters a string which should be used to look-up data in the DB and show the data as a table in a region.
For this we use the HR schema and build a look-up for locations after the name of the city of the location. In a page the user can insert the name or part of a cities name into a text field. This input is send as parameter to a bounded task flow. The default activity of the btf calls a method in the view object which uses a view criteria to search for cities starting with the given input data. In a second implementation the same technique is used but a where clause is used in the VO and the VO is called with executeWithParams. The result of the search is displayed as a table in a region.

Implementation

Model Project:
We start by creating a new ‘Fusion Web Application’ and creating a model project of the HR DB schema. Here we only use the location table for which we create entity object and view object.
Now we create the view criteria which we use to find locations by part of the city name.

Next step is to create the java class for the view object including the method to safely access the created bind variable. In the class we add a method to apply the created view criteria which we expose in the client interface well as the methods to access bind variables.


Finally we have to make sure that the locations view object is part of the data model of the application module.
Resulting Application Module Data Model

Resulting Application Module Data Model


Next we add another view object to the data model which we use to implement the use case a second time. This time we use the view criteria we defined in the view object LocationsView and select it as the default where clause.

ViewController Project:
We start implementing the view controller project by first adding a start page, ‘Start’, to the unbounded task flow in adfc-config.xml. For this page we use a quick layout (One Column, Header stretched).

After opening the page (which creates it) we add a third grid row to the panelGridLayout we got from the quick layout which later holds the result table. In the first grid row we add a captain for the page, ‘Execute with param sample’, the second grid row we add an af:inputText which holds the users input for the city name to search for.
The page looks like

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="Start.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <!-- Header -->
                        <af:outputText value="ExecuteWithParams Test" id="ot1" inlineStyle="font-size:x-large;"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="50px" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <!-- Content -->
                        <af:inputText label="City" id="it1" value="" autoSubmit="true"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow id="gr3">
                    <af:gridCell id="gc3">
                        <!-- REGION HERE -->
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

Now we create a pageDefinition for the page, where we define a variable and an attribute binding which holds the users input into the inputText we added to a grid row below the header.


The final inputText look like

<af:inputText label="City" id="it1" value="#{bindings.searchCityName1.inputValue}" autoSubmit="true"/>

As you see we set the autoSubmit property to true as we don’t have (and need) a button to submit the data to the binding layer.

The next task is to create a new bounded task flow which has one input parameter, which is used to search for locations with cities starting with the given parameter from the inputText component.

Once the bounded task flow is created we can drag this btf onto the start page and drop it in the girdCell in the third gridRow and wire the parameter for the task flow to the value we have stored in the in the variable iterator via the inputText.

Finally we make the region refresh whenever the inputParamter of the task flow changes by setting the regions refresh property to ‘ifNeeded’.
The final ‘Start’ page layout looks like

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="Start.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <!-- Header -->
                        <af:outputText value="ExecuteWithParams Test" id="ot1" inlineStyle="font-size:x-large;"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="50px" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <!-- Content -->
                        <af:inputText label="City" id="it1" value="#{bindings.searchCityName1.inputValue}" autoSubmit="true"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow id="gr3">
                    <af:gridCell id="gc3">
                        <af:region value="#{bindings.showlocatiobycitybtf1.regionModel}" id="r1"/>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

This concludes the first implementation and we can run the application

The sample application can be downloaded form ADFEMG Sample Project. It contains a second page (Start2) which uses the other view object (LocationsWithParamsView) inside the region. It’s build like the first version. The difference is that the default activity nor is the executeWithParams from the VOs operations instead the self implemented method from the VO. You spare writing the method and exposing the method in the client interface this way.
Be aware that the sample uses the HR DB schema and you have to change the connection information to point to your DB.

JDeveloper 12.1.3: Using File Templates

Since JDeveloper 12.1.2 Oracle added a feature called ‘File Templates’. Only there was absolutely no documentation for this. We only had the ‘File Templates’ node in the preferences. I filed a bug (ADFEMG-150) for this.
In the current JDeveloper 12.1.3 release the ‘inital’ documentation for the ‘File Templated’ has been added.
To get to the ‘File Templates’ via the Tools->Preferences->’File Tempalates’ and see the below image.

File Templates in Preferences

File Templates in Preferences


Loading the extension you get
FileT emplates Inital Dialog

File Templates Inital Dialog


The only help I found can be reached by clicking the Help button in the dialog which will get you
File Templates Help

File Templates Help


This is not much to work with. I you find more help on using this feature, drop me a note, please. All I present in this blog was found out using try and error. There seams to be a difference if you use a Windows or Unix based machine. Later in the blog I’ll give more info on this.

Let’s start with my findings. Clicking through the tree shown in the image above you quickly get the intention of the file templates. This feature allows us to define template for files (e.g. java classes) which can be used to define repeating code and generate the files from it. The sample which is provided by Oracle builds a template for a java class containing copyright and licence information.


However, there is no button or menu to create your own template from scratch. The pencil icon in the top right corner is disabled for each of the files. The only way to create a new template file is to copy an existing one by using the ‘Copy’ button. The first time you click the copy button for one of the files in the you get a note that you can’t change the files in place:
Confirm Modify Template

Confirm Modify Template


confirming the dialog with yes, a copy the content of the sample template from the jar will be created into a file you can edit. All custom template files are written to system12.1.3.0.41.140521.1008\o.jdeveloper.filetemplate\ folder by default. While copying the files you can change the ID, description, the Name and the file name of the template.

Please not the properties in the dialog which allows to define where you later find the template in the gallery.
Once we have copied all files from the tree we can start to modify them. The file suffix of the templates is ‘ftl’. The copied files are automatically opened in the JDev editor. Here comes the first glitch in the extension. If you change *.ftl files in the JDev editor and hit the ‘Save All’ button, the files are not saved. The caption of the tabs remains italic, meaning that the content of the tab has changed and is not saved. You need to hit the ‘Save’ button to save each file by itself.
Once the templates are copied into an accessible path, you can use the ‘Pencil’ button to open them again in the JDev editor, or you can make small changes directly in the editor window in the dialog.
Looking at the content of the files you see that ‘copyright-hahn-java.ftl’ inludes ‘license-hahn-txt.ftl’ which then includes ‘copyright-hahn-content.ftl’. This is the first file we change to change the copyright notice
Changed Copyright Text

Changed Copyright Text


To make the licence ‘license-hahn-txt.ftl’ pick up the changes copyright text we have to change the include statement in the licence template
Change Include  of Copyright in Licence Template

Change Include of Copyright in Licence Template


and finally we have to change the ‘copyright-hahn-java.ftl’ to pickup the changes licence template
Change Include of Licence in Template

Change Include of Licence in Template


After saving all changes (remember to save each file separately) we can test the template. For this we select a package in a project, right click the package we want the file created in and select ‘New’->’From Gallery…’
Select 'New'->'From Gallery...'

Select ‘New’->’From Gallery…’


When we save the java call template we saved the information where the template shows up via the ‘Category’, ‘Folder’ and ‘Feature’ properties of the template dialog (these properties define where the template shown up in the gallery). Select ‘Java’ and you should see
Select the Template from the Gallery

Select the Template from the Gallery


Please notice the name which is the ID we chose for the template and the description which comes from the save dialog too. Select the template and you get the file save dialog
File Save Dialog

File Save Dialog


In the JDev editor you get the generated java class
Generated Class from Template

Generated Class from Template


Please note the error in line 26 of the generated java class

package de/hahn/blog/test1213/.model/adfbc/;

should be

package de.hahn.blog.test1213.model.adfbc;

This only happens on Windows machines, Unix machines generate the right package statement. You have to correct this yourself. I’ll file an bug this.

Now that we have seen this sample, let’s ask what’s missing.

    1) Documentation: if you look at the templates you seethe usage of variables in the templates like ‘${name}’ or ‘${package}’, however, I could not find if there any other valuables as the once you see in the sample. Same is true for the language behind the template mechanism. The sample show some kind of ternary operator, question is what other options do we have. The file suffix ‘.ftl’ and some other information I gathered for this blog post, suggests that the templates are using a tool named ‘FreeMarker’.
    2) Use cases: I would like to use templates at other points like java classes generated for ViewObjectImpl, EntityObjecImpl or ApplicationModuleImpl. This is not possible right now. It would be handy e.g. to generate files with ADFLogger directly implemented. You can insert the ADFLogger into the class using the template, but as this is only done for classes you create via the template most of the other generated classed need to be changed by hand.
    3) applying the template to an existing class is not possible. If you like a copyright statement in each class you can’t attach it from a template.