InputNumberSpinbox without Spin

ADF offers a wide range of components which allow user to input data. There is a build in intelligence which chooses the ‚right‘ component for the given data type when you create the UI from a data control. This allows e.g. to create a form to input data which e.g. covers the basic formatting and error handling of the data types for the given fields.

From my point of view, one wrong decision is to use the af:inputNumberSpinbox for Integer and BigInteger data types. Setting a bigger number using the spin boxes isn’t working for most people, at least not for me.

The spin buttons are of no real use in most cases. In some versions of JDev the buttons are skinned too small so that it’s hard to use them at all. There are some cases, when the range of numbers is minimal, where using the spin buttons is OK.

What I like about the component is the build in error handling if I try to input anything but a number

without doing anything to the component. This is the code used for the above image

<af:inputNumberSpinbox label="Spinnumber" id="ins2"
    value="#{bindings.myNumber21.inputValue}"/>

As you see there in nothing but the component, still we get the right error message.

You can get the same result by using a normal af:inputText with an included af:numberConverter, but you need to know how to do this:

<af:inputText label="Number in af:inputText" id="it1"
    <af:convertNumber type="number" id="nc1" pattern="0"/>
</af:inputText>

This doesn’t look identical but close enough. One difference to note is that the af:inputText starts the input on the left whereas the af:inputNumberSpinbox aligns the numbers to the right. You can change this too with setting more properties on the component.

For this I like to use the af:inputNumberSpinbox without the spin buttons.

To make the af:inputNumberSpinbox usable I get rid of the spin buttons:

The component works like hte one with the spin buttons but look like a normal inputText

This can be done by changing the skin. If you like it can be done globally or you define a custom skin class and add this class where you don’t want to see the spin butons:

.nospin af|inputNumberSpinbox::incrementor-icon-style {
    display: none;
}

.nospin af|inputNumberSpinbox::decrementor-icon-style {
    display: none;
}

The ‘.nospin‘ is the name of the custom style class you can use on the af:inputNumberSpinbox to turn the spin buttons off.

Here is the part of the page

<af:panelGroupLayout id="pgl2" layout="vertical" inlineStyle="padding-left:20px;">
    <af:inputNumberSpinbox label="Number" id="ins1" value="#{bindings.myNumber1.inputValue}" styleClass="nospin"/>
    <af:spacer width="10" height="30" id="s1"/>
    <af:inputNumberSpinbox label="Spinnumber" id="ins2"
        value="#{bindings.myNumber21.inputValue}"/>
    <af:spacer width="10" height="30" id="s2"/>
    <af:inputText label="Number in af:inputText" id="it1">
       <af:convertNumber type="number" id="nc1" pattern="0"/>
    </af:inputText>
    <af:spacer width="10" height="30" id="s3"/>
    <af:button text="Submit" id="b1"/>
</af:panelGroupLayout>

You can download the sample from GitHub BlogInputSpinBoxWithoutSpin. The sample was built with JDeveloper 12.2.1.1.0 but should work with other versions too. There is no DB used or needed to run the sample.

Advertisements

JDev 12c: Implementing SQL IN Clause in an ADF ViewObject Query or ViewCriteria (Part 1)

There have been numerous questions about how to implement a SQL IN clause in ADF using a viewCriteria since the begin of life of ADF. There are a couple of solutions e.g. using an SQL array type or a DB table to store the values of the IN clause.

I came up with another solution which was using Oracle DBs CATSEARCH function or even CONTAINS search index.

All those solutions are more or less complex and need some programming to implement.

The solution I present in this blog is easy and elegant. However, it has its limitations still. Anyway, for about 90% of the use cases, I know where you want to add an SQL IN clause it works perfectly.

Problem

The problem is that you can’t simply define an IN clause in a query or ViewCriteria like

Select * from Employees where employee_id in (:pListOfValues)

Using this select statement as a query for a view object will not throw an error, but it won’t get you the desired result.

If you use a String type parameter for ‘pListOfValues’, e.g. “100, 110, 200” the query in the VO would look like

Select * from Employees where employee_id in (“100,110,200”)

And return nothing as a result. It is easy to see why: the parameter is expanded as a string, not a list of numbers. If you think you could overcome this by converting the employee_id to a string it will still not work as “100” IN “100, 110, 200” still won’t work.

Solution

A very elegant way to solve this problem is to change the query or where clause to

SELECT * FROM TABLE
  WHERE COLUMN IN (
    SELECT regexp_substr(:pListOfValues,'[^,]+',1,level) FROM dual
      CONNECT BY regexp_substr(:pListOfValues,'[^,]+',1,level) 
        IS NOT NULL)

The work is done by the select statement in the IN clause, This statement will split a comma-separated string in a series of values like a sub-select. If you run

SELECT
    regexp_substr(:pListOfvalues, '[^,]+', 1, level)
FROM
    dual
CONNECT BY
    regexp_substr(:pListOfvalues, '[^,]+', 1, level) IS NOT NULL

In a SQL worksheet and pass ‘11,12,15,17’ as ‘pListOfValues’ you get

You can pass any comma-separated string e.g. “1, hello, 444, world” and get

The SQL builds an internal table and add the values delimited by a comma to it. This internal table can then be used in the IN clause of another SQL statement.

Finally, running a complete query we can search e.g. for the employees which have the ID 100 or 110, or 180 or 176

In part 2, I’ll show how to implement this kind of query in a ViewCriteria of a ViewObject by adding a custom operator.

JDev 12.2.1.3: Multi select component table filter

In this blog article, I show how to use a multi-select component as a filter in a table. The sample is based on an older sample from Frank Nimphius (98. How-to use multi select components in table filters). The sample was built for JDev 11g R1 and R2.

It works using 12c too, but you get deprecation warnings after the migration. A user on the JDeveloper & ADF forum asked if I could provide a sample running in 12c without the deprecation warnings.

I will only show how to rewrite the bean method which is called when the user enters one or more values in the filter. The remaining part of the original sample works without a change in 12c.

To better understand what I’m talking about I show some images from the original blog:

The image above shows the sample table. Below we see the multi-select component to filter for multiple departments:

Please read the original blog entry to understand how to build the UI. The remaining part of this blog covers how to build the custom query listener method.

Custom Query Listener

In the original sample the two methods

...
Map _criteriaMap = fqd.getFilterCriteria();
...
fqd.setFilterCriteria(_criteriaMap);

are used which produce deprecation warnings in 12c

Starting from JDev 12.1.3 you can’t use the criteriaMap from the FilterableQueryDescriptor. Instead, you have to first get the ConjunctionCriterion from the FilterableQueryDescriptor and get the map of Criterion from it. The map holds the filter criteria entered by the user.

As you don’t use the criteria directly, you can’t set it back after generating the filter fro the multi-select. You work with the Criterion instead.

The new Method looks like

    /**
     * Custom Query Listener.
     * Applies af:selectMany choice values to the table filter criterion
     * @param queryEvent
     */
    public void onEmployeeTableQuery(QueryEvent queryEvent) {
        //user selected values
        ArrayList<Object> departmentIdArray = null;
        FilterableQueryDescriptor fqd = (FilterableQueryDescriptor) queryEvent.getDescriptor();

        //current criteria
        ConjunctionCriterion conjunctionCriterion = fqd.getFilterConjunctionCriterion();
        Map<String, Criterion> criterionMap = conjunctionCriterion.getCriterionMap();
        Criterion criterion = criterionMap.get("DepartmentId");

        //Translate DepartmentId array list to OR separate list of values
        StringBuffer deptIdFilterString = new StringBuffer();
        AttributeCriterion adfcriterion = null;
        // flag we set only if the DepartmentId filter is set (to reset the selection later)
        boolean flagDepIdFilter = false;
        if (criterion != null) {
            adfcriterion = (AttributeCriterion) criterion;
            Object object = adfcriterion.getValue();
            if (object != null) {
                flagDepIdFilter = true;
                departmentIdArray = (ArrayList<Object>) object;

                for (int argIndex = 0; argIndex < departmentIdArray.size(); argIndex++) {

                    //You need to know what is the underlying data type you are dealing
                    //with for the attribute. If you are on 11gR1 (11.1.1.x) then this
                    //type is jbo.domain.Number for numeric attributes.
                    //
                    //If you are on 11g R2 (11.1.2.x) this could be oracle.jbo.domain.Number,
                    //Integer or BigDecimal. If you use 11g R2, check the View Object for the
                    //attribute data type

                    if (argIndex == 0) {
                        //first argument has no OR

                        //this sample used oracle.jbo.domain.Number for the
                        //DepartmentId attribute
                        Number departmentId = (Number) departmentIdArray.get(argIndex);
                        deptIdFilterString.append(departmentId.toString());
                    } else {
                        //any subsequent argument is OR'ed together
                        deptIdFilterString.append(" OR ");
                        Number departmentId = (Number) departmentIdArray.get(argIndex);
                        deptIdFilterString.append(departmentId.toString());
                    }
                }
                //for some reasons, if in a single value select case, the
                //filter breaks and an error message is printed that the
                //String representation of the single value isn't found in
                //the list. The line below fixes the problem for filter values
                //that are positive numbers
                deptIdFilterString.append(" OR -1");
                String departmentIds = deptIdFilterString.toString();
                adfcriterion.setValue(departmentIds);
                fqd.setCurrentCriterion(adfcriterion);
            }
        }


        // preserve default query listener behavior
        //#{bindings.allEmployeesQuery.processQuery}

        FacesContext fctx = FacesContext.getCurrentInstance();
        Application application = fctx.getApplication();
        ExpressionFactory expressionFactory = application.getExpressionFactory();
        ELContext elctx = fctx.getELContext();

        MethodExpression methodExpression =
            expressionFactory.createMethodExpression(elctx, "#{bindings.allEmployeesQuery.processQuery}", Object.class,
                                                     new Class[] { QueryEvent.class });
        methodExpression.invoke(elctx, new Object[] { queryEvent });

        //restore filter selection done by the user. Note that this
        //needs to be saved as an ArrayList
        if (flagDepIdFilter) {
            adfcriterion.setValue(departmentIdArray);
            fqd.setCurrentCriterion(adfcriterion);
        }
    }

From the FilterableQueryDescriptor we get the ConjunctionCriterion and from this the map of Criterion. This map holds all filter values entered by the user in the filter of the table. We retrieve the one for the ‘DepartmentId’ and check if the value for it is not null. In this case, the criterion holds an array of the selected DepartmentId. From this array, we build a new string where we use the ‘OR’ operator to concatenate the array values.

Once this string is built, we set it back to the Criterion and execute the original query listener

You can download the sample from GtHubBlogMultiSelectComponentFilterTable for inspection and/or testing. The sample was built using JDev 12.2.1.3 and uses the HR DB schema.

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.

JDev: af:panelList without bullet if no link is given

We all know that ADF components are well defined and have a lot of functions. However, what if we want to use a component but don’t like what we get out of the box from it?

The answer is easy most of the times as we can change the look of the component or its behavior to our needs. Sometimes the answer is not as straightforward, but still easy, as in this

Use Case

A user wants to have an af:panelList, showing bullets in front of each item in the list. The Items should be links to other pages. The problem part is that some of the links in the list should not be visible all the time. E.g. a user might not have to needed access right to some of the links.

Problem

When we use an af:panelList as is, we get the following look

From this setup

<?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="panelList.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:gridCell>
                </af:gridRow>
                <af:gridRow height="100%" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <af:panelList rows="5"> 
                            <af:link text="link 1" id="l1" destination="http://www.oracle.com"/>
                            <af:link text="link 2" id="l2" destination="http://www.oracle.com"/>
                            <af:link text="link 3" id="l3" destination="http://www.oracle.com"/>
                            <af:link text="link 4" id="l4" destination="http://www.oracle.com"/>
                        </af:panelList>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

Setting e.g. link 3 to not visible we get

So, seeing the bullet in front of the link isn’t what we are looking for. Using the rendered property instead of the visible property will give us

But the problem now is that the link can’t be simply brought back to the page without a full page refresh. That is one of the disadvantages of using the rendered property. Once a component is not rendered, you need to do a full page refresh to get it back. A partial page refresh won’t work.

If you want to show some white space for the missing ‘link 3’ you can’t use the rendered property at all. Putting a spacer between ‘link 2’ and ‘link 4’ you end up with the same image as you get for using the visible property.

Solution

One solution is to omit the bullet in front of the links, which is automatically generated by the component. If there is no bullet, we’ll get

So, still not what we really want.

The final part is how to get the bullet back in front of the visible links. Easy, as the af:link component has an icon property where can specify an image we use as a bullet. The final page looks like

You can use any other image as an icon to show in front of the link. The missing part is how we got rid of the original bullet from the af:panelList. Simply by using a style class, we defined in a skin file and applying it to the af:panelList

@charset "UTF-8";
/**ADFFaces_Skin_File / DO NOT REMOVE**/
@namespace af "http://xmlns.oracle.com/adf/faces/rich";
@namespace dvt "http://xmlns.oracle.com/dss/adf/faces";

.ivi af|panelList {
    list-style-type: none; 
}

.ivi af|panelList::item {
    list-style-type: none; 
}

And using this page

<?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="panelList.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">
                        <af:outputText value="PanelList with Bullet" id="ot1" inlineStyle="font-size:x-large;"/>
                        <!-- Header -->
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="100%" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <af:panelList rows="5" styleClass="ivi"> 
                            <af:link text="link 1" id="l1" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                            <af:link text="link 2" id="l2" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                            <af:spacer width="10" height="10" id="s1"/>
                            <af:link text="link 3" visible="false" id="l3" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                            <af:link text="link 4" id="l4" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                        </af:panelList>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

You can download the sample which was built using JDev 12.2.1.3 from GitHub BlogPanelList. The sample doesn’t need any DB connection or model project.

JDeveloper: Task Flow with optional Parameters

In one of my current projects, I came across a wired problem concerning a task flow which uses parameters to configure the flow. To make it simple to understand the problem I made up a fictive use case.

Use Case

This is not a real use case, but an abbreviation of it just to show the problem and how to resolve it. It might not make sense in real life, but it allows to show the problem.

We like to see a form to enter some data. One of the fields the user can enter should be used to select a specific layout of the following pages. Ony Layout should be horizontal and one should be vertical.

The layout should allow showing a title with a number, a text part, and a footer part. Each of the parts is optional, only the layout must be specified.

The flow can be used at different points in different flows in the application.

Implementation

The implementation is easy. We build a task flow, build with pages, and add 5 parameters to it. This task flow looks like this:

In a router component, we check the required parameter to decide which layout to use. The ‘none’ page is used if the parameter is not ‘v’ or ‘h’.

For the layout pages, which implement the layout part, I used a af:panelGridLayout with the right number of rows and columns. Here is the vertical.jsf page

Or the source view

The other pages can be found in the sample which was built using JDeveloper 12.2.1.3. You can download the sample using the link at the end of the blog.

Now that we have the reusable task flow we need another task flow (adfc-config.xml in this case) to show how to use the use of the task flow.

We see an index page which calls the task flow using three different navigations (toPageNoParam, toPageAllParam and toPageTextOnly). The difference between the navigations are the parameters set to the task flows.

Here they are

Running the application we get the following output after filling in the form on the index page

WAIT, this doesn’t look right. The page with text only parameters displays the text in the footer section instead of the text section. If we look at the parameters defined for this task flow call we see

Yes, we added the value to the wrong parameter, ‘footer’ instead of ‘text’!

Easy change, we copy move it over to the ‘text’ parameter using copy and paste:

And we get

Great, this looks like it should be.

Problem(s)

WAIT again, in the log window we now get an error message

<oracle.adf.model> <ValueMappingXmlImpl> <parse> 
   <ADFc: /WEB-INF/adfc-config.xml: Failed to parse element input-parameter: null value found for value.>

Why’s that?

If you look closely at the parameters, you’ll notice, that the ‘footer’ value is empty, whereas the other not set values showing a ‘-’. So we add the ‘-’ to the footer parameter:

To get this output running the app:

The error message in the log window is gone 🙂

Hm, but now we see the ‘-’ for the footer value. The value for the ‘title’ parameter looks identical to the parameter ‘footer’. However, we don’t see the ‘-’ for the ‘title’ in GUI.

Solution

Looking at the XML of the task flow call shows:

Now the problem is, that JDeveloper shows a ‘-’ for a parameter which is not set in the XML representation, meaning that the parameter is not in the XML structure at all!

The ‘-’ we typed into the ‘footer’ parameter is visible as value for the parameter and thus it printed when running the application.

The final solution is to remove the ‘footer’ parameter from the XML structure:

This will get us the following display in JDeveloper

The resulting running page now looks like

And we don’t see the error message in the log.

Sample Download

You can download the final application from GitHub BlogTaskFlowParameter. The sample was built using JDeveloper 12.2.1.3.0 and doesn’t need a DB connection.

JDeveloper 12c: Save IDE Window Positions

A question on the ODC JDeveloper and ADF space about how to save a specific arrangement of the editor windows came caught my attention. After thinking about this a moment I could not think of any configuration to do this. ‘Reset Windows To Factory Setting’ is not what was asked for.

Idea

I wanted to figure this out and came up with the idea to make a snapshot of the current running JDeveloper folder, then change the editor window settings, take another snapshot and then compare them.

Solution

I did this with my current installation of JDeveloper 12.1.3.0.0, but it should work the same way in other JDeveloper versions. After comparing the two snapshots using KDiff3 it turned out, that the information about the size, position, and visibility of the editor windows are stored in a subfolder of the system12.1.3.x.xx.xxxxxx.xxxx folder named ‘system_cache/config/Preferences’.

This folder contains many subfolders holding information about your preference settings. To save the data make a copy of this folder and store it somewhere. If you messed up the IDE you can restore the windows by removing the current ‘system_cache/config/Preferences’ folder and restore it from the saved one.

Attention

Before you restore the settings you should close JDeveloper and make another copy of the current ‘system_cache/config/Preferences’ folder.

Adding missing extensions to JDeveloper 12.2.1.3

The current version of JDeveloper 12.2.1.3 is missing some extensions which are popular in the older version of JDeveloper. One of them, which is asked for a couple of times, is the MAF extension. Other extensions like ‘BI ADF ViewRegions’ are missing too.

Here are the images from the update page of JDeveloper 12.2.1.3

As you see, there is no ‘Mobile Extension’.

In general, you have two options to get the missing extensions installed:

  1. Download the extension from the ‘Extension Exchange’ and install it from a local file
  2. Add the ‘Extension Exchange’ to the known sources of extensions and load the extension right from JDeveloper

Personally, I prefer the second way as it only shows the extensions available for your version of JDeveloper. You can only install extensions which are configured for your version anyway.

Extension Exchange on the WWW

You’ll find all extensions available for all version of JDeveloper on the public ‘Extension Exchange’ at

http://www.oracle.com/technetwork/developer-tools/jdev/index-099997.html

From there you can download an extension and install it in JDeveloper from the local file. When you download an extension make sure to load the right version as you can only install extensions which are configured for your version (check the min and max version of the extension!).

Once you have an extension downloaded you can install it from this local file. As a sample show images how to install a MAF extension from a local file. As a sample, we’ll use JDeveloper 12.2.1.3.

Download the extension to your local file system and remember the folder you saved the file to. In JDeveloper go to menu ‘Help’->’Check for Updates’. Select ‘Install From Local File’ and search the file downloaded

Click finish and the extension gets installed.

If you try to install an extension which is not compatible to your JDeveloper version you get

So make sure you download the right version of an extension you like to install.

URL to add the ‘Extension Exchange’

To avoid the hassle of selecting the right version, let JDeveloper do this work for you. We simply add the URL of the ‘Extension Exchange’ to the ‘Update Centers’ and JDeveloper will search for extensions compatible to your JDeveloper version.

The URL we have to add is:

http://www.oracle.com/webfolder/technetwork/jdeveloper/downloads/1213center.xml

Open ‘Help’->’Check for Updates’ again and click the ‘Add’ button

Enter a name for the URL and the URL itself and click OK. Now, after clicking ‘Next’ you’ll get

and can select the ‘MAF Extension’ or any other missing extension right from the dialog. The installation is identical to the one shown in the ‘Extension Exchange on the WWW’ section.

JDev 12.2.1.3: Creating a shared skin jar (Part 2)

In part 1 or the series we created a simple skin, built an ADF-Library from it and tried to reuse it by deploying it to a WebLogic Server. This approach failed. In this part, we try another option to share a jar with

Sharing the skin with other applications

To share the skin with other application we can

  1. Create an ADF Library
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server
  2. Create a normal jar
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server

For this blog the way we want to use if 2b. This allows to create the skin once, deploy it to a server and use it in every other application. In the next paragraph, we try out option 1a to show the problems when reading resources from a jar file.

Using a shared skin

Option 2b

Here we create a jar file containing the skin and additional resources like images and deploy it directly to a WebLogic Server as a shared library. The advantage is, that other applications can use the skin and other resources directly and that the jar can be versioned to allow different versions of the same jar on the server.

Option 1a which we discussed in the previous chapter doesn’t work for images. However, the documentation ‘Deploying a Custom Skin File in a JAR File’ and Frank Nimphius pointed it out in e.g. 86. Reading boilerplate images and icons from a JAR or How-to share skin definition files across applications how the jar file must be structured to allow the resource servlet to read the resources. The essential sentence is

‘All image resources and CSS files must also be under the META-INF directory.’

In his article, Frank suggested using the command line jar tool to create the jar. I show how to use JDev to create the jar with the needed structure and how to deploy it to a server as a shared library.

A sample application is used to use the skin and to show an image load from the jar.

The building plan for a skin in a shared Library which can be deployed to a WebLogic server is given in the article as:

To implement the shared library approach, developers need to change their existing skin definition so it can be deployed in a JAR file. The steps for this include

– Creating a META-INF directory – Creating a trinidad-skins.xml file that defines the skins deployed with the JAR file

– Creating an META-INF/adf sub directory for images and icons served from the JAR file

– Changing the image reference in the CSS to include the “adf” directory, which makes sure images and icons are handled by the ADF Faces resource loader, which can read resources from JAR files

– JAR the META-INF directory to create the library file

Looking at the current project for the skin we see a different layout

In the article Frank instructed to create the needed folders yourself and copying or moving the files to the new structure, then to use the command line to build a jar from the structure.

I’ll show how this can be done with a special deployment descriptor from within the project. The image below shows the needed layout of the final jar file.

To transform the folder structure present in JDev to the needed structure of the final jar, we create a new deployment descriptor in JDev

In image 4 we see the first part of the solution: here we set the path inside the jar to ‘MEAT-INF’. This will guarantee the structure we need. Then we add another contributor to the list (public_html) to get everything we need into this folder. Then we use the ‘Filters’ node to select all content we need skin part

Next part is to create another path in the jar for the metadata of the skin

We add another file group for the resources

Now we can deploy the jar using the new deployment descriptor

And the jar file is created in the deploy folder. It holds all files in the right folders

Finally, we can deploy this jar to the WebLogicServer. In this case, I use the integrated WLS, but it can be any stand-alone WLS too.

The error message you see on the 7th image can be ignored. It only tells you that the library can’t be deployed as an application but only as a shared library. This is exactly what we want to do 🙂

Now the jar file is deployed on the WLS as a shared library and can be used for every application on this server.

We use the existing application from part one to consume the jar skin from the shared library and show the images deployed with the jar.

Before we go any further, we have to remove the ADF Library we added to show the problem from the project. For this open the project properties and select the ‘Library/Classpath’ node and remove the ‘ADFLibrary’ entry

The page should now look like no skin is used at all.

As we already added a skin (with the ADF Library) we don’t have to do this again. However, we have to add a library reference to configure the application to use the shared library deployed on the server. For this, we open the application descriptors and edit the ‘weblogic-application.xml’ file by double clicking the entry in the application resources section

In the ‘Shared Library Reference’ section, we add a reference to the now deployed shared jar ‘blogsharedskin’

Saving everything we don’t see any change to the page design, as the library isn’t part of the application yet. Starting the application we get

Just what we liked to see. The images are visible, checking the page with Chrome’s Dev Tools shows that the images are correctly loaded

This proves that the shared library with the skin and the images are working correctly.

To make the skin visible in JDev during development, we can add the jar we developed to the server a library. We create a library

and make sure the ‘Deploy by default’ is NOT set. Adding the library to the project

will make the skin visible in design mode

The unset checkmark prevents the jar from being packed into the WAR or EAR file. It’s just used in the IDE. That you can’t see the images is normal as there is no full server to serve the images to the design view.

Summary

In this mini-series, I showed the problem when creating a skin as ADF Library and trying to share it on a Weblogic Server. Then I showed how to create a deployment descriptor for the skin and other resources and how to deploy the resulting jar to a WebLogic Server.

The sample application can be downloaded from BlogSharedSkin. The sample was created by using JDev 12.2.1.3 but the same technique can be used in any 12.2.1.x JDev version. There is no database connection needed.

JDev 12.2.1.3: Creating a shared skin jar (Part 1)

In earlier versions of JDev, skins have been created either by pure code or by using the free Skin Editor. However, since JDev 12.2.1.x the skin editor has been integrated into JDeveloper itself.

A couple of questions in the ODC JDeveloper space are about how to create a skin with JDev which can be deployed as a shared library to a WebLogic Server. I gave this a try and it turned out, that you can build an ADF library jar from a skin project but you can’t use images to this jar which you might want to use in the application.

In this blog, I’ll show how to create a skin with resources like images and how to build a jar file from the skin together with the images and deploy it aa s shared jar to a WeblogicServer.

Building a skin project

The first part is to build a small skin project. The project we use to create a minimal skin, just to show that the skin is changing something. Then we add some images to the skin which we want to use in the application which uses the skin. Such images can e.g. used on an af:button component.

We start by creating a new application as an ‘ADF Fusion Web Application’

As we don’t need the created model project we delete it completely

If you get another dialog, telling you that you can’t undo the action, answer ‘Yes’ to delete the project. Now you should see a workspace with just the one project:

Know that we have a project we add a skin and e.g. add some skin selectors to change to the color of the button text. For this, we right click the ‘Web Content’ folder in the project and select ‘New from Garaly’ and then select ‘ADF Skin’ from the ‘JSF/Facelets’ node and fill in the basic information:

This will create the needed css file and the descriptors which define our skin (trinidad-config.xml and trinidad-skins.xml).

We open the sharedskin.css file if it’s not open already and switch to source mode. Here we add two simple skin selectors

which are changing the color of the text of a button and a link. You can add more sophisticated selectors but for this blog, it’s enough to show the working skin. To make it more interesting, and because that’s the real reason for this blog, we add some images to the skin which we like to use in the application using the skin. We add the images into a new folder like shown below

The reason for this structure is, that to read the images from the jar in the consuming application, we need a special resource loader. In case of ADF it’s the resource servlet which listens to the URL pattern ‘/adf/’’. This servlet is installed automatically for ADF Web Applications and is configured in the web.xml file

The final task for the skin project is to create a jar file which we can use in other applications. The easiest way to get such a jar is to create an ADF Library deployment descriptor. Open the project properties of the skin project and select the ‘Deployment’ node

And click the ‘New Profile’ icon, select to create an ADF Library Jar

and click ‘OK’. The remaining dialogs you can just click ‘OK’ or ‘Finish’.

To create the library we have to execute the descriptor by right-clicking on the project and selecting ‘Deploy’ and choosing the ‘sharedskinadflib’

This will create the jar in the ‘deploy’ folder of the project.

Sharing the skin with other applications

To share the skin with other application we can

  1. Create an ADF Library
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server
  2. Create a normal jar
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server

For this blog, we want to use option 2b. This allows to create the skin once, deploy it to a server and use it in every other application. In the next paragraph, we try out option 1a to show the problems when reading resources from a jar file.

Using a shared skin

Option 1a

We start with option 1a, just to show the problem when we try to read a resource from a jar. We build another ADF Fusion Web application and add the skin as ADF library from a ‘File System Connection’ which we create and let it point to the ‘deploy’ folder

Right-click on the ‘sharedskinadflib’ and add it to the new sample project. This will make the skin available to the application. To use the skin we have to add a skin to the application like we did to create the skin project. The difference is that we now choose the shared skin as the base skin

Creating a new page and adding a button and/or a link to the page we see the new style introduced by the ‘sharedskin’

So, the shared skin is working. Well, yes, but what about the images we added to the ‘sharedskin’?

Let’s try to add one to the button. In the property editor, we select the icon property of the button and click ‘Edit’ to get

However, we don’t see any image in the whole project. As we know where we put the images (or we can look into the sharedskinadflib) we can just add the path to the image like ‘skins/sharedskin/adf/images/home.png’ and we see the image

Running the application we get the page with the button but don’t see the image

Using DeveloperTools we see that the resource couldn’t be found. Inspecting the button element we see

The path to the image is not found 😦

If we change the address of the image to ‘/adf/images/home.png’ to use the resource servlet we still get an error

The reason is that the resource servlet expects the resources in a different path inside the jar. Every resource which should be read from a jar should be in a folder named ‘META-INF’.

The ADF library did not put the images into the META-INF folder

The problem is that we can’t change the layout of the ADF Library. When you create an ADF Library there is no option to make any changes to the content of the jar.

The conclusion is that using a skin in an ADF Library is problematic if there are other resources which you need to share.

To be continued…

In the final part 2 of the series, we see how the skin can be shared with other applications.