JDeveloper: executeWithParams Problem: bind variable setters are not called

In one of my current bigger ADF projects (yes, there are still big ADF projects!) I had a problem with the ‘executeWithParams’ operation binding called from a bean. To understand the problem I first give a brief description.

Problem

We have a special string column in some of our VOs. For this column, there is a bind parameter which is added to the where clause of the VO. Getter and setter for this bind parameters are generated to be able to set this parameter safely. In the setter method, some checks are done and special formatting is applied to the passed value.

Now we used the VOs executeWithParams operation to create a search form. Hitting the button to execute the query gave us a wrong result. As it turned out, that the execWithParams operation passes the parameters to the VO but it doesn’t use the getter/setter methods for the parameter. The parameters are directly set into the slots by the VariableManager.

Solutions

We want to transform or convert the given parameter right in the query. This way the developer doesn’t need to think about or to remember that there should be a transformation of the parameter. A central solution is preferred. Two solutions come to mind.

Expose a method in the VO

One solution would be to create a method in the VO and expose this method in the client interface of the VO. Then this method is visible in the binding layer and you can use call the method instead of using executeWithParams.

This works OK, but if a developer uses the executeWithParams method, you’ll still don’t get the right result.

Change the behavior of the executeWithParams method

I looked for a direct hook into the executeWithParams method but did not find one 😦

However, it turned out that the executeWithParam method calls the executeQuery method of the VO. At this point, the parameters are all set. This is done in the background via the VariableManager used in VOs to keep track of the bind parameters and their different type. There are where clause parameters and parameters used in view criteria. The latter are handled differently but this is not essential for this solution. All we need to know is that the parameters are correctly set when the executeQuery method is called.

The solution is to overwrite the executeQuery method of the view object (or use a base class) and check the parameters before executing the query.

Implementing the solution

In an earlier blog “Dump VO query and it’s parameter with their values” I showed how to get to the parameters defined for a VO and dump their values. Instead of writing the values to the log file, we look for the parameter, change the value, if one is given and then execute the query with the now converted parameter.

I use reflection to check if a setter method for a parameter is defined in the VO. If this is the case, I call the setter with the value set by the user. This way the setter is called when the query is called via the executeWithParams method. The code to change the parameter has only to be written once in the setter.

Below is the code:

    @Override
    public void executeQuery() {
        _logger.info("executeQuery");
        transformHistoryParameter();
        dumpQueryAndParameters();
        super.executeQuery();
    }

    private void transformHistoryParameter() {
        VariableValueManager vm = ensureVariableManager();
        Variable[] variables = vm.getVariables();
        // check each parameter for this query
        for (Variable var : variables) {
            String varName = var.getName();
            try {
                String mName = "set" + varName;
                Class mvarType = var.getJavaType();
                Method m = this.getClass().getMethod(mName, new Class[] { mvarType });
                // check if method is a setter method
                if (isSetter(m)) {
                    Object val = vm.getVariableValue(var);
                    m.invoke(this, val);
                }
            } catch (NoSuchMethodException e) {
                // nothing to do if there is no setter defined
                return;
            } catch (IllegalAccessException | InvocationTargetException e) {
                // nothing to do if there is no setter defined
                return;
            }
        }
    }

    public boolean isGetter(Method method) {
        if (!method.getName().startsWith("get")) {
            return false;
        }
        if (method.getParameterTypes().length != 0) {
            return false;
        }
        if (void.class.equals(method.getReturnType())) {
            return false;
        }
        return true;
    }

    public boolean isSetter(Method method) {
        if (!method.getName().startsWith("set")) {
            return false;
        }
        if (method.getParameterTypes().length != 1) {
            return false;
        }
        return true;
    }

    public void dumpQueryAndParameters() {
        // get the query in it's current state
        String lQuery = getQuery();
        // Dump query
        _logger.info("---query--- " + lQuery);
        //get Valriables
        AttributeList attributeList = getNamedWhereClauseParams();
        String[] attributeNames = attributeList.getAttributeNames();
        if (attributeNames == null || attributeNames.length == 0) {
            _logger.info("--- No variables found");
        } else {
            _logger.info("---Variables:");
            for (int ii = 0; ii < attributeNames.length; ii++) {
                Object lObject = attributeList.getAttribute(ii);
                _logger.info("  --- Name: " + attributeNames[ii] + " Value: " +
                             (lObject != null ? lObject.toString() : "null"));
            }
        }
    }

Sample

The sample to see the code at work can be downloaded from GitHub BlogExecuteWithParamsProblem

It was implemented using JDeveloper 12.2.1.3 and the HR DB schema.

To make it easy, I used the HR DB schema and built a query on the EMPLOYEES table:

A forth bind variable is introduced in a view criteria

The transformation uses the pMail bind parameter and makes it uppercase.

    /**
     * Returns the bind variable value for pMail.
     * @return bind variable value for pMail
     */
    public String getpMail() {
        String val = (String) getNamedWhereClauseParam("pMail");
        _logger.info("value: " + val);
        return val;
    }

    /**
     * Sets <code>value</code> for bind variable pMail.
     * @param value value to bind as pMail
     */
    public void setpMail(String value) {
        _logger.info("value:" + value);
        String newVal = value;
        if (value != null) {
            newVal = value.toUpperCase();
            _logger.info("Value transformed to " + newVal);
        }
        setNamedWhereClauseParam("pMail", newVal);
    }

I know that it would be easy to without this code by just setting the ‘ignore case’ flag when defining the query, but this is just a sample of a change of a parameter.

The data model in the application module looks like

As you see I added a second instance of the EmployeesView and attached the view criteria to this instance. Running the application module in the tester (see JDeveloper & ADF: Use the Application Module Tester (BC4J Tester) to Test all your BusinessLogic)

We get

And the following log

Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl executeQuery
INFO: executeQuery
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl setpMail
INFO: value:ski
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl setpMail
INFO: Value transformed to SKI

Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl setpName
INFO: value: null
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl setpDate
INFO: value: null
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl dumpQueryAndParameters
INFO: —query— SELECT Employees.EMPLOYEE_ID, Employees.FIRST_NAME, Employees.LAST_NAME, Employees.EMAIL, Employees.PHONE_NUMBER, Employees.HIRE_DATE, Employees.JOB_ID, Employees.SALARY, Employees.COMMISSION_PCT, Employees.MANAGER_ID, Employees.DEPARTMENT_ID FROM EMPLOYEES Employees WHERE ( ( ( ( Employees.LAST_NAME LIKE ( :pName || ‘%’) ) OR ( :pName IS NULL ) ) AND ( ( Employees.EMAIL LIKE ( :pMail || ‘%’) ) OR ( :pMail IS NULL ) ) AND ( ( Employees.HIRE_DATE >= :pDate ) OR ( :pDate IS NULL ) ) ) )
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl dumpQueryAndParameters
INFO: —Variables:
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl dumpQueryAndParameters
INFO: — Name: pMail Value: SK
I
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl dumpQueryAndParameters
INFO: — Name: pName Value: null
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl dumpQueryAndParameters
INFO: — Name: pDate Value: null
Feb 08, 2019 3:05:38 PM de.hahn.blog.execwithparams.model.EmployeesViewImpl dumpQueryAndParameters
INFO: — Name: pSalary Value: null

We see, that executeQuery() is called, then setpMail is called and the value given in the UI is converted to uppercase. In the dump of the query, we see that the uppercase parameter is used for the query.

The same works with the other view object in the application module. Just try it out yourself. The sample has a small UI project allowing you to test it in the browser too.

Advertisements

Fasten your seat belts: Flying the Oracle Development Cloud Service (3 – Take Off – V1)

In part three of the series about the Oracle Developer Cloud we start working on a project as a member of a team in the developer cloud.

Before starting a new project some basic ground has to be covered. What architecture and technology should the project use as well as which package path to use. For the technology the the decision is easy as we want to use ADF. For the architecture we can choose on one of the patterns outlined at ‘Angles in the architecture’.

A good starting point is to introduce a for every ADF project, regardless of the architectural pattern, is a framework extension project (see ‘Extending a Helping Hand’). So we start with this too.

As a developer can’t create a new repository in a cloud project, we have to do this as a user with admin rights.


The first thing to note is that you should create an empty repository (unmark the ‘Initialize repository with README file’). If you initialize the repository with a README file, the developer can’t later just push his initial local version of the JDev workspace into the remote repository. The local repository has be updated with the README file first.

Now that the remote, empty repository exists we switch roles and work as a developer. For this we use a different login as a user who only has developer rights in the Oracle Developer Cloud.
Before the developer uses the new repository he creates a new workspace or project. We create a workspace for the framework extension library.


Next we add the ‘ADF Model Runtime’ library to the project and then the framework extension classes to the workspace.

Right now we don’t need to add or change any of the code in the created classes. If we later need to add some global functionality we come back to these classes. The next thing to do is to create an ADF library from the created classes

To make the new library available for other projects you can create a new file system connection using the same path we specified in the deployment descriptor

Later we come back to this step as we see that we have to change it a bit to make it work in the cloud. Right now we leaf it as is as this shows how you normally would do this in a normal project.
The next thing to do is to initialize a local GIT repository and push this to the Oracle Development Cloud repository as the initial master

and then push the local master branch to the Oracle Developer Cloud repository. For this we use the repository URL we get when we log into the cloud as the developer
Copy git repository address

Copy git repository address


Using this URL we push the local repository to the remote one

to finally see the changes in the cloud

More Decisions
With the basics covered we have to make another decision:
How to define the workflow for changes to make to the project sources.
Should all team members work on the trunk (called the mainline) or should each member use a branch to work on (called a feature branch). Both of these practices have their supporters and naturally opponents. The first is more CI like per definition. Feature Branches on the other side are not CI by the definition, as the code is not continuously integrated into the main line. This dispute is not for this post and may be not for this blog. Anyway, lets start with feature branching.
This allows us to show a feature of the Oracle Developer Cloud as it allows for code reviews which are mostly used if you work with feature branches, but can be used for the other practice too.

Feature: add build files
The feature we implement is to setup a build system for our framework extension project. We name the feature ‘feature-setup-build’


We learned in part 1 that the Oracle Developer Cloud provides a Continuous Integration server (CI). We plan to use this CI server to build our library whenever the code changes. For this we need to use ANT or Maven as the build system. For this project we choose ANT and can now build the needed build.xml files from the project

To finish this part we add the new files to our local repository and then push them to the remote as a new branch.

We push the local changes to the remote repository in the cloud using the same branch name

We had not looked into the created build.xml file or the build.properties files, we had them just created and pushed them into the repository. Question is, will they work?
Let’s try it on the local machine first.

Now we can run the ANT target ‘all’ which is the default one.
Well, as JDev 12.1.3 has somehow eliminated the ANT tool bar buttons running ANT on a project is a bit cumbersome (hopefully the ANT build buttons are back with the next release)

OK, this works like a charm.

As this post is already very long, we split the take off into two parts, V1 and ROTATE. This concludes part V1. Next time we make the necessary changes to the build files to integrate them to the clouds build system and start the CI process.

Note: for those who wonder about the terms V1 and ROTATE:
– V1 is the maximum speed at which an aircraft pilot may abort a takeoff without causing a runway overrun
– ROTATE or Vr is the speed of an aircraft at which the pilot initiates rotation to obtain the scheduled takeoff performance

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.