Handling images/files in ADF (Part 5)

I received a couple of questions regarding the handling of the images directly after upload for the sample application done in part 1-4.

    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

The sample application finished in part 3 (part 4 is a backport to JDev 11gR1 only) has one minor glitch: it doesn’t display an uploaded image directly to the user after uploading it. The user has to commit the data after insert or update of an image before the image becomes visible. Users like to see the newly uploaded image before committing the row. This allows the users to cancel the change or select and upload another image. In this 5th part of the series we implement this.

Before we start to implement let’s talk about how to implement this enhancement. Why isn’t it possible to upload the image data into the blob and then just show the image from the blob via the servlet (see part 3)?
The problem is that the BlobDomain uses a stream to read the data uploaded from the user. This stream can only be read after the BlobDomain is saved, meaning after the commit.

The solution we implement in this part stores the uploaded data (inserted or updated) in a temporary file on the server. Then the server uses the image data from the temporary file to visualize the data. This sounds easy enough, however there is some house keeping to do to make it work.

First we have have to find a place (folder) where we can store the uploaded data until it’s stored in the db or the operation is canceled. Then we need to distinguish which data to show from the servlet (file or blob). Finally we have to clean up the temporary file when we are done.

Lets dive into the implementation. We start from the application at the end of part 3. As the current JDeveloper version is 12.1.3 we do the implementation in this version. The first task is to migrate the old application to 12.1.3. This is done automatically when opening the old work space in JDev 12.1.3 by answering the ‘OK’ to the migration popup. Nothing need to be done here. However, when you download  the work space you’ll notice some clean up I did, like changing the old af:commandButton to the new af:button.

One thing to notice is that the Apache Commons-IO version is updated to 2.4. This update made one other change necessary in the weblogic-application.xml file.

  <prefer-application-packages>
    <package-name>org.apache.commons.io</package-name>
  </prefer-application-packages>

This entry allows the application to use the included commons-io jar to be loaded before the already available commons-io jar, of an older version, in WebLogic server 12.1.3.

Here are the steps we take to implement the tasks:
1) Save the uploaded data to a temporary file as well as to the blob. This is done for convenience. It’S possible to store the data first in the temporary file and only copy it to the BlobDomain when the user commits the changes.
We implement a new java class UploadBlob which holds the BlogDomain and the path to the temporary file. This class also allows to test if a temporary is available.

package de.hahn.blog.uldl.view.types;

import oracle.jbo.domain.BlobDomain;

/**
 * This type class holds the BlogDomain and a path to a temporary file holding the uploaded image data
 */
public class UploadBlob {
    /**
     * Holds the uploaded data
     */
    BlobDomain dataBlob;

    /**
     * Path to the temporary file if availabe
     */
    String tempFile;

    /**
     * C'tor.
     */
    public UploadBlob() {
        super();
        tempFile = null;
        dataBlob = null;
    }

    /**
     * Gets the status of the temporary file
     *
     * @return true if a temporary file is available, false otherwise
     */
    public Boolean getTempFileAvailabe() {
        return (tempFile != null ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * @param inageBlob
     */
    public void setInageBlob(BlobDomain dataBlob) {
        this.dataBlob = dataBlob;
    }

    /**
     * Gets the BlobDomain holding the uploaded data
     * @return
     */
    public BlobDomain getDataBlob() {
        return dataBlob;
    }

    /**
     * Sete the path to the temporary file holding the uploaded data
     * @param tempFile path to the temporary file
     */
    public void setTempFile(String tempFile) {
        this.tempFile = tempFile;
    }

    /**
     * Getter for path to temp file holding the data of the uploaded data
     * @return path to the temporary file holding the uploaded data
     */
    public String getTempFile() {
        return tempFile;
    }
}

2) Use this class in the ImageBean.java class where the uploaded data is read. This happens in the valueChangeListener uploadFileValueChangeEvent(ValueChangeEvent valueChangeEvent).

    /**
     * @param valueChangeEvent
     */
    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("ImagesView2Iterator");
        Row newRow = lBinding.getCurrentRow();
        // set the file name
        newRow.setAttribute("ImageName", fileName);
        // create the BlobDomain and set it into the row
        UploadBlob blob = createBlobDomain(file, Boolean.TRUE);
        newRow.setAttribute("ImageData", blob.getDataBlob());
        // set the mime type
        newRow.setAttribute("ContentType", contentType);
        String tmp = (blob.getTempFileAvailabe() ? blob.getTempFile() : null);
        setTemporaryFileVar(tmp);
        UIComponent ui = (UIComponent) valueChangeEvent.getSource();
        // PPR refresh a jsf component
        ui = ui.getParent();
        AdfFacesContext.getCurrentInstance().addPartialTarget(ui);

    }

Instead of reading the data into the BlobDomain a changed method createBlobDomain is called (line 18). the method now returns an instance of the new class UploadBlob. Below is the code of the new method:

    private UploadBlob createBlobDomain(UploadedFile file, Boolean createTempFile) {
        // init the internal variables
        InputStream in = null;
        OutputStream outTmp = null;
        UploadBlob blobDomain = null;
        OutputStream out = null;
        File tempfile = null;
        logger.info("Starting to create UploadBlog from data...");
        try {
            logger.info("... create BlobDomain...");
            blobDomain = new UploadBlob();
            // Get the input stream representing the data from the client
            in = file.getInputStream();
            // if a temporary file should be created , we do this first as we can't get
            // data data back from the blob until we commit the row. in the next step we
            // write the upload data to a temp file and then copy it into the blob
            if (createTempFile) {
                logger.info("... Creating temporary file...");
                File tempdir = FileUtils.getTempDirectory();
                String ext = FilenameUtils.getExtension(file.getFilename());
                if (!ext.isEmpty()) {
                    ext = "." + ext;
                }
                logger.info("... set extension to " + ext + "...");
                tempfile = File.createTempFile("upl", ext, tempdir);
                logger.info("... " + tempfile.getAbsolutePath() + "...");
                // set path to temporary file
                blobDomain.setTempFile(tempfile.getAbsolutePath());
                FileOutputStream fileOutputStream = FileUtils.openOutputStream(tempfile);
                logger.info("... copy data to temporary file...");
                IOUtils.copy(in, fileOutputStream);
                in = FileUtils.openInputStream(tempfile);
                logger.info("... set inputstream for blog to temporary file...");
            }
            // create the BlobDomain datatype to store the data in the db
            blobDomain.setInageBlob(new BlobDomain());
            // get the outputStream for hte BlobDomain
            out = blobDomain.getDataBlob().getBinaryOutputStream();
            // copy the input stream into the output stream
            logger.info("... copy data to BlobDomain ...");
            /*
             * 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);
            logger.info("... Finished OK");
        } catch (Exception e) {
            logger.severe("Error!", e);
            if (tempfile != null) {
                // delete temp file on exception but don'T throw one if there is another exception
                logger.info("Deleted temporary file " + tempfile.getAbsolutePath());
                FileUtils.deleteQuietly(tempfile);
            }
        }
        // return the filled BlobDomain
        return blobDomain;
    }

Depending on the new boolean parameter passed to the method a temporary file is created and the uploaded data is first saved to the temporary file. After that the data is copied from the temporary file into the BlobDomain. At this point the path to the temporary file is saved in the new class for later reference. In case of an exception the temporary file is removed.
Finally in line 22 and 23 of the value change listener we check if a temporary file was generated and we set the path to it to a pageDef variable (see Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef). For this we use the code below.

    /**
     * Set the temporary file name into a page variable for later use
     * @param name
     */
    private void setTemporaryFileVar(String name) {
        // set pathto temporary file to page variable
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("TemporaryFile1");
        if (attr != null) {
            attr.setInputValue(name);
        }
    }

The variable is used in the af:image component in the editImage.jsff fragment

                   <af:image source="/render_image?id=#{bindings.ImageId.inputValue}&tmp=#{bindings.TemporaryFile1.inputValue}" id="i1"
                              shortDesc="#{bindings.ImageName.hints.tooltip}" inlineStyle="width:200px;" partialTriggers="cb3" visible="true"/>

here the path to the temporary file is passed to the servlet as second parameter ‘tmp’. In lines 24-27 of the value change listener we send a ppr to the parent component of the af:image to show the now uploaded image.

Another thing to do is to cleanup after the user either cancel or commit the changes. This is done in the cancel_action() or the commit_action() in the ImageBean. Here we call the deleteTemporaryFile() method which checks the existence of a temporary file and deletes it.

    /**
     * delete the temporary file if is present
     */
    public void deleteTemporaryFile() {
        String tempfile = getTemporaryFileVar();
        removeTemporaryFile(tempfile);
        setTemporaryFileVar(null);
    }

3) The final part of the implementation is done in the servlet which is used to get the data back to the client. This is simple as we read the second parameter passed to the servlet. If it’s not empty we always read the image data from the temporary file. If the parameter is empty the servlet gets the data by reading the row from the DB and read the data from the blob. Here are the relevant parts from the servlet:

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        StringBuilder sb = new StringBuilder(100);
        String appModuleName = "de.hahn.blog.uldl.model.facade.ULDLAppModule";

        sb.append("ImageServlet ").append(appModuleName);

        try {
            // get parameter from request
            Map paramMap = request.getParameterMap();
            oracle.jbo.domain.Number id = null;
            String tmporaryFilePath = "";
            if (paramMap.containsKey("id")) {
                String[] pVal = (String[]) paramMap.get("id");
                id = new oracle.jbo.domain.Number(pVal[0]);
                sb.append(" id=").append(pVal[0]);
            }
            // check if we find a temporary file name. In this case we allways use this!
            if (paramMap.containsKey("tmp")) {
                String[] pVal = (String[]) paramMap.get("tmp");
                tmporaryFilePath = pVal[0];
                sb.append(" tmp=").append(pVal[0]);
            }

            OutputStream outputStream = response.getOutputStream();
            InputStream inputStream = null;
            BlobDomain image = null;
            String mimeType = null;
            // no temporary file path given, read everything from DB
            if (tmporaryFilePath.isEmpty()) {
                // get method action from pagedef
                BindingContext bindingContext = BindingContext.getCurrent();
                DCBindingContainer amx = bindingContext.findBindingContainer("de_hahn_blog_uldl_view_image_dummyPageDef");
                JUCtrlActionBinding lBinding = (JUCtrlActionBinding) amx.findCtrlBinding("getImageById");
                // set parameter
                lBinding.getParamsMap().put("aId", id);
                // execute method
                lBinding.invoke();
                // get result
                Object obj = lBinding.getResult();
                ImageAccessViewRow imageRow = (ImageAccessViewRow) obj;

                // Check if a row has been found
                if (imageRow != null) {
                    // Get the blob data
                    image = imageRow.getImageData();
                    mimeType = imageRow.getContentType();
                    // if no image data can be found and no temporary file is present then return and do nothing
                    if (image == null) {
                        mLogger.info("No data found !!! (id = " + id + ")");
                        return;
                    }
                    inputStream = image.getInputStream();
                } else {
                    mLogger.warning("No row found to get image from !!! (id = " + id + ")");
                    return;
                }
                sb.append(" ").append(mimeType).append(" ...");
                mLogger.info(sb.toString());
            } else {
                // read everything from temporary file path
                mimeType = ContentTypes.get(tmporaryFilePath);
                File file = FileUtils.getFile(tmporaryFilePath);
                FileInputStream fileInputStream = FileUtils.openInputStream(file);
                inputStream = fileInputStream;
            }

            // Set the content-type. Only images are taken into account
            response.setContentType(mimeType + "; charset=utf8");
            IOUtils.copy(inputStream, outputStream);
            if (tmporaryFilePath.isEmpty()) {
                // cloase the blob to release the recources
                image.closeInputStream();
            }
            inputStream.close();
            // flush the outout stream
            outputStream.flush();
        } catch (Exception e) {
            mLogger.log(Level.WARNING, "Fehler bei der Ausführung: " + e.getMessage(), e);
        } finally {
            mLogger.info("...done!");
        }
    }

The gallery below shows the new work flow.

The work space for part 5 can be downloaded from the ADF EMG Sample side BlogUploadDownload_12.1.3V4.zip.
Or if you are in GIT you can get the work space from GitHub BlogUploadDownload_12.1.3V4

Advertisements

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