Case Study: Backporting a JDeveloper Project from11gR2 to 11gR1

I got a couple of queries to backport my file and image handling sample, which was built using JDeveloper 11.1.2.x version, to JDeveloper 11.1.1.x version. This I have done and like to share the steps on how to do this.

Here are the general steps:

  1. copy the workspace from 11gR2 into a fresh folder which you should onöy access with 11gR1. It’s essential to don’t open one workspace with different workspaces as this might change descriptors and or files.
  2. open the ‘.jsw’ file in 11gR1. If you get a message if you want to migrate answer ‘yes’
  3. recompile the model project.
  4. make the necessary changes to the viewController project files: ui pages and descriptors
  5. Test the application

As base for this case study we use the sample BlogUploadDownloadV2.zip which is the final sample from the Part 3.

1) unzip the sample into a fresh folder. We use the same folder names from the zip archive, but use a different base directory (backport in this case)
2) The images below show the steps to take after copying the files into the backport folder and opening the ‘.jws’ file. Don’t be fooled by the wizard which tell you that the files are converted from jsf1.0 to jsf1.2. Somehow hte wizard only knows that that the files are not jsf1.2 so it assumes that they are jsf1.0.

3) The database project don’t need any attention. The model project can just be recompiled. Here are the last lines after the rebuild. If you like you can test application module using the application module tester

...
Validating Business Component: de.hahn.blog.uldl.model.businessobjects.Catalog
  copying de/hahn/blog/uldl/model/businessobjects/Catalog.xml to output directory
Updated file:/Q:/backport/BlogUploadDownload/ULDLModel/classes/META-INF/adfm.xml
[7:11:42 PM] Successful compilation: 0 errors, 0 warnings. 

4) Now we have to make some obvious changes to the ViewController project. Fist we have to check if the ui pages are built using JSPX pages of JSF pages. A look into the public_html folder reveals that there is only one page (Catalog.jsf) which was built as JSF page. In a first step we rename the file to Catalog.jspx. Fragments have the suffix ‘.jsff’ in both versions, but are based on different schemas. However, we don’t need to rename the fragments.
Now we refresh the viewController project to see the new jspx file.

Refresh Project

Refresh Project


When we open the file we’ll see an error which is hte result that we tried to open a jsf (renamed only to jspx) page in 11gR1.
Error opening the Catalog.jspx

Error opening the Catalog.jspx


We ignore the error and continue our work. To fix the error we have to look into the file (source) and see that the jsf page uses a different root element:

<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">

whereas a jspx page uses

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">

After changing this root element and adding the close tag, the whole page look like

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
        <af:document title="Catalog.jspx" id="d1">
            <af:form id="f1" usesUpload="true">
                <af:panelStretchLayout topHeight="50px" id="psl1">
                    <f:facet name="top">
                        <af:outputText value="Upload Download Test" id="ot1" inlineStyle="font-size:xx-large;"/>
                    </f:facet>
                    <f:facet name="center">
                        <af:region value="#{bindings.catalogtaskflowdefinition1.regionModel}" id="r1"/>
                        <!-- id="af_one_column_header_stretched"  -->
                    </f:facet>
                </af:panelStretchLayout>
            </af:form>
        </af:document>
    </f:view>
</jsp:root>

We still see red marks in the right side gutter of the file. These are because we have not jet changes the libraries used by the project. After removing the JSF2.0 library we use the ‘Code Assist’ function of JDeveloper to add the correct JSF1.2 tag libs and libraries.


You may have to close JDeveloper and reopen it again to get rid of the red marks. After this you can open hte Catalog.jspx file in design mode:
Migrated Catalog.jspx

Migrated Catalog.jspx


The next step is to look into the ‘.jsff’ fragments. These too are using a different schema. This is the JSF2.0 fragment which uses a tag:

<?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"
                xmlns:f="http://java.sun.com/jsf/core">

The JSF1.2 fragment uses a jsp:root element instead:

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" xmlns:f="http://java.sun.com/jsf/core">

Changing the ui:component tag to the jsp:root tag (don’t forget to change the end tag) the fragments can be opened in design mode too. Depending on the components you have used in your may have to change some ui components to get your page to work.
Next step is to adjust the Databindings.cpx which still holds a link to the Catalog.jsf page.

Old Databindings.cpx

Old Databindings.cpx


Open the file in source mode and change the ‘Catalog.jsf’ to ‘Catalog.jspx’. Next we have to check the ‘adfc-config.xml’ which holds a reference to the ‘Catalog.jsf’ page. This we change to ‘Catalog.jspx’.
Now we are ready to try to run the backported app the first time. We check the DB connection first, then run the application.

<20.05.2013 20:58 Uhr MESZ> <Error> <J2EE> <BEA-160197> <Unable to load descriptor P:\jdeveloper\system\system11.1.1.7.40.64.93\o.j2ee\drs\BlogUploadDownload/META-INF/weblogic-application.xml of module BlogUploadDownload. The error is weblogic.descriptor.DescriptorException: Unmarshaller failed
	at weblogic.descriptor.internal.MarshallerFactory$1.createDescriptor(MarshallerFactory.java:161)
	at weblogic.descriptor.BasicDescriptorManager.createDescriptor(BasicDescriptorManager.java:323)
	at weblogic.application.descriptor.AbstractDescriptorLoader2.getDescriptorBeanFromReader(AbstractDescriptorLoader2.java:788)
...
	at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)
Caused by: com.bea.xml.XmlException: weblogic.descriptor.BeanAlreadyExistsException: Bean already exists: "weblogic.j2ee.descriptor.wl.LibraryRefBeanImpl@eadc995c(/LibraryRefs[[CompoundKey: adf.oracle.domain]])"
	at com.bea.staxb.runtime.internal.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:54)
	at com.bea.staxb.runtime.internal.RuntimeBindingType$BeanRuntimeProperty.setValue(RuntimeBindingType.java:539)
...

This error points to one of the descriptors we did not check up to now. The ‘/LibraryRefs[[CompoundKey: adf.oracle.domain]]’ is defined in the weblogic-application.xml file which we find ‘Application Resources’->’Descriptors’->’META-INF’ node.

weblogic-appliaction.xml

weblogic-appliaction.xml


Here we see that there are multiple entries for the same listeners and library-ref entries. These entries are getting duplicated each time you open the project (or restart JDeveloper). The reason for this is that the ‘xsi:schemaLocation’ is pointing to the wrong version ‘1.1’ (and location) in this case. The correct version for the 11gR1 is ‘1.0’. We replace the wrong schema with the correct one and remove the duplication entries.

<weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-application http://www.bea.com/ns/weblogic/weblogic-application/1.0/weblogic-application.xsd"
                      xmlns="http://www.bea.com/ns/weblogic/weblogic-application">

Before we run the application again we check need to check the web.xml which contains a couple of entries which are not needed using 11gR1.
There ‘context-param’s javax.faces.PARTIAL_STATE_SAVING, oracle.adf.view.rich.security.FRAME_BUSTING, javax.faces.FACELETS_VIEW_MAPPINGS, javax.faces.FACELETS_SKIP_XML_INSTRUCTIONS, javax.faces.FACELETS_SKIP_COMMENTS, javax.faces.FACELETS_DECORATORS and javax.faces.FACELETS_RESOURCE_RESOLVER should be removed.
After these changes the application is ready to run.
5) Test run the application

Running Backported Application

Running Backported Application

If you like to use hte new skyros skin you neet to alter the trinidad-config.xml to:

<?xml version="1.0" encoding="UTF-8"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
  <skin-family>skyros</skin-family>
  <skin-version>v1</skin-version>
</trinidad-config>

The last things to check is the ‘transaction isolation level’ of the task flows and the ‘ChangeEventPolicy’ of the iterators. The default ‘ChangeEventPolicy’ has been changed for 11gR2 applications to ‘ppr’ whereas it was ‘none’ for 11gR1. This change might not be visible at first, but you may notice some flicker in the table of the sample. This is the result of the new ‘ChangeEventPolicy’. If you check it back to none, you have to app the partial triggers to the components yourself. This is done in the sample which comes with Part 4 of the file and image handling sample.

There sure are more things to change, which I did not mention. This is because I did not need to change them ot did not find them. If you come across suhc a missing thing, please drop me a note so that I can add this to this article.

Advertisements

JDeveloper: Case Insensitive Search and Performance

A couple of days ago Frank Nimphius published a new ADF Insider Essential video about Search Forms Customization where he also showed how to implement case insensitive searches. While the tip how to do this is fine, he did not mention the bottleneck involved in doing so. Yesterday, while writing up this blog I came across this blog ‘ADF ViewCriteria performance impact’ by Raman Nanda who summarized the same issue. The last statement in his blog is the starting of this blog:

Note:Also don’t choose to ignore null values for predicates that are required and create proper indexes on the table structure depending upon how you filter results. For ex: If predicate is upper(ename)=upper(:bvar) then create a index on upper(ename) .

Lets start with a look on a view criteria definition in JDeveloper 11.1.2.1.0. the image below shows the definition of a simple view criteria to search for employees who’s names start with a given bind variable. When you first add items to the view criteria both check boxes ‘Ignore Case’ and ‘Ignore NULL Values’ are checked.

ViewCriteria Definition: Ignore Case and Ignore Null

ViewCriteria Definition: Ignore Case and Ignore Null


A close look at the ‘View Object Where Clause’ part reveals that the ‘Ignore Case’ part of the query is not visible in JDev 11.1.2.1.0. This is a bug which I’m going to file in the near future. Before going into detail with the query lets uncheck the ‘Ignore NULL Values’ to see the final criteria:
ViewCriteria only 'Ignore Case'

ViewCriteria only 'Ignore Case'


Running the application module in the tester reveals the final query as (copied from the log window)

[104] 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,         
             Employees.ACTION_COMMENT 
             FROM EMPLOYEES Employees 
             WHERE ( ( ( (UPPER(Employees.LAST_NAME) LIKE UPPER( :bindLN || '%') ) ) ) )
[105] Bind params for ViewObject: [de.hahn.blog.vcinsesitivesearch.model.dataaccess.EmployeesView]VCISAppModule.EmployeesView1
[106] Binding param "bindLN": K

Here you see that the ‘Ignore Case’ results in calling SQL UPPER'(…) on the bind parameter as well as on the row attribute. When you copy this query and run it in a SQL worksheet it returns the right results. However, in this bog we are more interested in the execution plan of the query. This is shown in the below image:

Execution Plan Without Index

Execution Plan Without Index


The interesting thing is that the result is reached by a full table scan, which you see as the option is ‘FULL’ for table access. This is not a problem if the table contains only a small number of rows, but if you work on large tables with 10000+ rows it’ll take ages (OK, it’s notable longer then you would expect) to execute.
If you only have defined the normal index on the LAST_NAME column

create index normal_ln_idx on employees (last_name);

the plan doesn’t change at all. This is exactly what Raman Nanda meant in his blog. You need to create a function based index on the LAST_NAME column. Here is the SQL to do so:

create index upper_ln_ix on employees (UPPER(last_name));

Running the query again after creation of the new index results in

Execution Plan with UPPER Index

Execution Plan with UPPER Index


As yo ucan see the table now is accessed via the ‘upper_ln_ix’ index we created. This speeds things up in large tables.

To summarize this blog: you should, as part of your testing, check the execution plans of the queries executed by your application. This can result in a huge improvement of the performance. You should ask your DBA to help you with this task. There are tools readily available to the DBA to help getting information about the queries executed by your application.

JDeveloper: Preventing return of large row sets on page load of VO using bind variable

Back in 2009 Andrejus Baranovskis blogged about how to prevent the execution of a (default) query when ADF loads a page here. This is sometimes necessary if the query defined for a page consumes a lot of time or return a lot of rows.
Lately some users reporting that the solution provided in the blog mentioned does not work. I have to confess, that I did not try out the method, so I can’t really comment on that.
In this blog I show a different way to archive this. The idea I implement is to add a part to the where clause of the VO which, when executed, return no rows. So I don’t prevent the execution but ensure that no row is returned.
To archive this I add the following where clause:

1 = 0

Now, if I add this as is, the query never will return any row. Instead I use a bind variable

1 = :bindDummy

This allows to use the bind variable to enable the query or in fact disable it. The full query defined in the VO looks like

SELECT Employees.ACTION_COMMENT, 
       Employees.COMMISSION_PCT, 
       Employees.DEPARTMENT_ID, 
       Employees.EMAIL, 
       Employees.EMPLOYEE_ID, 
       Employees.FIRST_NAME, 
       Employees.HIRE_DATE, 
       Employees.JOB_ID, 
       Employees.LAST_NAME, 
       Employees.MANAGER_ID, 
       Employees.PHONE_NUMBER, 
       Employees.SALARY
FROM HR.EMPLOYEES Employees
WHERE 1=:bindDummy

The next challenge is to control the bind variable from the UI. Here ADF Task Flows comes to help. A bounded task flow has a start activity which is executed whenever the bounded task flow is started. I use this start activity to set the bind variable to ‘0’ to prevent the return of any row. Then, on the page I have a button which sets the bind variable to ‘1’ and execute the query. This time I get the desired result.

adfc-config.xml

adfc-config.xml

I use the EWPTest ViewId to start the sample. On this page I add a button ‘Show Emplyoees’ which navigates to the bounded task flow ‘show-emp-bft’. In this task flow the start activity is ‘ExecuteWithParams’ which sets the bind variable to ‘0’.

Bounded Task Flow 'show-emp-bft'

Bounded Task Flow 'show-emp-bft'

Here is the page def file for the start activity. As you see this method call activity call ‘ExecuteWithParams’ and sets the bind variable to ‘0’ thus preventing the return of any row from the query.

Start Activity in Task Flow 'show-emp-btf'

As a result the next page only shows an empty table.

Show Employees after entering the page

Show Employees after entering the page

In the toolbar I placed a button ‘Execute with bindDummy set to 1′ which calls the executeWithParams’ method, this time with the bind variable set to ‘1’

ExecuteWithParams bind variable set to 1

ExecuteWithParams bind variable set to 1

As the result I get the desired result

Result after ExecuteWithParams with bind variable set to 1

Result after ExecuteWithParams with bind variable set to 1

The ‘Back’ button in the toolbar return from the task flow to the first page. If I hit the ‘Show employees’ again the bind variable is set to ‘0’ again and the query again does not return any row.

You can download the sample workspace, build with JDev 11.1.2.1.0 and depending on the HR db schema, from here: BlogExecWithParams_V2.zip.doc
Please rename the file to ‘.zip’ after downloading it!

JDeveloper & ADF: Reset Form Fields

In the last couple of weeks I saw some posts on JDeveloper & ADF forum asking how to reset form fields when the user had changed some or all fields of an input form and wants to undo the changes. This is a typical use case when you work with forms to enter or change data.

In this blog I summarize the different possible solutions build in the ADF framework. The sample provided for you to analyze is build using JDeveloper 11.1.2.1.0 and uses the HR schema. A link to download the workspace is provided at the end of the blog post.

Before I go into the different solutions, I like to point out that all solutions can only reset values to the last data model state if they are not submitted. After a value is submitted the reset operation will show the same value as in the UI. If you e.g. navigate to the next record all values are submitted to the data model (not the db). Calling reset after you go back to the previous record you’ll see no change as the values are already submitted.
As an implication you need to make sure that the action component (button, listener…) uses the immediate property set to ‘true’. If the immediate property is set to ‘false’ the action first updates the model after which no reset is possible. In this case you can only rollback the transaction to get the old values back.

Lets start with a picture of the running application:

Running Applicaiton

Running Applicaiton

As you see it’s just the employees table dropped as adf form on a jsf page with navigation buttons and an other row of buttons to demonstrate the different possible solution for the use case. First I show how to use java code in a bean to reset the changes made in the form. Next I use a command button with an af:resetActionListener attached. Then there is the new af:resetButton which was introduced in JDev 11.1.2.0.0 and is essentially like the previous solution. As a variation I added two buttons which I use to queue an action from inside a bean method to the second and third buttons. Finally there is cancel button which does a rollback of the transaction. This rollback undo changes even if you navigate back and forth over the row set.

1. Reset field from a java bean

The button ‘Reset Form Fields by Bean’ uses an action listener to call a method in a request scope bean. This method then resets the form fields using the ResetUtils.reset(UIComponent start); method to do the work.

    public void resetFormFieldsListener(ActionEvent actionEvent)
    {
        // check if hte action has a component attatched
        UIComponent uiComp = actionEvent.getComponent();
       
        if (uiComp == null)
        {
            // if not we use the button which we bound to this bean
            uiComp=getButtonResetByBean();
            _logger.info("reset fields: buttonID = " + uiComp.getId());
        }
        else
        {
            _logger.info("reset fields: CompID = " + uiComp.getId());
        }
        // pass component inside the UIForm, UIXForm, UIXSubform, UIXRegion, UIXPopup, RichCarousel
        // or RichPanelCollection which holds the components to reset
        ResetUtils.reset(uiComp);
    }

The ResetUtils.reset(UIComponent start) method walks up the component tree to find the first UIForm, UIXForm, UIXSubform, UIXRegion, UIXPopup, RichCarousel or RichPanelCollection component. Any one of this components are used as container elements for input components (inputText, inputDate …) which are reset to the values stored in the model data. This allows to only reset part of the input components on a page. So passing the right start component to the reset method is essential. In the simple layout I used in the sample it’s OK to use the button which has the actionListener attached. The af:form is the container found by the ResetUtils.reset(UIComponent start) method.
The ResetUtils.reset(UIComponent start); can be used from other bean code too. In this case you need to set a component as start component. Which component you use depends on your page layout.

Solution 1 ResetUtils.reset(...)

Solution 1 ResetUtils.reset(...)

2. Command button with attached resetActionLitener

This was the default solution in JDev 11.1.1.x.0. You attach an af:resetActionListener to an other command component and when an action is triggered the reset take place. See solution 4 for a variation.

        <af:commandButton actionListener="#{bindings.Rollback.execute}"
            text="Cancel" immediate="true" id="cb6">
              <af:resetActionListener/>
        </af:commandButton> 
Solution 2: af:resetActionListener

Solution 2: af:resetActionListener

3. af:resetButton
This component was introduced with JDev 11.1.2.0.0 and works like a command button with an attached af:resetActionListener. Advantage is that you have only the button, nothing attached which makes it more clear to understand.

resetButton

resetButton

<af:resetButton text="Reset Form Fields" id="rb1" binding="#{BRFFBean.buttonResetButton}"/>
Solution 3: af:resetButton

Solution 3: af:resetButton

4. Queue an action to a button with attached af:resetActionListener

This solution is a combination of the first one using java code which then queues an action on an existing button on the page. If this button has an af:resetActionListener attached it resets the form fields as if the user had hit the button itself. On the page I use a button with an actionListener simply to get to a bean method (an actionListener in this case)

<af:commandButton text="Queue Action1 " id="cb9"
                  actionListener="#{BRFFBean.queueAction1Listener}"
                  immediate="true"/>

and in the bean I queue an action to the button with the attached af:resetActionLisener. This button is bound to a bean property to keep things simple.

    public void queueAction1Listener(ActionEvent aEvent)
    {
        _logger.info("Queue action for button wiht af:resetActionListener");
            
        ActionEvent actionEvent = new ActionEvent(this.getButtonResetActionListener());
        actionEvent.queue();
    }
Solution 4: Queue action to existing button with af:resetActionListener

Solution 4: Queue action to existing button with af:resetActionListener

5. Queue an action to a af:resetButton

Like in solution 4 I use an existing button (the af:resetButton in this case) and queue an action on this button using java code in a bean.

<af:commandButton text="Queue Action2" id="cb10"
                  actionListener="#{BRFFBean.queueAction2Listener}"
                  immediate="true"/>

and the code in the bean looks like in solution 4, only the button the action is queued on differs.

    public void queueAction2Listener(ActionEvent aEvent)
    {
        _logger.info("Queue action for af:resetButton");
        ActionEvent actionEvent = new ActionEvent(this.getButtonResetButton());
        actionEvent.queue();        
    }

Now, if you carefully look at the result of this action you’ll notice that nothing has happened. I filed an SR for this, resulting in bug 13802277 ‘QUEUEING AN ACTION EVENT ON AN RICHRESETBUTTON DOESN’T WORK’.

Solution 5: Queue action on af:resetButton

Solution 5: Queue action on af:resetButton

6. Cancel action with rollback

This solution just call the rollback of the data control. This give you the values back which are stored in the DB. This allows even to reset changes which where submitted to the data model, but where not persisted in the db.

You can download the sample workspace, build with JDev 11.1.2.1.0 and depending on the HR db schema, from here: BlogRestFormFields.zip.doc
Please rename the file to ‘.zip’ after downloading it!

JDEV: af:query hide ‘Add Fields’ from query panel via custom skin

In my last post ‘JDEV: af:query hide some attributes from query panel but show them in the result table’ I showed how to hide some of the available attributes from the af:query panel.
Juan, an other blogger informed me that he too had shown how to do this (Control visibility of a query in adf). The blog not only showed how to hide attributes from the panel, but also showed how to hide the ‘Add Fields’ button you see in the advanced mode.

Query Panel Advanced Mode

Query Panel Advanced Mode


The method (or better trick) is to put a component in the footer facet of the af:query, which is stated in the docs. If you use an af:spacer (e.g. 1×1) for this, nothing is visible in the panel. The automatically filled in button ‘Add Fields’ is gone.
Well, I’m not just copying the other blog, but like to show a different approach using a custom skin to do it. The advantage using the skin approach is that you can clearly see (via hte name of the style class) why you don’t see the ‘Add Field’ button. Using the spaces the button is simply gone and you have to remember how you get rid of it (in a year).

First we need to add an ADF Skin to the project. For this we add a new ADF Skin file to the project and set its properties:

Skin creation

Skin creation


Skin Properties

Skin Properties

As I’m using JDev 11.1.2.1.0 the skin editor is build in. If you are trying this on an older version, you can use the stand alone version to create the skin file.
In the skin editor we look for the af:query component, which we want to change. In the component properties for the af:query we look for the ‘footer-facet-content-style’ pseudo element. For this element we set the display property to none. This will hide the whole facet in the UI. As this facet holds the ‘Add Fields’ button, the button is not visible in the UI.

Change Element

Change Element

Preview of af:query with skin applied

Preview of af:query with skin applied

If you leaf the skin in this state, the change works for all af:query components of hte project. As I like it to be changeable on a per component basis, I define my own style class for this change. For this change to the source mode of the skin file and add a style class name in front of the selector:

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

.AFQueryHideAddFields af|query::footer-facet-content-style
{
  display: none; 
}

Now you can use this ‘AFQueryHideAddFields’ style class on each af:wuery component where you want to hide the ‘Add Fields’ button .

...
                    <af:query id="qryId1" headerText="Search" disclosed="true"
                              value="#{bindings.ImplicitViewCriteriaQuery.queryDescriptor}"
                              model="#{bindings.ImplicitViewCriteriaQuery.queryModel}"
                              queryListener="#{bindings.ImplicitViewCriteriaQuery.processQuery}"
                              queryOperationListener="#{bindings.ImplicitViewCriteriaQuery.processQueryOperation}"
                              resultComponentId="::pc1:resId1" styleClass="AFQueryHideAddFields">
...
Apply StyleClass

Apply StyleClass

You can download the sample workspace, build with JDev 11.1.2.1.0 and depending on hte HR db schema, from here: BlogHideAttributesInQueryPanel_v2.zip
Please rename the file to ‘.zip’ after downloading it!

JDeveloper 11.1.2.1: Cascading Tables

Lately a user on OTN JDeveloper and ADF forum ask how to cascade to tables instead of two LOV components. My first thought was to use an af:treeTable, however, this would give the user a different experience then you get from a cascading LOV. In the end I build a small test case using the HR db schema using the departments as master table and the employees of the selected departments as detail table. At the end of the post you’ll find the link to the sample workspace.
The sample is very simple as it only has the departments with the cascading employees view as data model.

Data model

Data model

The view controller is simple too. Its consists of only one page which holds a region. Inside the region are two panelCollection components, one holding the departments as table (read only, single selection mode) and one holding the cascading employees table (read only, single selection mode).

Region holding the cascading tables

Region holding the cascading tables

The magic which make the sample work, is the partial trigger which is used on the employee table and is listening on the departments table. The selection of the employees is done in the model via the viewLink which is automatically setup when you create the business components from the HR tables.

 PartialTrigger

PartialTrigger

When you run the sample, which was build using JDev 11.1.2.1.0, you see the that the first row of the departments table is selected and the employees of this department in the lower table.

Start of sample

Start of sample

If you select an other row in the department table you see the different employees in the lower table

Selection of an other department

Selection of an other department

To summarize this blog, I can say that the implementation of the use case did not need one line of java code. The solution was easy to archive by only using a declarative approach.

You can download the sample workspace, build with JDev 11.1.2.1.0 and depending on hte HR db schema, from here: BlogCascadingTables.zip
Please rename the file to ‘.zip’ after downloading it!

JDev11.1.2.1.0: Handling images/files in ADF (Part 3)

This blog article is part 3 of a series of posts showing how to deal with images or files in an ADF application. Each of the techniques to do this are described in other blog posts or documents, but I couldn’t find a sample doing it all together in on sample application.
The goal of this series is to provide a sample showing how to upload a file from a client to the server, store the data on the server, make it available for later retrieval and show the data in the user interface (form and table).

    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

Implements two techniques to show the data (image) on the user interface

This part of the mini series handles about embedding images in JSF pages. Images can be embedded in tables and forms as shown in the picture below:

Embedded image in table and form

Embedded image in table and form

To do this we use an af:image tag inside an af:column for the table or inside an af:form instead of an af:inputText or af:outputText. The af:image tag needs an URI to retrieve the image data from and present them to the client. The problem here is that you can’t access the whole file system of the server your application is running on or access images from other servers, NAS or DB storage. Only image data deployed with the application below the web root (not within the WEB-INF folder) is directly accessible.
For all ohter locations you need some code in a servlet where you can access your image data and pass it to the client. The rest of this post shows two different way to access image data in a servlet.

Setting up the servlet in hte application
To make a servlet accessible to the application we need to define a path, relative to you web root, which your servlet listen to. This definition is done in the web.xml file:

  &lt;servlet&gt;
    &lt;display-name&gt;ImageServlet&lt;/display-name&gt;
    &lt;servlet-name&gt;ImageServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;de.hahn.blog.uldl.view.frkext.servlet.ImageServlet&lt;/servlet-class&gt;
    &lt;load-on-startup&gt;100&lt;/load-on-startup&gt;
  &lt;/servlet&gt;
  &lt;servlet-mapping&gt;
    &lt;servlet-name&gt;ImageServlet&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/render_image&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;

Here you define the servlet name, class and the URI part (‘/render_image’) the servlet is listening to. To access this URI from an af:image tag you use code like this:

&lt;af:image source=&quot;/render_image?id=#{bindings.ImageId.inputValue}&quot; id=&quot;i1&quot;
                              shortDesc=&quot;#{bindings.ImageName.hints.tooltip}&quot;
                              inlineStyle=&quot;width:200px;&quot;/&gt;

The servlet implements the doGet(…) method where all the real work is done. First of all you can get the parameters passed to together with the URI: ‘id=#{bindings.ImageId.inputValue}’ in the sample above. The parameter(s) should be sufficient to get the image data from within the servlet. In the sample I pass the primary key of the row holding the image data in a blob.

1. Accessing the image data using a RootApplicationModule
The first method I like to show is the use of an RootApplicationModule to access the view object holding the image data.

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        synchronized (this)
        {
            StringBuilder sb = new StringBuilder(100);
            String appModuleName = &quot;de.hahn.blog.uldl.model.facade.ULDLAppModule&quot;;
            String appModuleConfig = &quot;ULDLAppModuleLocal&quot;;
            ApplicationModule am = null;
            ViewObject vo = null;
            try
            {
                am = Configuration.createRootApplicationModule(appModuleName, appModuleConfig);
                sb.append(&quot;ImageServletRooAppModule &quot;).append(appModuleName);
                ULDLAppModuleImpl amULDL = (ULDLAppModuleImpl) am;
                vo = am.findViewObject(&quot;ImageAccessView1&quot;);
                if (vo == null)
                {
                    throw new Exception(&quot;ImageAccessView1 not found!&quot;);
                }
                ImageAccessView imageView = (ImageAccessView) vo;
                
                // get parameter from request
                Map paramMap = request.getParameterMap();
                oracle.jbo.domain.Number id = null;
                if (paramMap.containsKey(&quot;id&quot;))
                {
                    String[] pVal = (String[]) paramMap.get(&quot;id&quot;);
                    id = new oracle.jbo.domain.Number(pVal[0]);
                    sb.append(&quot; id=&quot;).append(pVal[0]);
                }                

                // Get the result (only the first row is taken into account
                ImageAccessViewRow imageRow = (ImageAccessViewRow) imageView.getImageById(id);
                BlobDomain image = null;
                String mimeType = null;
                // Check if a row has been found
                if (imageRow != null)
                {
                    // We assume the Blob to be the first a field
                    image = imageRow.getImageData();
                    mimeType = (String) imageRow.getContentType();
                }
                else
                {
                    mLogger.warning(&quot;No row found to get image from !!! (id = &quot; + id + &quot;)&quot;);
                    return;
                }
                sb.append(&quot; &quot;).append(mimeType).append(&quot; ...&quot;);
                mLogger.info(sb.toString());

                // Set the content-type. Only images are taken into account
                response.setContentType(mimeType + &quot;; charset=utf8&quot;);
                OutputStream outputStream = response.getOutputStream();
                IOUtils.copy(image.getInputStream(), outputStream);
                // cloase the blob to release the recources
                image.closeInputStream();
                // flush the outout stream
                outputStream.flush();
            }
            catch (Exception e)
            {
                mLogger.warning(&quot;Fehler bei der Ausführung: &quot; + e.getMessage());
            }
            finally
            {

                if (am != null)
                {
                    //Release the appModule
                    Configuration.releaseRootApplicationModule(am, true);
                }
            }

            mLogger.info(&quot;...done!&quot;);
        }
    }

Line 13 shows the code to create a rot ApplicationModule which is then used in line 16 to get the view object holding the data. Lines 23 to 31 retrieve the parameter passed with the URI. Line 34 get the row holding the image data, the next passage gets the mime type and image name and finally the image blob to copy it to the servlets output stream.
The rest of the code cleans up and releases the root ApplicationModule.
Releasing the ApplicationModule is essential as you running out off resources quickly otherwise.

This approach does have some disadvantages. First creating a root ApplicationModule is a costly operation, second you can’t share the context with the application itself. This leads to the solution presented next.

2. Using the existing BindingLayer of the application

This is my preferred method, however it need some more preparation. Still the advantages out wight this. One advantage is that there is no difference in the way you talk to the model layer. This helps to avoid errors. Next you can get a ‘Shared BindingContext’, meaning that you share the same data control context (frame) as the underlying UI pages and Task Flows within that application. For more information check out this sample Sharing ADF context with a Servlet.
In this article I only show how to setup the binding layer in the servlet and use it to load image data.

First of all we need to build a PageDef.xml file which defines the binding layer for the servlet as it does for normal JSF pages. Directly creating a PageDef.xml is not suppoerted in the current JDev version (11.1.2.1.0) or earlier versions.
Here are a step by step instruction to create a PageDef.xml file:

A. Create an new XML file in the same folder where you found the existing PageDef.xml files and name it e.g. ‘xyz_dummyPageDef.xml’. You see hte result in the image below.

Create new empty PageDef.xml file

Create new empty PageDef.xml file


B. Open any of your existing PageDef.xml files in Source mode and copy the whole content into the newly created xyz_dummyPageDef.xml file
C. Now edit the new file so that the content looks like below. Make sure that you change the id property to the name of of the new file.

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;pageDefinition xmlns=&quot;http://xmlns.oracle.com/adfm/uimodel&quot; version=&quot;11.1.2.60.81&quot;
                id=&quot;xyz_dummyPageDef&quot; Package=&quot;de.hahn.blog.uldl.view.pageDefs&quot;&gt;
  &lt;parameters/&gt;
  &lt;executables&gt;
    &lt;variableIterator id=&quot;variables&quot;/&gt;
  &lt;/executables&gt;
  &lt;bindings&gt;
  &lt;/bindings&gt;
&lt;/pageDefinition&gt;

D. As you see the new file does not have the same icon as the other files (4.). Save your work and close JDev. Open JDev again and it will look OK. Now we can begin to setup the PageDef file.

E. We start with adding an iterator into the executable section

Setup PageDef.xml file for servlet  (part 1)

Setup PageDef.xml file for servlet (part 1)

Then we add an methodAction into the binding section on the left (1.), for this we select view iterator (or application module) which holds the method (2.) and select the method in the operation drop down box. The final result looks like (3.)

Setup PageDef.xml file for servlet  (part 2)

Setup PageDef.xml file for servlet (part 2)

The source of the PageDef.xml file looks like

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;pageDefinition xmlns=&quot;http://xmlns.oracle.com/adfm/uimodel&quot; version=&quot;11.1.2.60.81&quot;
                id=&quot;image_dummyPageDef&quot; Package=&quot;de.hahn.blog.uldl.view.pageDefs&quot;&gt;
  &lt;parameters/&gt;
  &lt;executables&gt;
    &lt;variableIterator id=&quot;variables&quot;/&gt;
    &lt;iterator id=&quot;ImageAccessView1Iterator&quot; Binds=&quot;ImageAccessView1&quot;
              DataControl=&quot;ULDLAppModuleDataControl&quot; RangeSize=&quot;25&quot;/&gt;
  &lt;/executables&gt;
  &lt;bindings&gt;
    &lt;methodAction IterBinding=&quot;ImageAccessView1Iterator&quot; id=&quot;getImageById&quot;
                  RequiresUpdateModel=&quot;true&quot; Action=&quot;invokeMethod&quot; MethodName=&quot;getImageById&quot;
                  IsViewObjectMethod=&quot;true&quot; DataControl=&quot;ULDLAppModuleDataControl&quot;
                  InstanceName=&quot;data.ULDLAppModuleDataControl.ImageAccessView1&quot;
                  ReturnName=&quot;data.ULDLAppModuleDataControl.methodResults.getImageById_ULDLAppModuleDataControl_ImageAccessView1_getImageById_result&quot;&gt;
      &lt;NamedData NDName=&quot;aId&quot; NDType=&quot;oracle.jbo.domain.Number&quot;/&gt;
    &lt;/methodAction&gt;
  &lt;/bindings&gt;
&lt;/pageDefinition&gt;

After creating the PageDef.xml the second step is to create a ‘Page Definition Usage’ for the PageDef file in Databindings.cpx.
For this we open the Databindings.cpx, select the ‘PageDefinitionUsages’ node in the structure window and right click to add a page. In the dialog you enter an id for the ‘Page Definition Usage’ and the path to the PageDef.xml file. The id is used later, when we load the BindingContext inside the servlet.

Create entry in Databindings.cpx

Create entry in Databindings.cpx

The third and final step is to configure the adf binding filter that it also filters requests to the image servlet. This is done in the web.xml. Here we select the ‘Filters’ node and look for the ‘adfBindings’. Now we insert a new servlet mapping. For this we select the adfBindings filter, select the ‘Filter Mapplings’ tab. Hitting the green ‘+’ sign we add a new mapping. Select ‘Servlet’ as ‘Mapping Type’, in the mappling column we select the ‘ImageServlet’ and select ‘FORWARD’ and ‘REQUEST’ as ‘Dispatcher Type’. The result of this should look like

Activate ADFbinding filter for the new servlet

Activate ADFbinding filter for the new servlet

This concludes the preparation for the access of the binding layer from a servlet. Now we look at the code which actually implements the doGet() method in the servlet.

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        synchronized (this)
        {
            StringBuilder sb = new StringBuilder(100);
            sb.append(&quot;ImageServlet &quot;);

            ViewObject vo = null;
            try
            {
                // get parameter from request
                Map paramMap = request.getParameterMap();
                oracle.jbo.domain.Number id = null;
                if (paramMap.containsKey(&quot;id&quot;))
                {
                    String[] pVal = (String[]) paramMap.get(&quot;id&quot;);
                    id = new oracle.jbo.domain.Number(pVal[0]);
                    sb.append(&quot; id=&quot;).append(pVal[0]);
                }

                // get method action from pagedef
                BindingContext bindingContext = BindingContext.getCurrent();
                DCBindingContainer amx =
                    bindingContext.findBindingContainer(&quot;de_hahn_blog_uldl_view_image_dummyPageDef&quot;);
                JUCtrlActionBinding lBinding =
                    (JUCtrlActionBinding) amx.findCtrlBinding(&quot;getImageById&quot;);
                // set parameter
                lBinding.getParamsMap().put(&quot;aId&quot;, id);
                // execute method
                lBinding.invoke();
                // get result
                Object obj = lBinding.getResult();                
                ImageAccessViewRow imageRow = (ImageAccessViewRow) obj;
                BlobDomain image = null;
                String mimeType = null;

                // Check if a row has been found
                if (imageRow != null)
                {
                    // Get the blob data
                    image = imageRow.getImageData();
                    mimeType = (String) imageRow.getContentType();
                    if (image==null)
                    {
                        mLogger.info(&quot;No data found !!! (id = &quot; + id + &quot;)&quot;);
                        return;                        
                    }
                }
                else
                {
                    mLogger.warning(&quot;No row found to get image from !!! (id = &quot; + id + &quot;)&quot;);
                    return;
                }
                sb.append(&quot; &quot;).append(mimeType).append(&quot; ...&quot;);
                mLogger.info(sb.toString());

                // Set the content-type. Only images are taken into account
                response.setContentType(mimeType + &quot;; charset=utf8&quot;);
                OutputStream outputStream = response.getOutputStream();
                IOUtils.copy(image.getInputStream(), outputStream);
                // cloase the blob to release the recources
                image.closeInputStream();
                // flush the outout stream
                outputStream.flush();
            }
            catch (Exception e)
            {
                mLogger.log(Level.WARNING, &quot;Fehler bei der Ausführung: &quot; + e.getMessage(), e);
            }
            finally
            {

            }

            mLogger.info(&quot;...done!&quot;);
        }
    }

The first part of the code looks like the doGet() method of the other servlet which uses a root application module to get the image data. The interesting part starts a line 22 in the code above. Here we get the binding layer like we do inside a managed bean. The only difference is that we use the

DCBindingContainer amx =
     bindingContext.findBindingContainer(&quot;de_hahn_blog_uldl_view_image_dummyPageDef&quot;);

to get the binding container instead of using

DCBindingContainer amx =
     (DCBindingContainer) bindingContext.getCurrentBindingsEntry();

all other code is identically to code you would use in a managed bean to call a method defined in the binding layer. This is one big advantage as the access method and the way to call a method are ‘equal’. This should result in lesser errors.

This finishes the mini series about file/image handling in ADF. The workspace can be downloaded from BlogUploadDownloadV2.zip. Please rename the file to ‘.zip’ after downloading it! You have to check the DB connection. Please don’t forget to add the needed tables for the catalog and the image data. The SQL DDL can be fond in the ULDLDatabaseModel. Instructions on how to setup the DB can be found in Part 1
The Commons IO package in the version 2.0.1 you can download from the Apache Software Foundation apache web side

JDEV 11.1.2.1.0: Using router to conditionally set navigation target

Interesting question came up on OTN ADF froum.
You have one page (page2) which is called from two other pages (page1 and page3). The question is how to set up the navigation in page2 so that you have only on button (back) which gets you back to the page the user navigated from to page2?

There is more then one solution to this problem. In this blog entry I show the declarative solution, so no java code is used. Here is adfc-config.xml which shows the navigation:

Router Back Navigation

Router Back Navigation

This is a simple navigation where the user can navigate from Page1 or Page3 to Page2. Page2 uses only one navigation case ‘back’ and let the router decide where to go. To make this happen, the button which navigates from Page1 to Page2 needs to store a hint ‘1’ in pageFlowScope which the router can check to decide where to go to. For this I add a af:setPropertyListener to store the hint in pageFlowScope. You don’t need a bean to store the value as the storage for the value is set up automatically.

                        <af:commandButton text="Page 2" id="cb1" action="page12">
                            <af:setPropertyListener from="#{'1'}" to="#{pageFlowScope.backTarget}"
                                                    type="action"/>
                        </af:commandButton>

The same technique is used in Page3 to store the hint ‘3’ in pageFlowScope

                        <af:commandButton text="Page 2" id="cb1" action="page32">
                            <af:setPropertyListener to="#{pageFlowScope.backTarget}" type="action"
                                                    from="#{'3'}"/>
                        </af:commandButton>

Finally the magic is done in the router. Here the value which is stored in pageFlowScope variable ‘backTarget’ is checked and hte correct navigation target is used for the back navigation. Below is the source of the router, the design view is shown in the first picture.

  <router id="backRouter">
    <case id="__12">
      <expression>#{pageFlowScope.backTarget eq '1'}</expression>
      <outcome>page21</outcome>
    </case>
    <case id="__13">
      <expression>#{pageFlowScope.backTarget eq '3'}</expression>
      <outcome>page23</outcome>
    </case>
    <default-outcome>page21</default-outcome>
  </router> 

When you run the sample you start from Page1 and navigate to Page2 you see the output text which shows the content of the pageFlowScope variable, ‘1’ in this case.

Navigation from Page1 to Page2

Navigation from Page1 to Page2

If the user navigates from Page3 to Page2 the output looks like

Navigation Page3 to Page2

Navigation Page3 to Page2


the output text shows ’3′ in this case.

You can download the sample application which is build using JDeveloper 11.1.2.1.0 from here BlogRouterBackNavigation.zip.
Please rename the file to ‘.zip’ after downloading it!
The sample don’t use any db connection and should be runnable on older JDeveloper versions too.

JDev11.1.2.1.0: Handling images/files in ADF (Part 2)

This blog article is part 2 of a series of posts showing how to deal with images or files in an ADF application. Each of the techniques to do this are described in other blog posts or documents, but I couldn’t find a sample doing it all together in on sample application.
The goal of this series is to provide a sample showing how to upload a file from a client to the server, store the data on the server, make it available for later retrieval and show the data in the user interface (form and table).

    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


Uploading, downloading and storing of data in a blob

In this part of the series I show how to upload a file from the client to the server and store the data in a blob column in a db. The tables I’m using are CATALOG and IMAGES. The DML to define the tables and can be found in PART 1 of the series or in the sample workspace at the end of this part.
Lets start with uploading a file from client to the server. ADF rich faces provide the tag af:inputFile to allow uploading of data.

&lt;af:inputFile label=&quot;Select new file&quot; id=&quot;if1&quot; autoSubmit=&quot;true&quot;
              valueChangeListener=&quot;#{ImageBean.uploadFileValueChangeEvent}&quot;/&gt;

As you see the tag has set its autoSubmit property to true to allow direct upload of data. The real work is done in the valueChangeListener which is bound to a backing bean in request scope. The value the event carries allows access to the data and give us the original filename and mime type.

    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(&quot;ImagesView2Iterator&quot;);
        Row newRow = lBinding.getCurrentRow();
        // set the file name
        newRow.setAttribute(&quot;ImageName&quot;, fileName);
        // create the BlobDomain and set it into the row
        newRow.setAttribute(&quot;ImageData&quot;, createBlobDomain(file));
        // set the mime type
        newRow.setAttribute(&quot;ContentType&quot;, contentType);
    }

    private BlobDomain createBlobDomain(UploadedFile file)
    {
        // init the internal variables
        InputStream in = null;
        BlobDomain blobDomain = null;
        OutputStream out = null;

        try
        {
            // Get the input stream representing the data from the client
            in = file.getInputStream();
            // create the BlobDomain datatype to store the data in the db
            blobDomain = new BlobDomain();
            // get the outputStream for hte BlobDomain
            out = blobDomain.getBinaryOutputStream();
            // copy the input stream into the output stream
            /*
             * 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);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        catch (SQLException e)
        {
            e.fillInStackTrace();
        }

        // return the filled BlobDomain
        return blobDomain;
    }

Please note the I use the Apache Commons IO package in the version 2.0.1 which you need to download from the Apache Software Foundation web side. The use the class IOUtils which allow easy copying of streams.
If your are using an older version of JDeveloper you may need to add the usesUpload property to the af:form tag. In the current JDev version it should work automatically (but please check it).

&lt;af:form id=&quot;f1&quot; usesUpload=&quot;true&quot;&gt;

If you use fragments (as in this sample) you need to check the jspx or jsf page which is holding the af:form tag (Catalog.jsf), as the fragments don’t have a form tag.
By default, Oracle ADF 11g application allows to upload maximum 2 MB size files. This maximum can be configured in the web.xml file if you need to upload files bigger then 2 MB. For this you need to specify the context parameters

    org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY
    org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE
    org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR

For more information about the parameters and how they work check the doc Oracle® Fusion Middleware Web User Interface Developer’s Guide for Oracle Application Development Framework.

Now to the download part. This is handled in ADF via the af:fileDownloadActionListener tag. The tag is a client listener tag and is therefor applied to a command ui tag. In the sample I use a af:commandButton:

&lt;af:commandButton text=&quot;Download Data&quot; id=&quot;cb3&quot;
                  visible=&quot;#{bindings.ImageData.inputValue ne null}&quot;
                  binding=&quot;#{ImageBean.downloadButton}&quot;&gt;
          &lt;af:fileDownloadActionListener contentType=&quot;#{bindings.ContentType.inputValue}&quot;
                                         filename=&quot;#{bindings.ImageName.inputValue}&quot;
                                         method=&quot;#{ImageBean.downloadImage}&quot;/&gt;
&lt;/af:commandButton&gt;

The real work is done in the downloadImage method in the managed bean. The signature of the method is

public void downloadImage(FacesContext facesContext, OutputStream outputStream)

This allows you to access to the FacesContext and the output stream which you use to pipe the data to the client.

    public void downloadImage(FacesContext facesContext, OutputStream outputStream)
    {
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding(&quot;ImageData&quot;);
        if (attr == null)
        {
            return;
        }

        // the value is a BlobDomain data type
        BlobDomain blob = (BlobDomain) attr.getInputValue();

        try
        {   // copy hte data from the BlobDomain to the output stream 
            IOUtils.copy(blob.getInputStream(), outputStream);
            // cloase the blob to release the recources
            blob.closeInputStream();
            // flush the outout stream
            outputStream.flush();
        }
        catch (IOException e)
        {
            // handle errors
            e.printStackTrace();
            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), &quot;&quot;);
            FacesContext.getCurrentInstance().addMessage(null, msg);
        }
    }

This is all you need to do to download a blob from the db and send it back to the client.

I like to mention one other function of the sample application. If you hit the ‘Cancel’ button in the insert or edit image page I use a rollback to get the original data back and remove all changes made in the form. A rollback resets all current row pointers of the iterators. To avoid that the user sees the first row of the catalog table the rollback has to be handled in a special way. You have to save the current row (of the catalog iterator), do the rollback and reset the current row back to the saved one. This is done in the bean method

public String cancel_action() {...}

which you find in the ImageBean class.

This concludes part 2. In part three I implement two techniques to show the data (image) on the user interface (page)

The sample application workspace is build with JDeveloper 11.1.2.1.0 and can be loaded from here BlogUploadDownload_P2.zip
Please rename the file to ‘.zip’ after downloading it!
The Commons IO package in the version 2.0.1 you can download from the Apache Software Foundation apache web side

To be continued…

JDev11.1.2.1.0: Handling images/files in ADF (Part 1)

This blog article is part 1 of a series of posts showing how to deal with images or files in an ADF application. Each of the techniques to do this are described in other blog posts or documents, but I couldn’t find a sample doing it all together in on sample application.
The goal of this series is to provide a sample showing how to upload a file from a client to the server, store the data on the server, make it available for later retrieval and show the data in the user interface (form and table).

    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


Overview of the sample application

The sample allows to create and manage catalogs. A catalog has a unique id, a name and may contain files and images which the user can upload into a catalog and download from it. All this is implemented in a simple way, so no fancy layout, only bare functionality. Here is a screen shot of the sample application after part 2 is finished:

Sample app end of part 2

Sample app end of part 2

As you see the UI is nothing I would use in a real world application, but for this sample it does the trick.
To create a new catalog you click the ‘New Catalog’ button and can fill in a name for the new catalog. The id is automatically assigned via a groovy expression which calls a sequence defined in the db. In the catalog screen you see the catalog together with all images added to this catalog. Here you can remove the whole catalog. The image data is deleted too in this case.

Create Catalog

Create Catalog

Once you have a catalog created you can add images or other files to it by using the ‘New Image’ button.

Create Image

Create Image

When adding a new image to a catalog you can specify a name for the image, the content type which will be read from the file once you hit the upload button. The image id is assigned by a groovy expression, the catalog id is populated by the master record, the catalog. As there is no visible image of the data in this version, an output text shows you if data has already been uploaded (Image Data available/not available).
This concludes the short run through the sample application.
The following db diagram shows the two tables involved (CATALOG and IMAGES) and their 1:* relationship. For reference I added the two sequences which generate the primary key for the tables.

DB Diagram

DB Diagram

Next is the DML to generate the two tables and the sequences. You can add the tables to an existing DB or create a new schema and add them their. If you later download the source code you’ notice, that I added the DML to the well known HR schema. If you use an other schema, you have to change the db connection accordingly.

CREATE TABLE CATALOG 
(
  CATALOG_ID NUMBER(12) NOT NULL 
, CATALOG_NAME VARCHAR2(200 CHAR) NOT NULL 
, CONSTRAINT CATALOG_PK PRIMARY KEY 
  (
    CATALOG_ID 
  )
  ENABLE 
);

CREATE UNIQUE INDEX CATALOG_PK ON CATALOG (null ASC);

CREATE SEQUENCE CATALOG_SEQ INCREMENT BY 1 START WITH 100 NOCACHE;

CREATE TABLE IMAGES 
(
  IMAGE_ID NUMBER(12) NOT NULL 
, IMAGE_NAME VARCHAR2(200 CHAR) NOT NULL 
, CONTENT_TYPE VARCHAR2(50 CHAR) 
, IMAGE_DATA BLOB 
, CATALOG_ID NUMBER(12) NOT NULL 
, CONSTRAINT IMAGES_PK PRIMARY KEY 
  (
    IMAGE_ID 
  )
  ENABLE 
);

CREATE UNIQUE INDEX IMAGES_PK ON IMAGES (null ASC);

ALTER TABLE IMAGES
ADD CONSTRAINT IMAGES_CATALOG_FK FOREIGN KEY
(
  CATALOG_ID 
)
REFERENCES CATALOG
(
  CATALOG_ID 
)
ENABLE;

CREATE SEQUENCE IMAGE_SEQ INCREMENT BY 1 START WITH 100 NOCACHE;

Finally here are the task flows which build the first part of the sample application:

Task flows

Task flows

The start page ‘Catalog’ contains a region (catalog-task-flow-description.xml) which is used to add or edit catalogs and to add or edit images for a catalog.

This concludes part 1. In part two I describe in detail how to implement the file upload and download feature and store the data in a blob column in the db.

To be continued…