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.

Advertisements

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.