JDev & ADF Goodies

22. January 2012

JDev: Custom selectionListener for ViewObjects in ‘RangePaging’ mode

Lately a question on the Oracle JDev forum came up, asking for a solution for a problem with a ViewObject in ‘RangePaging’ mode and a single selection af:table defined on this ViewObject. The problem is to get the current selected row in such a case. Under some circumstances (which are not always reproducible) using the default

#{bindings.YOUR_VIEWNAME.collectionModel.makeCurrent} 

doesn’t mark the selected record and a call in a bean to get the selected record returns null:
BindingContext lBindingContext = BindingContext.getCurrent();
BindingContainer lBindingContainer = lBindingContext.getCurrentBindingsEntry();
DCBindingContainer bindingsIte = (DCBindingContainer) lBindingContainer ;
DCIteratorBinding dciter = bindingsIte.findIteratorBinding("YOUR_VIEWNAMEIterator");
Row row = dciter.getCurrentRow();
if (row == null) {
    return null;    // no current row
}

For ViewObjects in ‘Scrollable’ mode you get the selected record without any problem. ViewObjectes in ‘RangePaging’ mode are mostly used for tables which contain many rows and the use case doesn’t allow to filter the result set to a reasonably number. The ‘RangePaging’ option is a tuning parameter in the ViewObject definition
Set a ViewObject to 'RangePaging' mode

Set a ViewObject to 'RangePaging' mode


I run into this condition myself and use the following work around:

  1. remove the current selectionlistener from the table (#{bindings.YOUR_VIEWNAME.collectionModel.makeCurrent})
  2. define a new selection listener (use the small arrow on the right side) in a bean of your choice. The scope of the bean has to be view or pageflow depending on where you need access to the selected row
  3. in the new selectionListener you get the selected row from the event, get the key of the row and store it in a bean attribute
  4. when you need the selected row you use the stored row key and work with this. If you need attributes from the row you have to query the row again, as you only have the key

If you only need the key of the row you can e.g. pass this key to a service method defined in the application module or the ViewObject. Here is a sample of such a selection listener:

public void singleSelectionListener(SelectionEvent selectionEvent) {
        RowKeySet rksAdd = selectionEvent.getAddedSet();
        if (rksAdd.isEmpty())
            return;  // no selection
 
        Object[] it = rksAdd.toArray();
        // as this is for single selection there should only be one, but...
        for (Object obj: (List) it[0]) {
            mLogger.fine("Selected :" + obj);  // log selected row
            Key k = (Key) obj;   // the object is the row key
            Object[] kv = k.getKeyValues();  // get the key value for later
            // strore the key value in a bean attribute: mLastSelectedOID is defined in the bean
            mLastSelectedOID = (Integer) kv[0]; // store the key value (if the key has multiple parts you need to store them all)
        }
    }

The variable mLastSelectedOID is defined in the bean. The type of the attribute depends on the type the primary key of the table has. If you like you can generate getter/setter methods for the attribute and use them instead of assigning the value directly.

11. January 2012

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

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

Query Panel Advanced Mode

Query Panel Advanced Mode


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

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

Skin creation

Skin creation


Skin Properties

Skin Properties

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

Change Element

Change Element

Preview of af:query with skin applied

Preview of af:query with skin applied

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

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

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

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

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

Apply StyleClass

Apply StyleClass

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

6. January 2012

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!

29. December 2011

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: BlogCascadingTable.zip
Please rename the file to ‘.zip’ after downloading it!

23. December 2011

WLS 10.3.x: Deployment faild with ‘Invalid Archive’

Filed under: JDeveloper,Uncategorized,Weblogic Server — Timo Hahn @ 22:22
Tags: ,

I run into a strange problem today while working an a presentation about a ‘One Click Build’ process. Part of the presentation is building an EAR archive which can be deployed to a WLS server (10.3.5 + Sherman + update2) running under Ubuntu Linux 11.04. The application is build with JDev 11.1.2.1.0. First time I build the EAR and deployed it to my test server all went OK.
I added some files to my project rebuild the ear and got the following

Error dialog

Error dialog


A look into the log revealed nothing to shed light on this error
Error Log

Error Log


For all searching for this exception I include the stack trace as text here too:
java.io.IOException: Exception in AppMerge flows' progression
at weblogic.deploy.api.internal.utils.AppMerger.getMergedApp(AppMerger.java:70)
at weblogic.deploy.api.model.internal.WebLogicDeployableObjectFactoryImpl.createDeployableObject(WebLogicDeployableObjectFactoryImpl.java:181)
at weblogic.deploy.api.model.internal.WebLogicDeployableObjectFactoryImpl.createDeployableObject(WebLogicDeployableObjectFactoryImpl.java:163)
at weblogic.deploy.api.tools.SessionHelper.initialize(SessionHelper.java:727)
at weblogic.deploy.api.tools.SessionHelper.initializeConfiguration(SessionHelper.java:556)
at weblogic.deploy.api.tools.SessionHelper.initializeConfiguration(SessionHelper.java:544)
at oracle.sysman.emas.sdk.picFramework.deploy.WLSDPConfigTreeManager._initialize(WLSDPConfigTreeManager.java:165)
at oracle.sysman.emas.sdk.picFramework.deploy.DPConfigTreeManager.<init>(DPConfigTreeManager.java:201)
at oracle.sysman.emas.sdk.picFramework.deploy.WLSDPConfigTreeManager.<init>(WLSDPConfigTreeManager.java:108)
at oracle.sysman.emas.sdk.picFramework.deploy.WLSDeployer._buildDPDeployConfigTree(WLSDeployer.java:741)
at oracle.sysman.emas.sdk.picFramework.deploy.WLSDeployer.buildup(WLSDeployer.java:471)
at oracle.sysman.emas.model.oc4j.deploy.DeployModelBase.buildup(DeployModelBase.java:876)
at oracle.sysman.emas.view.oc4j.deploy.DeployWizardSelectArchiveViewBean.goLoadArchive(DeployWizardSelectArchiveViewBean.java:1561)
at oracle.sysman.emas.view.oc4j.deploy.DeployWizardSelectArchiveViewBean.processCurrentStepAction(DeployWizardSelectArchiveViewBean.java:2287)
at oracle.sysman.emas.view.oc4j.deploy.DeployWizardTrainViewBean.doNext(DeployWizardTrainViewBean.java:441)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.el.parser.AstValue.invoke(Unknown Source)
at com.sun.el.MethodExpressionImpl.invoke(Unknown Source)
at org.apache.myfaces.trinidad.component.MethodExpressionMethodBinding.invoke(MethodExpressionMethodBinding.java:46)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at org.apache.myfaces.trinidad.component.UIXCommand.broadcast(UIXCommand.java:190)
at oracle.adf.view.rich.component.fragment.ContextSwitchingComponent$1.run(ContextSwitchingComponent.java:130)
at oracle.adf.view.rich.component.fragment.ContextSwitchingComponent._processPhase(ContextSwitchingComponent.java:461)
at oracle.adf.view.rich.component.fragment.ContextSwitchingComponent.broadcast(ContextSwitchingComponent.java:134)
at oracle.adf.view.rich.component.fragment.UIXInclude.broadcast(UIXInclude.java:112)
at oracle.adf.view.rich.component.fragment.ContextSwitchingComponent$1.run(ContextSwitchingComponent.java:130)
at oracle.adf.view.rich.component.fragment.ContextSwitchingComponent._processPhase(ContextSwitchingComponent.java:461)
at oracle.adf.view.rich.component.fragment.ContextSwitchingComponent.broadcast(ContextSwitchingComponent.java:134)
at oracle.adf.view.rich.component.fragment.UIXInclude.broadcast(UIXInclude.java:106)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:787)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1252)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._invokeApplication(LifecycleImpl.java:965)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._executePhase(LifecycleImpl.java:346)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:204)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.help.web.rich.OHWFilter.doFilter(Unknown Source)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.sysman.emSDK.license.LicenseFilter.doFilter(LicenseFilter.java:101)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.adf.model.servlet.ADFBindingFilter.doFilter(ADFBindingFilter.java:173)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.adfinternal.view.faces.webapp.rich.RegistrationFilter.doFilter(RegistrationFilter.java:122)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:468)
at oracle.adfinternal.view.faces.activedata.AdsFilter.doFilter(AdsFilter.java:60)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:468)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:293)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:199)
at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.sysman.emas.fwk.MASConnectionFilter.doFilter(MASConnectionFilter.java:41)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.adf.library.webapp.LibraryFilter.doFilter(LibraryFilter.java:180)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.sysman.eml.app.AuditServletFilter.doFilter(AuditServletFilter.java:179)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.sysman.eml.app.EMRepLoginFilter.doFilter(EMRepLoginFilter.java:203)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.sysman.core.model.targetauth.EMLangPrefFilter.doFilter(EMLangPrefFilter.java:158)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.sysman.core.app.perf.PerfFilter.doFilter(PerfFilter.java:141)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.sysman.eml.app.ContextInitFilter.doFilter(ContextInitFilter.java:542)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.security.jps.ee.http.JpsAbsFilter$1.run(JpsAbsFilter.java:111)
at oracle.security.jps.util.JpsSubject.doAsPrivileged(JpsSubject.java:313)
at oracle.security.jps.ee.util.JpsPlatformUtil.runJaasMode(JpsPlatformUtil.java:413)
at oracle.security.jps.ee.http.JpsAbsFilter.runJaasMode(JpsAbsFilter.java:94)
at oracle.security.jps.ee.http.JpsAbsFilter.doFilter(JpsAbsFilter.java:161)
at oracle.security.jps.ee.http.JpsFilter.doFilter(JpsFilter.java:71)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.dms.servlet.DMSServletFilter.doFilter(DMSServletFilter.java:136)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3715)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3681)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2277)
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2183)
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1454)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)
Caused by: weblogic.utils.compiler.ToolFailureException: Exception in AppMerge flows' progression
at weblogic.application.compiler.AppMerge.merge(AppMerge.java:172)
at weblogic.deploy.api.internal.utils.AppMerger.merge(AppMerger.java:88)

The other log files available didn’t help either. So started to remove the files I added after the last successful deployment. I tagged this version, so I new where to start. In the end I found the file:
Einführung.pdf
As you see the file contains a German special character ‘ü’. It turned out that an EAR file should not contain files with special characters in their name. I did not test this on a WLS running under Window, as I don’t have one installed, but I guess it’s working there as I did not get this error running the application on the integrated WLS under a WIN 7 64Bit system.

21. December 2011

JDeveloper 11.1.2.1.0: Stumbled uppon this Gem

Filed under: JDeveloper,Konfiguration — Timo Hahn @ 20:20
Tags: , ,

I don’t know why I never seen this before, but I like to share this (for me) hidden gem in the JDeveloper 11.1.2.1.0 IDE.
If you debug often I’m sure you like the possibility to change breakpoints so that they only hit under special conditions. In the older version you have to right click the breakpoint to get to the dialog to change the breakpoint conditions.

Old Breakpoint Edit Dialog

Old Breakpoint Edit Dialog

Well, today I happen to notice that using the current JDev 11.1.2.1.0 if you move the mouse over a breakpoint and wait for half a second you get this ‘Hoover’ edit breakpoint dialog.

New Hoover Edit Breakpoint Dialog

New Hoover Edit Breakpoint Dialog

The old dialog (which allows to reach all possible changes) can be reached as before using a right mouse click on the breakpoint.

For more information about how to manage breakpoints refer to the docs here.

Have fun playing with this.

16. December 2011

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

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:

  <servlet>
    <display-name>ImageServlet</display-name>
    <servlet-name>ImageServlet</servlet-name>
    <servlet-class>de.hahn.blog.uldl.view.frkext.servlet.ImageServlet</servlet-class>
    <load-on-startup>100</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>ImageServlet</servlet-name>
    <url-pattern>/render_image</url-pattern>
  </servlet-mapping>

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:

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

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 = "de.hahn.blog.uldl.model.facade.ULDLAppModule";
            String appModuleConfig = "ULDLAppModuleLocal";
            ApplicationModule am = null;
            ViewObject vo = null;
            try
            {
                am = Configuration.createRootApplicationModule(appModuleName, appModuleConfig);
                sb.append("ImageServletRooAppModule ").append(appModuleName);
                ULDLAppModuleImpl amULDL = (ULDLAppModuleImpl) am;
                vo = am.findViewObject("ImageAccessView1");
                if (vo == null)
                {
                    throw new Exception("ImageAccessView1 not found!");
                }
                ImageAccessView imageView = (ImageAccessView) vo;
                
                // get parameter from request
                Map paramMap = request.getParameterMap();
                oracle.jbo.domain.Number id = null;
                if (paramMap.containsKey("id"))
                {
                    String[] pVal = (String[]) paramMap.get("id");
                    id = new oracle.jbo.domain.Number(pVal[0]);
                    sb.append(" id=").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("No row found to get image from !!! (id = " + id + ")");
                    return;
                }
                sb.append(" ").append(mimeType).append(" ...");
                mLogger.info(sb.toString());

                // Set the content-type. Only images are taken into account
                response.setContentType(mimeType + "; charset=utf8");
                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("Fehler bei der Ausführung: " + e.getMessage());
            }
            finally
            {

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

            mLogger.info("...done!");
        }
    }

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.
<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="11.1.2.60.81"
                id="xyz_dummyPageDef" Package="de.hahn.blog.uldl.view.pageDefs">
  <parameters/>
  <executables>
    <variableIterator id="variables"/>
  </executables>
  <bindings>
  </bindings>
</pageDefinition>

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

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

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("ImageServlet ");

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

                // 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;
                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("No data found !!! (id = " + id + ")");
                        return;                        
                    }
                }
                else
                {
                    mLogger.warning("No row found to get image from !!! (id = " + id + ")");
                    return;
                }
                sb.append(" ").append(mimeType).append(" ...");
                mLogger.info(sb.toString());

                // Set the content-type. Only images are taken into account
                response.setContentType(mimeType + "; charset=utf8");
                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, "Fehler bei der Ausführung: " + e.getMessage(), e);
            }
            finally
            {

            }

            mLogger.info("...done!");
        }
    }

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("de_hahn_blog_uldl_view_image_dummyPageDef");

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

27. November 2011

JDEV 11.1.2.1.0: Using router to conditionally set navigation target

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

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

Router Back Navigation

Router Back Navigation

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

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

The same technique is used in Page3 to store the hint ’3′ in pageFlowScope
                        <af:commandButton text="Page 2" id="cb1" action="page32">
                            <af:setPropertyListener to="#{pageFlowScope.backTarget}" type="action"
                                                    from="#{'3'}"/>
                        </af:commandButton>

Finally the magic is done in the router. Here the value which is stored in pageFlowScope variable ‘backTarget’ is checked and hte correct navigation target is used for the back navigation. Below is the source of the router, the design view is shown in the first picture.
  <router id="backRouter">
    <case id="__12">
      <expression>#{pageFlowScope.backTarget eq '1'}</expression>
      <outcome>page21</outcome>
    </case>
    <case id="__13">
      <expression>#{pageFlowScope.backTarget eq '3'}</expression>
      <outcome>page23</outcome>
    </case>
    <default-outcome>page21</default-outcome>
  </router> 

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

Navigation from Page1 to Page2

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

Navigation Page3 to Page2

Navigation Page3 to Page2


the output text shows ’3′ in this case.

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

26. November 2011

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


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.

<af:inputFile label="Select new file" id="if1" autoSubmit="true"
              valueChangeListener="#{ImageBean.uploadFileValueChangeEvent}"/>

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).
<af:form id="f1" usesUpload="true">

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:

<af:commandButton text="Download Data" id="cb3"
                  visible="#{bindings.ImageData.inputValue ne null}"
                  binding="#{ImageBean.downloadButton}">
          <af:fileDownloadActionListener contentType="#{bindings.ContentType.inputValue}"
                                         filename="#{bindings.ImageName.inputValue}"
                                         method="#{ImageBean.downloadImage}"/>
</af:commandButton>

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


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…

Next Page »

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 34 other followers