Lazy Initalizing Beans

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

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

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

import javax.servlet.ServletContext;

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

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

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

    private String myName;

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

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

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

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

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

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

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

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

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

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

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

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

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

Advertisements

JDeveloper: Showing a Popup when Selecting an af:selectOneRadio

A question on the new OTN JDeveloper and ADF forum (or space if you like the new name better) inspired this post.

Use Case
The use case is to show a popup each time the user clicks on one of the radiobuttons of a button group. This popup should show an inputText component to let the user enter some text for the selection. After the selection the text is shown on the page under the button group.
To make it more interesting, it was not ask for this in the question, the popup should show which radio button was clicked.
UPDATE
An additional question on the OTN forum asks how to display the label of the selected radiobutton and not it’s value. I updated the workspace to show how to implement this.

Implementation UPDATE
The use case sounds easy first: add a showPopupBehavior to the selectItem to show the popup and show the selected radioValue as the title of the dialog inside the popup. Problem with this solution is that a selectItem can’t handle client listeners, which a showPopupBehavior is under the hood. If you try this you get an error

Caused by: javax.servlet.jsp.JspException: ADF_FACES-60023:Component: RichSelectItem[UIXFacesBeanImpl, id=si10] does not support client listeners.

Putting the showPopupBehavior onto the af:selectOneRadio isn’t a solution either as this would would allow to show the popup, but would prevent the selection of the radio button. This is because the showPopupBehavior is a client behavior tag which prevents the event to
go further.

The solution to the problem is to use a valueChangeListener on the af:selectOneRadio and show the popup from the bean code. Before showing the popup we need to process the value change event to get the selected value in the dialog of the popup.

The UI part looks like

        <af:panelGroupLayout id="pgl2">
          <af:selectOneRadio label="Activation" id="sor1" value="#{bindings.SelectedActivation1.inputValue}"
                             inlineStyle="border-width:thin; border-color:Blue; border-style:solid;"
                             valueChangeListener="#{RadiobuttonPopupBean.activationChangedListener}" autoSubmit="true">
            <af:selectItem label="Active" value="active" id="si1"/>
            <af:selectItem label="Inactive" value="not active" id="si2"/>
          </af:selectOneRadio>
          <af:outputText value="Last reason: #{bindings.PopupText1.inputValue}" id="ot1" partialTriggers="d1"/>
        </af:panelGroupLayout>
        <af:popup id="p1" contentDelivery="lazyUncached" binding="#{RadiobuttonPopupBean.radioPopup}">
          <af:dialog id="d1" title="Selected: #{bindings.SelectedActivation1.inputValue}">
            <af:inputText label="Reason" id="it1" value="#{bindings.PopupText1.inputValue}"/>
          </af:dialog>
        </af:popup>

The listener code in the bean, which is created in request scope as it contains a reference to a ui component, look like

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

import oracle.adf.view.rich.component.rich.RichPopup;

import org.apache.myfaces.trinidad.util.ComponentReference;

public class RadiobuttonPopupBean {
    private ComponentReference radioPopup;

    public void setRadioPopup(RichPopup radioPopup) {
        this.radioPopup = ComponentReference.newUIComponentReference(radioPopup);
    }

    public RichPopup getRadioPopup() {
        if (radioPopup != null)
            return (RichPopup)radioPopup.getComponent();

        return null;
    }

    public void activationChangedListener(ValueChangeEvent valueChangeEvent) {
        // process updates to get the selected value inside the popup
        FacesContext contxt = FacesContext.getCurrentInstance();
        valueChangeEvent.getComponent().processUpdates(contxt);
        //show the popup
        RichPopup.PopupHints hint = new  RichPopup.PopupHints();
        getRadioPopup().show(hint);
    }
}

The application running look like


You see that the popup shows the value of the selected radio button as the title and the entered text in the popup is shown as ‘last reason’ below the radio button group. This ‘magic’ is done by simply adding a partial trigger to the outputText component which is listening to the dialog inside the popup.
As the sample don’t store the value of the selection of the radio group or the entered text in the db, I use the variable iterator to store these values. More about this technique can be found in my blog

UPDATED IMPLEMENTATION
As the use case changed a bit I changes the solution in a way to show how to implement this. First step is that I added another attribute to the variables iterator to store the selected label. The attribute is name ‘SelectedActivation1’ and is of type String. This attribute is needed as it’s not easyly possible to get the selected label of the radiobutton outside the selection listener. The changed valueChangeListener looks like the code below:

    public void activationChangedListener(ValueChangeEvent valueChangeEvent) {
        // process updates to get the selected value inside the popup
        FacesContext contxt = FacesContext.getCurrentInstance();
        valueChangeEvent.getComponent().processUpdates(contxt);
        //get the selected lable from the radio button
        // for this we need to iterate over the children of hte af:selectOneRadio
        // and find the child which has the same value as the new value
        RichSelectOneRadio rsoc =
            (RichSelectOneRadio)valueChangeEvent.getSource();
        List childList = rsoc.getChildren();
        String newVal = (String)valueChangeEvent.getNewValue();
        for (int i = 0; i < childList.size(); i++) {
            if (childList.get(i) instanceof RichSelectItem) {
                RichSelectItem csi = (RichSelectItem)childList.get(i);
                if (((String)csi.getValue()).equals(newVal)) {
                    // get the binding container
                    BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

                    // get an ADF attributevalue from the ADF page definitions
                    AttributeBinding attr = (AttributeBinding)bindings.getControlBinding("SelectedLabel1");
                    // and store the label there
                    attr.setInputValue(csi.getLabel());
                }
            }
        }
        //show the popup
        RichPopup.PopupHints hint = new RichPopup.PopupHints();
        getRadioPopup().show(hint);
    }

The images below showing the changes application

You can download the workspace from the ADF EMG Sample Project BlogTestRadiobutton.zip. The sample uses the HR DB schema (even as this is not really needed for the sample).

JDeveloper 11.1.1.5.0 : Use Router to Create New Row or Edit Existing

Based on a question on the OTN ADF forum I wrote a small sample to show how to use a router in a bounded task flow to either edit an existing row, or create a new row and present it to enter data.

Use Case
Based on a query which returns either zero or one row an edit form should should be presented to the user. This form show the data from the row found, or an empty form to allow creation of a new row.

Implementation
The sample workspace, which you can download from the ADF Samples webside using the link at the end of this post, uses a very simple model. It contains only one view object, based of the LOCATIONS table of the HR schema.
The view object has one custom method implemented which search for a location record by an id. This method is used as query which returns either one row (if the location is found) or no row (if the location is not found).
The UI is build with one page which has a splitter component. On the left facet we find an simple form which we use to input the location id to search for and a button to execute the query. To allow to save or cancel the actions we add a rollback and a commit button. On the second facet we see a region which shoes the panelForm to either edit the existing record or to enter data into a new created record.
The image below shows the page layout.

General page layout

General page layout

Running

The next image shows the running application with a location id found

Running application with Location Id found

Running application with Location Id found

and then with a location id not found

Location ID not found

Location ID not found

You clearly see the empty from to enter new row data.

Now, to implement this the first splitter facet has a button to executes the query (find location by id) with the id given in the inputText. The region on the second splitter facet refreshes itself as we set the ‘Refresh’ property to ‘ifNeeded’. To make this work we need to change an input parameter to the region. This is done by binding the input parameter of the region to the inputText component for the location id.

Setup of input parameter of the region

Setup of input parameter of the region

So whenever the input parameter changes the region gets restarted. this effect we use in the bounded task flow to check if we have to create a new row and thus show a empty form, or to just edit an existing row. For this the bounded task flow uses a router component as start activity. This router checks if the iterator of the locations view object contains zero rows or more.

Bounded task flow

Bounded task flow

The router uses the estimatedRowCount of the underlying view object in an expression to check the number of rows:

EL to check if iterator has zero rows

EL to check if iterator has zero rows

The router navigates to the ‘insert’ case if the EL evaluated to true, to the ‘edit’ case otherwise. That concludes the use case.

You can down load the sample work space from ADF Samples Repository. The sample was build using JDeveloper 11.1.1.5.0 but should work using 11.1.1.6.0 and 11.1.2.x too. It uses the HR schema and you need to correct the db credentials to run the sample.

JDeveloper & ADF: Carefully Select the Data Control Scope of Bounded Task Flows

A customer asked me to look into an error which showed up in one of their applications. Without going into the details of real use case I like to bring this issue to your attention as it might happen to you too.

Use Case
I set up a mock use case to show the problem as this: A page (.jspx or .jsf) includes one (or more) other task flows as a region. The base page retrieves some data using a method call exposed in the data control, which should be displayed in the region for information purpose.

Implementation
If the task flows share their data controls this is not a big deal to implement this. You can download the sample using the link provided at the end of this blog. The sample uses the HR schema and was build using JDev 11.1.1.5.0 and was additional tested with JDev 11.1.1.1.6.0 and JDev 11.1.2.2.0, which show the same behavior.

On the base page I set up an inputText which I use to pass a parameter to the method (callMeTest) which retrieves some info from the application module. The callMeTest method simply returns the passed parameter together with a ‘nice’ message:

    public String callMeTest(String name) {
        _logger.info("Called with: "+name);
        return "Call me Test "+name;
    }

The return value is then displayed on the base page at the bottom under the button and should be displayed in the region, under the table, too. The sample just uses one region. When no value is present ‘****’ is used as parameter. The image below shows the application after starting it.

Start Screen

Start Screen

After entering a value into the inputText field below the table and hitting the ‘callMeTest’ button underneath it, you see that the return value is displayed below the button on the base page and under the table inside the region. If the button is hit with another value, both values are updated.

After entering a value and hitting the button

After entering a value and hitting the button

Nothing unusual so far. Now, for some unknown reason, after a change to another part the application, that functionality did not work any longer. The value in the base page still updates after the call to the method, but the value in the region stopped updating.

Screen after next deployment

Screen after next deployment

The reason for this behavior is a simple one. A look into the task flow properties of the region, revealed that the checkmark for the ‘Share data controls with calling task flow’ was not set, meaning that the task flow get its own data control frame. The result is that the method outcome from the base page is not visible in the region.

TaskFlow properties (1)

TaskFlow properties (1)

The question was how did this happen, when the change which results in the new deployment had nothing to do with this part of the application?

Analysis
It turned out, that when you create a new bounded task flow this property value is set to ‘default’. This is shown in the ‘Overview’ of the task flow as bluish shade in the check box. No checkmark is set and a look into the source reveals that the whole section

    <data-control-scope>
      <....../>
    </data-control-scope>

is just missing from the XML file.

TaskFlow after creation

TaskFlow after creation

Once you click on this property in the property editor you can just switch between ‘isolated’ or ‘shared’.

TaskFlow properties (2)

TaskFlow properties (2)

The only possible way to get the bluish shade back is to delete the section in the source view of the task flow.

TaskFlow properties in 'Source Mode'

TaskFlow properties in ‘Source Mode’

So this checkbox has three states ‘bluish’, ‘isolated’, and ‘shared’. Well, there is a forth state which you get if you open the property editor in source mode and select ‘default’. The following table shown all states together with their meaning:

State  Meaning
Bluish  Shared
Check marked  Shared
Unchecked not bluish  Isolated
Data-control-scope empty  Isolated

While the application works according to the meaning of the flag, I find it not intuitive how JDev handles this setting. The programmer who made the change to the application clicked this property, but meant to do so on a different task flow. So he unchecked the ‘Share data controls with calling task flow’ property as he remembered that it was not set before.

Recommendation
My advise is to set the flag whenever you create a task flow so you know which share mode the task flow uses. This avoids errors like the one described in this blog.

UPDATE
In the meantime Chris Muir raised the issue with the UX specialists (Oracle intern) and Bug 14390576 has been raised for this issue.
Thank you Chris!

The sample uses the HR schema and was build with JDeveloper 11.1.1.5.0, tested with 11.1.1.6.0 and 11.1.2.2.0 . You can download the sample work space form ADF Samples: Source Code

ADF: How to find out which ADF version is installed on a manged WebLogic server

As a developer and architect I’m working with different version of JDeveloper and WebLogic servers. Sometimes I switch version multiple times a day. Most customers have multiple deployment targets (development, QS and production) on different Weblogic servers. This make it sometimes hard to keep track of the different versions.

I searched for a way to find out which ADF Runtime is installed on a given WebLogic server. If you look at the console application or the Enterprise Manager application you’ll see no difference. Sometimes you can guess which version is running if you know which other application version is running under which server version. Still there should be a better solution.
Searching (my friend Google) the only way I found was to check the MANIFEST.MF of a specific jar on the server. As I don’t have access to a command shell on all servers this method won’t work for me. Then I stumbled upon the PrintVersion class (http://docs.oracle.com/cd/E15051_01/apirefs.1111/e10653/oracle/jbo/common/PrintVersion.html) which prints the BC4J version. A bit more digging led to the class oracle.jbo.Version which was mentioned in a really old blog entry from 2003 (http://radio-weblogs.com/0123729/stories/2003/07/10/associationConsistencyOnNondetailVos.html). This class is still available and hold information about the ADF version like the build label and BC4J version. Some of the info is stored in static final variables and only one public method Version.getAsAtring() is available.

This class is the solution I was looking for. As I couöd not build an ADF application, but needed a solution which is deployable to a managed server, I decided to implement a web service which returns the BC4J version and the build label of the ADF Runtime Library deployed ton the WebLogic server.
To make this work, I could not directly access the static final fields of the Version class. Doing this would compile the version information from the ADF Runtime Library at compile time into the web service. To access the build label which has no getter method I need to use reflection. Finally I wrote this Class

package de.hahn.blog.adfversionws;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javax.jws.WebMethod;
import javax.jws.WebService;

import oracle.jbo.Version;


@WebService
public class AdfVersion
{
    public AdfVersion()
    {
        super();
    }

    @WebMethod
    public String getVersion()
    {
        return Version.getAsString();
    }
    
    @WebMethod
    public String getBuildLabel()
    {
        Class<?> cl = null;
        try
        {
            cl = this.getClass().getClassLoader().loadClass("oracle.jbo.Version");
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
            return e.getMessage();
        }

        Method[] lDeclaredMethods = cl.getDeclaredMethods();
        Field[] lDeclaredFields = cl.getDeclaredFields();
        try
        {
            Field lDeclaredField = cl.getDeclaredField("buildLabel");
            String y = (String) lDeclaredField.get(lDeclaredField);
            return y;
        }
        catch (IllegalAccessException e3)
        {
            e3.printStackTrace();
            return e3.getMessage();
        }
        catch (NoSuchFieldException e)
        {
            e.printStackTrace();
            return e.getMessage();
        }
    }    
}

To make the web service independent from the Jdeveloper ADF Runtime I reference the ADF Runtime Library which is deployed on the server as shared library ‘adf.oracle.domain’. For this I created a weblogic-application.xml descriptor and add the library ref to it.

<?xml version = '1.0' encoding = 'UTF-8'?>
<weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-application http://xmlns.oracle.com/weblogic/weblogic-application/1.1/weblogic-application.xsd"
                      xmlns="http://xmlns.oracle.com/weblogic/weblogic-application">
  <library-ref>
    <library-name>adf.oracle.domain</library-name>
  </library-ref>
</weblogic-application>

Now, after deployment of the EAR you can test the web service on different servers with different ADF Runtime Libraries deployed and see something like

WLS 10.3.5 with ADF 11.1.1.5.0 Runtime Library

WLS 10.3.5 with ADF 11.1.1.5.0 Runtime Library


or
WLS 10.3.5 + Sherman Patch and ADF Runtime 11.1.2.1.0

WLS 10.3.5 + Sherman Patch and ADF Runtime 11.1.2.1.0

The ear should be deployable on all 10.3.x WebLogic servers.

You can download the workspace (build with JDev 11.1.2.1.0) from here: blogadfversoinws-zip.doc
Please rename the file to ‘.zip’ after downloading it! There in no DB connection needed.

JDEV: af:query hide some attributes from query panel but show them in the result table

An interesting question came up today in the OTN JDev forum. The use case is to use an af:query component to qurey a db table, but make some of the attributes available in the table invisible in the query panel.
You can archive this using viewCriteria, but in this case you still can reach the other attributes in advanced mode.
The way to go is to make the attribute which you want to hidenot queryable in the view definition. For this we open the VO and select the attribute node.

Queryable Attribute in a VO

Queryable Attribute in a VO

Here we remove the check mark from the ‘Queryable’ checkbox like I did for the JobId attribute in the picture below

Not queryable attribute in a VO

Not queryable attribute in a VO

Now when we use this VO in a af:query component we see all queryable attributes but not the ones where we removed the check box. In the sample I removed the checkmark for JobId, ManagerId and DepartmentId in the EmployeesView. The resulting query panel which I build using the ‘All Queriable Attributes’ from the ‘Names Criteria’ section of the EmployeesView

Build Query Panel

Build Query Panel

looks like the picture below in the running application. As you can see JobId, ManagerId and DepartmentId are not part of the query panel but can be seen the result table.

Query Panel

Query Panel

In advanced mode you can’t add the missing attributes

Query Panel Advanced Mode

Query Panel Advanced Mode

You can remove the checkbox from the EO too, but this would mean that no VO build on this EO can query the attributes. If you only remove the checkbox in the VO you can build an other VO based on the same EO and make all attributes queriable.

You can download the sample workspace, build with JDev 11.1.2.1.0 and depending on hte HR db schema, from here: BlogCascadingTable.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

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…