Sunday, November 7, 2010

ADF Faces RC: Resolving javax.faces.model.NoRowAvailableException thrown by <af:tree> Component

Caused by: javax.faces.model.NoRowAvailableException
 at javax.faces.model.ListDataModel.getRowData(ListDataModel.java:150)
 at org.apache.myfaces.trinidad.model.SortableModel.getRowData(SortableModel.java:67)
 at org.apache.myfaces.trinidad.model.ChildPropertyTreeModel.getRowData(ChildPropertyTreeModel.java:207)
 at org.apache.myfaces.trinidad.model.ChildPropertyTreeModel.isContainer(ChildPropertyTreeModel.java:219)
 at org.apache.myfaces.trinidad.model.TreeModel.isContainerEmpty(TreeModel.java:153)
I have an <af:tree> component which is bound to a tree model that I create programmatically as illustrated in a previous post. The tree component throws the above described error whenever the tree model changes after making disclosure on some nodes. I thought setting the rendered attribute to false whenever the estimatedRowCount is less than 1 would solve the issue, but it did not. Further search made me found the forum post described below that actually resolve the issue.
The solution is to clear the disclosed row keys of the RichTree component whenever there will be changes on the tree model.
    if (richTree != null && richTree.getDisclosedRowKeys()!=null ){
        richTree.getDisclosedRowKeys().clear();//to resolve NoRowAvailableException
    }
Kodus to the collaborators of the following post: http://forums.oracle.com/forums/thread.jspa?messageID=5394433

Cheers!

Wednesday, October 27, 2010

ADF Region as Source of Partial Trigger

In my previous post, I describe a way on how to expose the state represented by the pageFlowScope object of a bounded task flow (BTF) to the outside world. Now, if there's an event inside the BTF that changes it exposed state, then the dependent outside component needs to refresh as well, and the idea is for it to have a partialTriggers property that point to the region where the BTF resides.

The problem is, the events that changes the state of the task flow are actually coming from the command button components and not the region. To have a hook on these events, then you need have to combine the relative Id of the region and the Id of the command button relative to the containing region like "r1:ctb1". Below is a code snippet from sample app on my previous post.

      <af:form id="f1">
        <af:panelStretchLayout id="psl1" topHeight="50px">
          <f:facet name="center">
            <af:region value="#{bindings.samplestatefulbtftaskflow1.regionModel}"
                       id="r1"/>
          </f:facet>
          <f:facet name="top">
            <af:panelGroupLayout id="pgl1" layout="horizontal">
              <af:toolbar id="t1">
                <af:commandToolbarButton text="Approve" id="ctb1"
                                         partialTriggers="r1:ctb1 r1:ctb2 r1:ctb3"
                                         disabled="#{sessionScope.region1StateHolder['editMode'] eq 'true'}"
                                         actionListener="#{viewScope.mainForm.approve}"/>
              </af:toolbar>
              <af:outputText value="This toolbar button disables and enables depending on the ('editMode') state of the bounded task flow below:" id="ot1"/>
            </af:panelGroupLayout>
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
But in the case of a dynamic region wherein you have no idea of the Ids of the contained command button components then the solution above cannot apply.

We need to step back a little bit and rethink the solution we have above. Since we cannot reference the Ids of the components which we don't have idea about for in the case of a dynamic region, then we should only have a hook on the region itself like "r1". We are left with no choice but to modify our BTFs so that when there's an internal action event that modifies the potentially exposed state of the current BTF, then we also raise a dummy region event as follows:

    private void broadcastAsRegionEvent(FacesEvent event) {
        RichRegion region = findImmediateContainingRegion(event);
        if (region != null) {
            AttributeChangeEvent dummyRegionEvent =
                new AttributeChangeEvent(region, "dummy", null, null);
            region.broadcast(dummyRegionEvent);
        }
    }
    private RichRegion findImmediateContainingRegion(FacesEvent event) {
        UIComponent comp = event.getComponent();
        while (!(comp instanceof RichRegion)) {
            comp = comp.getParent();
        }
        if (comp instanceof RichRegion) {
            return (RichRegion)comp;
        }
        return null;
    }
Below are some sample action listeners that calls the broadcastAsRegionEvent() method above:
    public void edit(ActionEvent actionEvent) {
        //some other operations here

        getPageFlowScope().put(EDIT_MODE, true);

        //workaround so that simply referencing the region
        //as partial trigger will work
        //in real app, need to confirm if the BTF is indeed used as region
        broadcastAsRegionEvent(actionEvent);
    }

    public void cancel(ActionEvent actionEvent) {
        //some other operations here
        getPageFlowScope().put(EDIT_MODE, false);
        broadcastAsRegionEvent(actionEvent);
    }

Wheew! I have the feeling that I have gone too far off :-( . This just started on the challenge of disabling a parent page component when my bounded task flow is on edit mode. Guys, comments and feedback are very much appreciated.

You can download the sample application from here.


Tuesday, October 26, 2010

Bounded Task Flows as Components with Exposed State or Attributes

Overview

Bounded Task Flows (BTFs) can be considered a breakthrough in again bringing the concept of web component reuse to a new level. BTFs can be package into ADF Library JAR files and placed into to a reusable component repository, in which they can be applied in multiple projects and/or applications. This document describes a way on how to make BTFs a more full-pledged interactive component by incorporating them with state and/or attributes.

Problem Description

BTFs have a way to communicate with other BTFs and their containing page through contextual events in which they can pass data as part of the event payload. They can also be navigated from outside through "queueActionEventInRegion()" method exposed by the RichRegion component. But BTF has a major shortcoming as a component, and that is, it does not have an exposed state or attributes.

Solution Description

Undeniably, pageFlowScope is where the internal state and attributes of BTFs are usually kept. As a matter of fact, if you define input parameters, JDeveloper will automatically create the value expressions which are in pageFlowScope. This pageFlowScope is unique per instance of the BTF and will last long from an instance initialization up to a return activity, fault, or explicit detachment(like setting the region with a different TaskFlowId), but is not accessible outside the BTF. This solution provides a way on how the attributes of a bounded task flow can be optionally exposed to other components of the page beyond the BTF boundary.

The User Experience

Figure 1: The main page "Approve" button is enabled when the bounded task flow is not on editMode.
Figure 2: The "Approve" button on the main page is disabled when the bounded task flow was on edit mode.

Artifacts

Exposing the BTF attributes represented by the BTF pageFlowScope can be done in three steps:
  1. Define a string input parameter that will hold a unique value expression provided by the user.
  2. Set the value expression with the pageFlowScope object in the initializer of the BTF.
  3. Set the value expression to null in the finalizer of the BTF to ensure objects are released.

The details of the steps are as follows:
  1. Define a string input parameter that will hold a unique value expression provided by the user.
    Figure 3: Input parameter definition of the string that represents a value expression.
    PageFlowScope is unique per instance of BTF, so it is the responsibility of the user(a developer) of the BTF to ensure that he is passing a unique externalStateValueHolder string value expression per region so that the region pageFlowScopeObjects will not collide which can lead to unexpected results.
  2. Set the value expression with the pageFlowScope object in the initializer of the BTF.
        public void taskInit() {
            //EXTERNAL_STATE_HOLDER is a string constant with value "externalStateHolder"
            String expression =
                (String)getPageFlowScope().get(EXTERNAL_STATE_HOLDER);
            if (expression != null) {
                JSFUtils.setManagedBeanValue(expression, getPageFlowScope());
            }
        }
    
  3. Set the value expression to null in the finalizer of the BTF to ensure objects are released.
        public void taskCleanUp() {
            String expression =
                (String)getPageFlowScope().get(EXTERNAL_STATE_HOLDER);
            if (expression != null) {
                //to ensure pageFlowScope is released upon unload of BTF
                JSFUtils.setManagedBeanValue(expression, null);
            }
        }
    
    Figure 4: Initializer and finalizer definition of the bounded task flow
Below are the other relevant artifacts in the prototype:
  • Region model definition
        <taskFlow id="samplestatefulbtftaskflow1"
                  taskFlowId="/WEB-INF/sample-stateful-btf-task-flow.xml#sample-stateful-btf-task-flow"
                  activation="deferred"
                  xmlns="http://xmlns.oracle.com/adf/controller/binding">
          <parameters>
            <parameter id="externalStateHolder"
                       value="sessionScope.region1StateHolder"/>
          </parameters>
        </taskFlow>
    
  • Toolbar button disabled attribute
        <af:commandToolbarButton text="Approve" id="ctb1"
               partialTriggers="r1:ctb1 r1:ctb2 r1:ctb3"
               disabled="#{sessionScope.region1StateHolder['editMode'] eq 'true'}"
               actionListener="#{viewScope.mainForm.approve}"/>
    

Prototype

You can download the sample application from here.

Resources

  • Initiate Control Flow Within A Region From Its Parent Page Functional Pattern

Caveat

Care should be taken when you are exposing the BTFs pageFlowScope to the outside world (outside the boundaries of the BTF), because it could lead your BTFs to unexpected inconsistent state. The solution just illustrate the concept and it can easily be tweak to exposed a much restricted Map object instead of the pageFlowScope.

Wednesday, October 20, 2010

ADF Faces RC: Resolving "'BracketSuffix' returned null"

javax.el.PropertyNotFoundException: Target Unreachable, 'BracketSuffix' returned null
 at com.sun.el.parser.AstValue.getTarget(AstValue.java:96)
 at com.sun.el.parser.AstValue.getType(AstValue.java:56)
This is my story:
I have a string status attribute which is represented by a single character in the database but should be displayed in my pages as meaningful words as follows:
  • A -Active
  • P -Pending
  • U -Undefined
  • N -New
So I created a Constants class with a map attribute that holds the appropriate key-value pairs and defined this class as an application scoped managed bean.
I refer this in the page as follows:
<af:panelLabelAndMessage label="#{bindings.status.hints.label}"
                                     id="plam1">
  <af:outputText value="#{nisConstants.statusMap[bindings.status.inputValue]}" id="ot1"
                             clientComponent="true"/>                       
</af:panelLabelAndMessage>

And then I started encountering the error above. This is pretty hard to identify if you have a lot of expression in the page that has brackets, and I hope that the ADF Development team will improve on defining the error by including in the trace the expression that causes the problem.

Below is my analysis:
I easily avoided such error by initializing my status attribute to some default value like below:
    private String status = "N";

Cheers!

Wednesday, October 13, 2010

Looking Up SOAServiceInvokerBean from a Different JVM

I am writing this up before I will forget and hit this again...
javax.naming.CommunicationException [Root exception is java.rmi.UnmarshalException: failed to unmarshal class java.lang.Object; nested exception is: 
 java.lang.ClassNotFoundException: oracle.integration.platform.blocks.sdox.ejb.api.SOAServiceInvokerBean]
 at weblogic.jndi.internal.ExceptionTranslator.toNamingException(ExceptionTranslator.java:74)
....
Caused by: java.rmi.UnmarshalException: failed to unmarshal class java.lang.Object; nested exception is: 
 java.lang.ClassNotFoundException: oracle.integration.platform.blocks.sdox.ejb.api.SOAServiceInvokerBean
 at weblogic.rjvm.ResponseImpl.unmarshalReturn(ResponseImpl.java:244)
 at weblogic.rmi.cluster.ClusterableRemoteRef.invoke(ClusterableRemoteRef.java:348)
...
Caused by: java.lang.ClassNotFoundException: oracle.integration.platform.blocks.sdox.ejb.api.SOAServiceInvokerBean
 at weblogic.application.internal.AppClassLoaderManagerImpl.loadApplicationClass(AppClassLoaderManagerImpl.java:135)
 at weblogic.common.internal.ProxyClassResolver.resolveProxyClass(ProxyClassResolver.java:68)

I normally do not encounter this error when I look up the SOAServiceInvokerBean inside a simple java client, but when I tried to look this up inside one of my backing bean then I start hitting it again.

The following is an excerpt from the Developer's Guide for Oracle SOA Suite
36.4 Designing an SDO-Based Enterprise JavaBeans Client to Invoke Oracle SOA Suite To invoke an SDO - Enterprise JavaBeans service from Enterprise JavaBeans, you must use the client library. Follow these guidelines to design an Enterprise JavaBeans client.
  • Look up the SOAServiceInvokerBean from the JNDI tree.
  • Get an instance of SOAServiceFactory and ask the factory to return a proxy for the Enterprise JavaBeans service interface.
  • You can include a client side Enterprise JavaBeans invocation library (fabric-ejbClient.jar or the fabric-runtime.jar file located in the Oracle JDeveloper home directory or Oracle WebLogic Server) in the Enterprise JavaBeans client application. For example, the fabric-runtime.jar file can be located in the JDev_Home\jdeveloper\soa\modules\oracle.soa.fabric_11.1.1 directory.
If the Enterprise JavaBeans application is running in a different JVM than Oracle SOA Suite, the Enterprise JavaBeans application must reference the ejbClient library.

It says that to invoke the SOAServiceInvokerBean from a different JVM then you need to have the "fabric-ejbClient.jar" in your classpath, but "fabric-ejbClient.jar" is nowhere to be found. Searching on the same directory suggested above, we can find a "fabric-client.jar", and yes you are right that it can serve the purpose.

In summary, to look up and invoke the SOAServiceInvokerBean from a different JVM, you must reference the "fabric-client.jar" located in JDev_Home\jdeveloper\soa\modules\oracle.soa.fabric_11.1.1 directory.

Cheers!

Friday, October 8, 2010

Using GMail as Mail Server for Oracle SOA Suite 11g Notifications

Lucas Jellema has a good post about configuring SOA Suite 11g for sending email notifications with Google Mail, but unluckily, as he also noted, it was not working anymore. Nevertheless, that post demonstrated that Gmail could work as SMTP server for Oracle SOA and inspired me to dig more. In this post, I will share my configuration and the steps that I have made to have a working GMail SMTP server. The following are my suggested steps:
  1. Configure Workflow Notification Properties
  2. Configure Email Driver Properties
  3. Import the GMail's SSL certificate into Java keystore
  4. Remove "-Djavax.net.ssl.trustStore=%WL_HOME%\server\lib\DemoTrust.jks" in setDomainEnv.cmd

Configure Workflow Notification Properties

Right-click soa-infra > SOA Administration > Workflow Notification Properties
Set Workflow Notification to "All" or "Email".
Replace the "admin@javasoadev.com" and "action@javasoadev.com" with your own gmail address. You could replace both with just a single account. Don't be alarmed with my email address configuration that doesn't end with "gmail.com" because I have subscribe to Google Apps where I could have a customized email address.

Configure Email Driver Properties

Please see my configuration below which is presented as is for your reference.
Note that I am not using cleartext password but an encrypted indirect password as follows:

Import the GMail's SSL certificate into Java keystore

Follow the instructions on the following site up to the step of importing of the certificate to the keystore. Needless to say, import the certificate to the same JAVA_HOME that runs your weblogic server instance. http://confluence.atlassian.com/display/JIRA042/Using+Gmail+as+a+JIRA+Mail+Server

Remove "-Djavax.net.ssl.trustStore=%WL_HOME%\server\lib\DemoTrust.jks" in setDomainEnv.cmd

It seems that this command argument is affecting the way Weblogic locates trust as described in the following document: How WebLogic Server Locates Trust. With that argument, weblogic is not looking trust into JDK cacerts keystores.

Quick Testing...

Goto SOA Infrastructure>Service Engine>Human Workflow

Click "Notification Management" tab then click "Send Test Notification"... Enter something like the following values. Ensure that you gave a valid "Send To" email address.
Voila! Email is received accordingly...
Cheers and Thanks to Lucas!

Thursday, September 30, 2010

SDO: Implementing a HashMap like Service Data Objects

Introduction

I envisioned to create a Generic ADF Human Task Form to support all of our human task (workflow) activities, and in the solution that I thought of, I need an SDO HashMap.

For those who are not familiar, the ADF Human Task Form are remotely deployed bounded task flows (BTFs) that are being accessed by the Oracle BPM woklist app in showing the details of a listed task. The remotely deployed BTFs can support multiple human task as long as they have have identical payload elements. I will describe some more details in subsequent posts.

Problem Description

I need some sort of HashMap like SDO because I need to pass a parameterMap from a human task payload into the dynamic region inside my envisioned Generic ADF Human Task Form, but there is no such HashMap equivalent in SDO or in XML Schema. This post provides an implemented schema and classes to support a HashMap like SDO in Oracle SOA Suite 11g BPEL processes.

Artifacts

To create the SDO HashMap we need to have the following artifacts:
  1. MyMapSDO.xsd - Contains the definition of "MyMapSDO" and "MyEntrySDO" complex types to hold the list of entries and the key-value pairs respectively.
  2. MyEntrySDO.java - Java representation of the MyEntrySDO complex type.
  3. MyMapSDO.java - Java representation of the MyMapSDO complex type.
  4. MyEntrySDOImpl.java - implementation
  5. MyMapSDOImpl.java
  6. SimpleWrapperUtils.java - this class handles wrapping of simple types like string, integer, long , etc. to dataobjects as described in my previous post.
  • Below is the concrete "MyMapSDO.xsd" schema that contains both complex types:
    <?xml version="1.0" encoding="windows-1252" ?>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns="http://soadev.blogspot.com/map"
                targetNamespace="http://soadev.blogspot.com/map"
                xmlns:sdoJava="commonj.sdo/java" sdoJava:package="soadev.sdo.sdohashmap.model">
       <xsd:import namespace="commonj.sdo/java"/>
       <xsd:complexType name="MyMapSDO">
          <xsd:sequence>
             <xsd:element name="entry" type="MyEntrySDO" minOccurs="0"
                          maxOccurs="unbounded"/>
          </xsd:sequence>
       </xsd:complexType>
       <xsd:complexType name="MyEntrySDO">
          <xsd:sequence>
             <xsd:element name="key" type="xsd:string" minOccurs="0"/>
             <xsd:element name="value" type="xsd:anyType" minOccurs="0"/>
          </xsd:sequence>
       </xsd:complexType>
       <xsd:element name="myMapSDO" type="MyMapSDO"/>
    </xsd:schema>
    
  • MyEntrySDO.java
    package soadev.sdo.sdohashmap.model;
    
    public interface MyEntrySDO {
        String getKey();
        void setKey(String key);
        Object getValue();
        void setValue(Object value);
    }
    
    
  • MyMapSDO.java
    
    package soadev.sdo.sdohashmap.model;
    
    import java.util.List;
    
    public interface MyMapSDO {
        List getEntry();
        void setEntry(List value);
    }
    
  • MyEntrySDOImpl.java
    
    package soadev.sdo.sdohashmap.model;
    
    import org.eclipse.persistence.sdo.SDODataObject;
    
    public class MyEntrySDOImpl extends SDODataObject implements MyEntrySDO {
        public String getKey() {
            return getString("key");
        }
    
        public void setKey(String key) {
            set("key", key);
        }
    
        public Object getValue() {
            return get("value");
        }
    
        public void setValue(Object value) {
            set("value", value);
        }
    }
    
  • MyMapSDOImpl.java
    
    package soadev.sdo.sdohashmap.model;
    
    import commonj.sdo.DataObject;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.List;
    
    import java.util.Map;
    
    import org.eclipse.persistence.sdo.SDODataObject;
    import static soadev.sdo.utils.SimpleTypeWrapperUtils.unwrap;
    
    public class MyMapSDOImpl extends SDODataObject implements MyMapSDO {
        public List getEntry() {
            return getList("entry");
        }
    
        public void setEntry(List value) {
            set("entry", value);
        }
        //EL reachable
        public Map<String, Object> getMap(){
            Map<String, Object> map = new HashMap<String, Object>();
            for(Object obj: getEntry()){
                MyEntrySDO entry = (MyEntrySDO)obj;
                map.put(entry.getKey(), unwrap((DataObject)entry.getValue()));
            }
            return map;
        }
    }
    

Testing ...

Below is a sample runner class to illustrate the use case:
package soadev.sdo.sdohashmap.main;

import commonj.sdo.helper.DataFactory;

import commonj.sdo.helper.XMLHelper;

import java.math.BigDecimal;

import java.util.HashMap;
import java.util.List;

import java.util.Map;

import oracle.jbo.common.sdo.SDOHelper;

import soadev.sdo.sdohashmap.model.MyEntrySDO;
import soadev.sdo.sdohashmap.model.MyMapSDO;
import soadev.sdo.sdohashmap.model.MyMapSDOImpl;
import soadev.sdo.sdohashmap.sampleentity.JobSDO;
import static soadev.sdo.utils.SimpleTypeWrapperUtils.*;

public class Runner {

    public static void main(String[] args) {
        try {
            SDOHelper.INSTANCE.defineSchema("soadev/sdo/sdohashmap/sampleentity/",
                                            "JobSDO.xsd");
            SDOHelper.INSTANCE.defineSchema("soadev/sdo/sdohashmap/model/",
                                            "MyMapSDO.xsd");
            MyMapSDO myMapSDO =
                (MyMapSDO)DataFactory.INSTANCE.create(MyMapSDO.class);
            MyEntrySDO entry =
                (MyEntrySDO)DataFactory.INSTANCE.create(MyEntrySDO.class);
            entry.setKey("param1");
            entry.setValue(wrap("SampleValue"));
            MyEntrySDO entry2 =
                (MyEntrySDO)DataFactory.INSTANCE.create(MyEntrySDO.class);
            entry2.setKey("param2");
            entry2.setValue(wrap(1));
            MyEntrySDO entry3 =
                (MyEntrySDO)DataFactory.INSTANCE.create(MyEntrySDO.class);
            entry.setKey("param1");
            JobSDO job = (JobSDO)DataFactory.INSTANCE.create(JobSDO.class);
            job.setJobId("SOADev");
            job.setJobTitle("SOA Developer");
            job.setMaxSalary(20000L);
            job.setMinSalary(10000L);
            entry3.setKey("param3-job");
            entry3.setValue(job);
            MyEntrySDO entry4 =
                (MyEntrySDO)DataFactory.INSTANCE.create(MyEntrySDO.class);
            entry4.setKey("param4");
            entry4.setValue(wrap(100L));
            MyEntrySDO entry5 =
                (MyEntrySDO)DataFactory.INSTANCE.create(MyEntrySDO.class);
            entry5.setKey("param5");
            entry5.setValue(wrap(true));
            MyEntrySDO entry6 =
                (MyEntrySDO)DataFactory.INSTANCE.create(MyEntrySDO.class);
            entry6.setKey("param6");
            entry6.setValue(wrap(200.50));
            MyEntrySDO entry7 =
                (MyEntrySDO)DataFactory.INSTANCE.create(MyEntrySDO.class);
            entry7.setKey("param7");
            entry7.setValue(wrap(new BigDecimal("550.90")));
            List entries = myMapSDO.getEntry();
            entries.add(entry);
            entries.add(entry2);
            entries.add(entry3);
            entries.add(entry4);
            entries.add(entry5);
            entries.add(entry6);
            entries.add(entry7);
            
            //print XML representation
            String xmlFragment = XMLHelper.INSTANCE.save((MyMapSDOImpl)myMapSDO, null, "myMapSDO");
            System.out.println(xmlFragment);
            
            //get map Object
            Map<String, Object> parameterMap = ((MyMapSDOImpl)myMapSDO).getMap();
            for(Map.Entry<String, Object> mapEntry: parameterMap.entrySet()){
                System.out.println("key: " + mapEntry.getKey());
                System.out.println("value: " + mapEntry.getValue());
                System.out.println("value class: " + mapEntry.getValue().getClass());
                System.out.println("-----------------------------------------------");
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
Below is the output in the console after running the runner class above:
C:\Oracle\11gR1PS2\jdk160_18\bin\javaw.exe -client -classpath ////--removed very long classpath---////
Djavax.net.ssl.trustStore=C:\Oracle\11gR1PS2\wlserver_10.3\server\lib\DemoTrust.jks soadev.sdo.sdohashmap.main.Runner
[EL Warning]: 2010-09-30 03:22:46.25--SDOUtil: Generated Type [ChangeSummary] conflicts with SDO specification naming rules for [ChangeSummary] and should be renamed.
<?xml version="1.0" encoding="UTF-8"?>
<myMapSDO xsi:type="ns1:MyMapSDO" xmlns:ns1="http://soadev.blogspot.com/map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <entry>
      <key>param1</key>
      <value xsi:type="ns0:string" xmlns:ns0="http://www.w3.org/2001/XMLSchema">SampleValue</value>
   </entry>
   <entry>
      <key>param2</key>
      <value xsi:type="ns0:int" xmlns:ns0="http://www.w3.org/2001/XMLSchema">1</value>
   </entry>
   <entry>
      <key>param3-job</key>
      <value xsi:type="ns0:JobSDO" xmlns:ns0="http://soadev.blogspot.com/sampleentity">
         <jobId>SOADev</jobId>
         <jobTitle>SOA Developer</jobTitle>
         <maxSalary>20000</maxSalary>
         <minSalary>10000</minSalary>
      </value>
   </entry>
   <entry>
      <key>param4</key>
      <value xsi:type="ns0:long" xmlns:ns0="http://www.w3.org/2001/XMLSchema">100</value>
   </entry>
   <entry>
      <key>param5</key>
      <value xsi:type="ns0:boolean" xmlns:ns0="http://www.w3.org/2001/XMLSchema">true</value>
   </entry>
   <entry>
      <key>param6</key>
      <value xsi:type="ns0:double" xmlns:ns0="http://www.w3.org/2001/XMLSchema">200.5</value>
   </entry>
   <entry>
      <key>param7</key>
      <value xsi:type="ns0:decimal" xmlns:ns0="http://www.w3.org/2001/XMLSchema">550.90</value>
   </entry>
</myMapSDO>

key: param1
value: SampleValue
value class: class java.lang.String
-----------------------------------------------
key: param2
value: 1
value class: class java.lang.Integer
-----------------------------------------------
key: param4
value: 100
value class: class java.lang.Long
-----------------------------------------------
key: param5
value: true
value class: class java.lang.Boolean
-----------------------------------------------
key: param6
value: 200.5
value class: class java.lang.Double
-----------------------------------------------
key: param7
value: 550.90
value class: class java.math.BigDecimal
-----------------------------------------------
key: param3-job
value: soadev.sdo.sdohashmap.sampleentity.JobSDOImpl@1c208b0
value class: class soadev.sdo.sdohashmap.sampleentity.JobSDOImpl
-----------------------------------------------
Process exited with exit code 0.

Prototype

You can download the sample application from here.

What's Next?

In subsequent post, I will show how this SDO HashMap be used through Java Embedding inside a BPEL process. I will also show how to initialize an SDO entity with an XML fragment. And... I will also describe a problem that I later realize in the process of this goal of having a Generic ADF Human Task Form - there is always workaround though :-) .

Monday, September 13, 2010

SDO: Wrapping Simple Types as DataObjects

  <xsd:element name="value" type="xsd:anyType" minOccurs="0"/>
The SDO specs specifies that an XSD element with type "anyType" is map to a DataObject SDO type. This means that you cannot assign simple types like String, Integer, Long, and Boolean among others into this element, because if you do so, you will receive a java.lang.ClassCastException in runtime.

To be able to do that therefore, you need to wrap those simple types into SDO DataObjects. Luckily the current org.eclipse.persistense_1.0.0.0_2-0.jar that ships with JDeveloper already contains simple type wrappers with type "org.eclipse.persistence.sdo.types.SDOWrapperType" as follows:

  • String - org.eclipse.persistence.sdo.StringWrapperImpl
  • Integer - org.eclipse.persistence.sdo.IntObjectWrapperImpl
  • Long - org.eclipse.persistence.sdo.LongObjectWrapperImpl
  • Double - org.eclipse.persistence.sdo.DoubleObjectWrapperImpl
  • BigDecimal - org.eclipse.persistence.sdo.DecimalWrapperImpl
  • Boolean - org.eclipse.persistence.sdo.BooleanObjectWrapperImpl

How do we wrap our simple types into SDO DataObjects?
Below is a utility class that I have devised to handle wrapping and unwrapping of simple types.

package soadev.sdo.utils;

import commonj.sdo.DataObject;
import commonj.sdo.Type;
import commonj.sdo.helper.DataFactory;

import java.math.BigDecimal;

import oracle.jbo.common.sdo.SDOTypeHandler;

import org.eclipse.persistence.sdo.types.SDOWrapperType;


/**@author Rommel Pino http://soadev.blogspot.com
 */
public class SimpleTypeWrapperUtils {

    public static DataObject wrap(String value) {
        return wrap(value, "java.lang.String");
    }

    public static DataObject wrap(Long value) {
        return wrap(value, "java.lang.Long");
    }

    public static DataObject wrap(Integer value) {
        return wrap(value, "java.lang.Integer");
    }

    public static DataObject wrap(BigDecimal value) {
        return wrap(value, "java.math.BigDecimal");
    }

    public static DataObject wrap(Short value) {
        return wrap(value, "java.lang.Short");
    }

    public static DataObject wrap(Boolean value) {
        return wrap(value, "java.lang.Boolean");
    }

    public static DataObject wrap(Byte value) {
        return wrap(value, "java.lang.Byte");
    }
    
    public static DataObject wrap(Double value) {
        return wrap(value, "java.lang.Double");
    }

    public static DataObject wrap(Float value) {
        return wrap(value, "java.lang.Float");
    }


    private static DataObject wrap(Object value,
                                   String fullyQualifiedSimleTypeClassName) {
        Type type =
            SDOTypeHandler.getSDOType(fullyQualifiedSimleTypeClassName);
        DataObject wrapper = DataFactory.INSTANCE.create(type);
        wrapper.set("value", value);
        return wrapper;
    }

    public static Object unwrap(DataObject wrapper) {
        if (wrapper.getType() instanceof SDOWrapperType) {
            return wrapper.get("value");
        }
        //if not wrapper return the same object
        return wrapper;
    }

}

In subsequent post, I will describe how to implement some sort of SDO HashMap.

Wednesday, September 1, 2010

ADF Faces RC: Generic Query and ListOfValues Model for EJBs

Overview

The <af:query> and <af:inputListOfValues> components of ADF Faces RC are a must-have for applications, but if you are not using ADF Business Components, then you face the challenge of implementing your own query and listOfValues models. This pattern provides an implemented reusable model that you can readily adapt to your enterprise needs when you are using EJBs.

Problem Description

The query and listOfValues models are fairly complex. To build a query model alone, you need to extend and implement at least six abstract classes as depicted in the diagram below:
Figure 1: Query Model Class Diagram
Plus some additional classes to build the listOfValues model as depicted below:
Figure 2: ListOfValues Model Class Diagram
If you try to look into how these were implemented for ADF BC, you could easily get lost in the details, especially that our focus is to build business applications and not UI components, on the other hand, if you look into the DemoQueryModel and DemoListOfValuesModel implemented in the adffacesdemo, then those implementations were suggesting "I'm stiff and useless, don't dare create something like me.". I am telling this because I have been into these hardships.

Technical Pattern Description

Let us get back to the basic requirement and the existing set of capabilities of the technologies that we use. The basic requirement is to provide the end-user a component where he can set criteria based on existing attributes and execute query to get a filtered result.
The following are the existing capabilities:
  • EJB's JPQL supports dynamic queries so we can create a session bean method that accepts a JPQL statement, as in the case of "queryByRange()", and create EJB DataControls out of them.
  • We can drag the data control on the page to create tables. In the process, a tree binding is created in the page definition file. In this tree binding, we can access the relevant attributes and accessors of the information that we are interested in. On the same tree binding, we can get the resulting class, and the corresponding CollectionModel.
  • By implementing some sort of dummy Map interface, we can allow passing of parameters through EL.
  • When we declare our model as managed beans, we can inject managed properties. Through managed properties, we can pass the the unique list of saved search per query and the object that will handle persistence.
From the requirement and capabilities described above, I came up with a solution that is simple to use, reusable, and manageable from the complexity perspective. This solution has one basic requirement - a valid treeBinding defined in the pageDefinition file that is based on a method that accepts a "jpqlStmt" parameter. The solution hits three birds in one shot, because it supports the QueryModel, the ListOfValuesModel, and a Dynamic Converter to convert the string input into appropriate entity objects.

The User Experience

The saved searches that are injected on the managed beans will be displayed on the saved search list:
Figure 3: List page showing a query defined from managed bean

If no saved search is injected, the system will generate one, named "System Generated", setting the first attribute defined in the pageDefinition file as a search field.
Figure 4: Job list page showing a query with a system generated saved search.

Users will be able to use the accessor attributes as part of the search criteria. The displayed label supports internationalization.
Figure 5: Add Fields menu showing list of attributes including those of the accessors. 

Search is NOT case-sensitive.
Figure 6: Case In-sensitive search.

Aside from the primary components, the query panel can have selectComboBox, selectOneChoice, and inputListOfValues components as well (inputComboBoxListOfValues can be easily supported too).
Figure 7: A launched job.Job Id inputListOfValues  from the query panel.
Displayed below are three inputListOfValues component based on the same ListOfValuesModel class but different treeBindings.
Figure 8: Job, department, and manager LOVs based on the same model class.

Below is a launched Job Search. Selections made here are ensured to be easily converted to objects by using the dynamic converter.
Figure 9: A launched Job Search having the default system generated criterion

Artifacts

The QueryLOV class alone contains more than a thousand lines of code, so I leave the sample application which do have comments to speak of its own. Instead, in this section, I will show the artifacts in using the solution:
  • The employeeQuery managed bean declaration in the employee-list-task-flow:
    Figure 10: The employeeQuery managed bean definition in employee-list-task-flow.

    The employeeQuery has a defined "systemSavedSearchDefList" managed property which is a list. Inside this list is a value that points to another managed bean named "savedSearch1". Inside "savedSearch1" is a list with a value referencing to managed bean "searchField1". These managed properties are optional. If you did not specify a managed saved search list property, the QueryLOV will generate a default.

    The "supplementaryMap" property is a map with an entry that points to the jobLOV managed bean. This property supplements the model that made possible the presence of the Job inputListOfValue component on the query panel of the employee search.

  • Below is the source of the query component as used in the employee_list.jsff search page:
    <af:query headerText="Search" disclosed="true" id="q1"
        model="#{pageFlowScope.employeeQuery.param['bindings.Employee'].queryModel}"
        value="#{pageFlowScope.employeeQuery.param['bindings.Employee'].currentDescriptor}"
        resultComponentId="::pc1:t1"
        queryListener="#{pageFlowScope.employeeQuery.processQuery}"/>
    
    The 'bindings.Employee' is the name of the tree binding in the page definition file that is based on a methodAction "findEmployeesByCriteria" that accepts a "jpqlStmt" parameter.

  • Below is the source of the Job Search LOV in employee_details.jsff form page.
    <af:inputListOfValues label="Job"
        popupTitle="Search and Result Dialog" id="ilov2" searchDesc="Job Search"
        model="#{pageFlowScope.jobLOV.param['bindings.Job'].listOfValuesModel}"
        value="#{bindings.findEmployeeByIdIterator.currentRow.dataProvider.job}"
        converter="#{pageFlowScope.jobLOV.param['bindings.Job'].convert['jobId']}"
        autoSubmit="true" readOnly="#{!pageFlowScope.editMode}"/>
    
    The same as the 'bindings.Employee' above, the 'bindings.Job is the name of the tree binding in the page definition file that is based on a methodAction "findJobsByCriteria" that accepts a "jpqlStmt" parameter.
    The string 'jobId' is passed as parameter in .convert['jobId'] so that the dynamic converter will know how to transform our strings into Job objects.
    It is also important that you override the toString() method of your entities to return the id (jobId in the case of Job)representation of your object.

Pattern Implementation

To implement this Reusable Query and ListOfValues Model into your existing application, do the following steps:
  1. Download the accompanying sample application.
  2. Inside your existing application, open the EJBQueryLOV project by invoking "Open Project" menu and navigating to the directory of the sample application.
  3. Open project properties of your ViewController project and add a dependency on the EJBQueryLOV build output.
  4. The EJBQueryLOV projects has some dependency on the utility classes in the Utils project in the sample application. You could add the JSFUtils.jar inside the deploy folder into your classpath.

Prototype

You can download the sample application with the reusable query and listOfValues model project from here.

Known Issues

Sometimes, invoking the "Search" button on the Query component do not raise the necessary lifecycle process that will trigger the result component to refresh. In this case, the "Search" button needs to be invoked again.

When you drag a data control with a resulting entity that do have a recursive relation to itself - like in the case of Employee with attribute manager, in which manager is also an employee, for some reasons, JDeveloper do not create a separate node binding for the manager inside the tree binding in the pageDefinition file. In this case, you need to manually add the necessary node definition to support the query by manager attributes and to comply with underlying assumption of the QueryLOV that the sequence of the defined accessors in the primary node definition of the tree binding is laid out on the same sequence in the subsequent nodes.
To illustrate the case please see the default generated code VS. the necessary modified version of the tree binding
Generated Default:
    <tree IterBinding="findEmployeesByCriteriaIterator" id="Employee">
      <nodeDefinition DefName="src.model.Employee" Name="Employee0">
        <AttrNames>
          <Item Value="employeeId"/>
          <Item Value="firstName"/>
          <Item Value="lastName"/>
          <Item Value="email"/>
          <Item Value="commissionPct"/>
          <Item Value="hireDate"/>
          <Item Value="phoneNumber"/>
          <Item Value="salary"/>
        </AttrNames>
        <Accessors>
          <Item Value="department"/>
          <Item Value="manager"/>
          <Item Value="job"/>
        </Accessors>
      </nodeDefinition>
      <nodeDefinition DefName="src.model.Department" Name="Employee1">
        <AttrNames>
          <Item Value="departmentId"/>
          <Item Value="departmentName"/>
          <Item Value="locationId"/>
        </AttrNames>
      </nodeDefinition>
      <nodeDefinition DefName="src.model.Job" Name="Employee2">
        <AttrNames>
          <Item Value="jobId"/>
          <Item Value="jobTitle"/>
          <Item Value="maxSalary"/>
          <Item Value="minSalary"/>
        </AttrNames>
      </nodeDefinition>
    </tree>

The necessary modified version to comply with the QueryLOV assumption:
    <tree IterBinding="findEmployeesByCriteriaIterator" id="Employee">
      <nodeDefinition DefName="src.model.Employee" Name="Employee0">
        <AttrNames>
          <Item Value="employeeId"/>
          <Item Value="firstName"/>
          <Item Value="lastName"/>
          <Item Value="email"/>
          <Item Value="commissionPct"/>
          <Item Value="hireDate"/>
          <Item Value="phoneNumber"/>
          <Item Value="salary"/>
        </AttrNames>
        <Accessors>
          <Item Value="department"/>
          <Item Value="manager"/>
          <Item Value="job"/>
        </Accessors>
      </nodeDefinition>
      <nodeDefinition DefName="src.model.Department" Name="department">
        <AttrNames>
          <Item Value="departmentId"/>
          <Item Value="departmentName"/>
          <Item Value="locationId"/>
        </AttrNames>
      </nodeDefinition>
       <nodeDefinition DefName="src.model.Employee" Name="manager">
        <AttrNames>
          <Item Value="employeeId"/>
          <Item Value="firstName"/>
          <Item Value="lastName"/>
          <Item Value="email"/>
          <Item Value="commissionPct"/>
          <Item Value="hireDate"/>
          <Item Value="phoneNumber"/>
          <Item Value="salary"/>
        </AttrNames>
      </nodeDefinition>
      <nodeDefinition DefName="src.model.Job" Name="job">
        <AttrNames>
          <Item Value="jobId"/>
          <Item Value="jobTitle"/>
          <Item Value="maxSalary"/>
          <Item Value="minSalary"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
Note that the node names are not necessary but the sequence. The sequence of the node definition after the primary should be the same as the sequence of the defined accessors (department, manager, then job).

Related Patterns

The sample application is based on the ADF UI Shell Functional Pattern.


Authors Notes

I would to recognize other people who also have contributed on the evolution of this solution, specifically, Java Powers of http://javainthedesert.blogspot.com/.

Thursday, August 12, 2010

Loosely Coupled Bounded Task Flows + Outside-world Messenger

Chris Muir posted in ADF UI Patterns forum a thread entitled "Overcoming a challenge: combining UI Shell dirty tab + self-closing-BTFs". That post enforces my realization that there is something wrong with my current implementation of bounded task flows, in which we have so much dependency on the TabContext object of the UI Shell. This dependency made me unable to reuse the same task flow inside a stand-alone remote task flow (eg. ADF Task Flow from Human Task) that will be consumed by the Oracle SOA Suite BPM worklist app.
With that, I started reviewing my bounded task flows (BTFs)and succeeded in removing any dependency to the tabContext, while getting the same behavior. In short, my BTFs now do not have any defined "tabContext" input parameter.
Below are the tabContext specific actions that I have externalize:
  1. Setting the bounded task flow dirty (in which the UI Shell presents with an italicized tab title).
  2. Launching of new tabs from inside the bounded task flow.
  3. Closing of tabs from inside the bounded task flow.
  4. Postponing navigation to a return activity until a callback confirmation from the UI Shell dirty tab handler. (The challenge in Chris' post.)
Below are the important artifacts that I have incorporated.
  1. EventProducer.java - This class is exposed as a data control to work as a convenient event publisher. It has only one method "produceEvent()" that does nothing.
    public class EventProducer {
        public void produceEvent(){};
    }
    
  2. DynamicShellHelper.java - This class exposes the methods of TabContext as a data control. (I actually tried to expose the TabContext as data control but I'm not getting the right tabContext instance)
    package oracle.ui.pattern.dynamicShell;
    import java.util.Map;
    import soadev.ext.adf.taskflows.helper.Messenger;
    
    public class DynamicShellHelper {
          public void markCurrentTabDirty(TabContext tabContext, Boolean isDirty) {
              tabContext.markCurrentTabDirty(isDirty);
          }
    
          public void handleMessage(TabContext tabContext, Messenger messenger) {
              messenger.accept();
              tabContext.setMessenger(messenger);
              messenger.setRegion("pt_region" + tabContext.getSelectedTabIndex());
              System.out.println(messenger.getRegion());
              tabContext.showTabDirtyPopup();
          }
    
          public void launchActivity(TabContext tabContext, String title, String taskFlowId,
                                     Map<String, Object> parameterMap,
                                     boolean newTab) {
              try {
                  
                  if (newTab) { //allows multiple instance of taskflow.
    
                      tabContext.addTab(title, taskFlowId, parameterMap);
                  } else {
                      tabContext.addOrSelectTab(title, taskFlowId, parameterMap);
                  }
              } catch (TabContext.TabOverflowException toe) {
                  // causes a dialog to be displayed to the user saying that there are
                  // too many tabs open - the new tab will not be opened...
                  toe.handleDefault();
              }
          }
    }
    
  3. Messenger.java - This object is something that a bounded task flow can send to the outside world as a payload of a contextual event. If someObject somewhere accepted this payload object (invokes "accept()" method on the messenger instance), then the BTF put it's faith into that someObject, which the BTF doesn't know about, to invoke some affirmative or negative callback that will resolve the next navigation of the BTF. This class utilize the "Initiate Control Flow Within A Region From Its Parent Page Functional Pattern".
    package soadev.ext.adf.taskflows.helper;
    
    import javax.el.ELContext;
    import javax.el.ExpressionFactory;
    import javax.el.MethodExpression;
    import javax.faces.component.UIComponent;
    import javax.faces.context.FacesContext;
    import javax.faces.event.PhaseId;
    import oracle.adf.view.rich.component.rich.fragment.RichRegion;
    import soadev.view.utils.JSFUtils;
    
    public class Messenger {
    
        private boolean accepted = false;
        private String affirmativeOutcome;
        private String negativeOutcome;
        private String outcome;
        private String region;
    
        public void accept() {
            accepted = true;
        }
    
        public void affirmativeOutcomeCallback() {
            outcome = getAffirmativeOutcome();
            handleOuterPageAction();
        }
    
        public void negativeOutcomeCallback() {
            outcome=getNegativeOutcome();
            handleOuterPageAction();
        }
    
        public boolean isAccepted() {
            return accepted;
        }
    
        public void handleOuterPageAction() {
            UIComponent regionComponent = JSFUtils.findComponentInRoot(region);
            if (regionComponent instanceof RichRegion) {
                FacesContext fc = FacesContext.getCurrentInstance();
                ExpressionFactory ef = fc.getApplication().getExpressionFactory();
                ELContext elc = fc.getELContext();
                JSFUtils.setRequestAttribute("messenger", this);
                MethodExpression me =
                    ef.createMethodExpression(elc, "#{messenger.getOutcome}",
                                              String.class, new Class[] { });
                ((RichRegion)regionComponent).queueActionEventInRegion(me, null,
                                                                       null, false,
                                                                       -1, -1,
                                                                       PhaseId.ANY_PHASE);
            }
        }
    
        public void setAffirmativeOutcome(String affirmativeOutcome) {
            this.affirmativeOutcome = affirmativeOutcome;
        }
    
        public String getAffirmativeOutcome() {
            return affirmativeOutcome;
        }
    
        public void setNegativeOutcome(String negativeOutcome) {
            this.negativeOutcome = negativeOutcome;
        }
    
        public String getNegativeOutcome() {
            return negativeOutcome;
        }
    
        public void setRegion(String region) {
            this.region = region;
        }
    
        public String getRegion() {
            return region;
        }
    
        public void setOutcome(String outcome) {
            this.outcome = outcome;
        }
    
        public String getOutcome() {
            return outcome;
        }
    }
    
  4. TabContext.java - Below were the modification that I have made with regards to the TabContext class:
    • Added a Messenger attribute plus the getter and setter.
      private Messenger messenger;
      
    • Added a modified version of Chris Muir's RegionNavigationLister to support self-closing BTFs.
        public void myRegionNavigationListener(RegionNavigationEvent regionNavigationEvent) {
             String newViewId = regionNavigationEvent.getNewViewId();
             if (newViewId == null) {
                 //there is no turning back
                 //trans committed or rolledback already
                  _removeTab(getSelectedTabIndex(), true);
             }
        }
      
    • Modified the handleDirtyTabDialog() method to check if there is a messenger instance and invoke callback on the messenger accordingly. I believe that the patterns TabContext class should improve this method to allow the BTF to do appropriate rollback or commit before removing.
        public void handleDirtyTabDialog(DialogEvent ev){
          if (ev.getOutcome().equals(DialogEvent.Outcome.yes))
          {
              if(messenger != null){
                  messenger.affirmativeOutcomeCallback();
                  messenger = null;
              }else{//not initiated from inside the BTF
                  //do the regular way
                 _removeTab(getSelectedTabIndex(), true);
              }
          }else{
              if(messenger != null){
                  messenger.negativeOutcomeCallback();
                  messenger = null;
              }
          }
        }
      
  5. Programmatic raising of contextual events
    • In sending a messenger to the outside-world:
          public String cancel() throws Exception {
              Messenger messenger = new Messenger();
              messenger.setAffirmativeOutcome("rollback");
              fireEvent("produceEvent", messenger);
              if (messenger.isAccepted()) {
                  //stay on current page and wait for
                  //the knight in shining armor
                  return null;
              }
              //no one cares...
              return "rollback";
          }
      
    • In launching detail task flow on a separate tab from inside the BTF:
          public void jobSelected(ActionEvent event) {
              if (unbox((Boolean)getPageFlowScope().get("initiateLaunchActivityEvent"))){
                  Job job = (Job)getCurrentRowDataProvider("findAllJobsIterator");
                  Map payload = new HashMap();
                  payload.put("jobId", job.getJobId());
                  payload.put("taskFlowId",
                              getPageFlowScope().get("detailTaskFlowId"));
                  payload.put("title", "Job: " + job.getJobId());
                  fireEvent("produceEvent", payload);
              }
          }
      
    • Utility methods to fire contextual events.
          public EventProducer getEventProducer(String producer){
              BindingContainer bindings = getBindings();
              JUCtrlActionBinding actionBinding =
                  (JUCtrlActionBinding)bindings.getControlBinding(producer);
              return actionBinding.getEventProducer();
          }
      
          public void fireEvent(EventProducer eventProducer, Object payload) {
              BindingContainer bindings = getBindings();
              ((DCBindingContainer)bindings).getEventDispatcher().fireEvent(eventProducer, payload);
          }
      
          //more convenient
          public void fireEvent(String eventProducer, Object payload) {
              fireEvent(getEventProducer(eventProducer),payload);
          }
      
  6. dynamicTabShellDefinition.xml
    • Added methodActions from the DynamicShellHelper data control so they can become handlers of the event subscribers.
    • Defined the event map and event subscribers:
        <eventMap xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
          <event name="transDirtyEvent">
            <producer region="*">
              <consumer handler="markCurrentTabDirty">
                <parameters>
                  <parameter name="tabContext" value="#{viewScope.tabContext}"/>
                  <parameter name="isDirty" value="#{payLoad}"/>
                </parameters>
              </consumer>
            </producer>
          </event>
          <event name="messageEvent">
            <producer region="*">
              <consumer handler="handleMessage">
                <parameters>
                  <parameter name="tabContext" value="#{viewScope.tabContext}"/>
                  <parameter name="messenger" value="#{payLoad}"/>
                </parameters>
              </consumer>
            </producer>
          </event>
          <event name="launchActivityEvent">
            <producer region="*">
              <consumer handler="launchActivity">
                <parameters>
                  <parameter name="tabContext" value="#{viewScope.tabContext}"/>
                  <parameter name="title" value="#{payLoad.title}"/>
                  <parameter name="taskFlowId" value="#{payLoad.taskFlowId}"/>
                  <parameter name="parameterMap" value="#{payLoad}"/>
                  <parameter name="newTab" value="true"/>
                </parameters>
              </consumer>
            </producer>
          </event>
        </eventMap>
      

Whew! This is a long post... I will describe the other artifacts and mechanism when my minds gets clear. For now you can download the sample application from here.



Continuation...

I guess the sample application is already enough to detail its mechanics so I leave this post as is.

In conclusion, I would like to thank Mr. Chris Muir for the encouragement and the exchange of ideas that we made through email related to this post.

Cheers!

Thursday, July 29, 2010

ADF UI Shell: Updating Title of the Current Tab

Sometimes you may wish to update the current tab title after some actions like saving a new record. You can do so through the following code inside your backing bean:
    public void updateCurrentTabTitle(String title) {
        TabContext tabContext = TabContext.getCurrentInstance();
        int currentTabIndex = tabContext.getSelectedTabIndex();
        Tab tab = tabContext.getTabs().get(currentTabIndex);
        tab.setTitle(title);
        //force refresh
        tabContext.setSelectedTabIndex(currentTabIndex);
    }
Be sure that you have defined a "tabContext" input parameter in your task flow definition so you can access the TabContext object.

Monday, June 21, 2010

Configuring a New Oracle SOA Domain on Oracle Enterprise Linux

Introduction

With the current trend of having 64 bit as the new desktop standard, then you would most likely come into a situation where you have a hard time deciding what OS to buy that can support your software development tasks, while not being left behind. That OS selection decision is made simpler by the advent of Virtualization wherein you can have multiple OS on a single hardware machine.
In this series, I will share with you how I was able to set-up and run Oracle SOA Suite on my Windows 7 Professional 64 bit OS through Oracle VM VirtualBox.

This is Part V of the following series:
  1. Setting-up Oracle Enterprise Linux on Oracle VM VirtualBox 3.2.4
  2. Installing Java 1.6 on Oracle Enterprise Linux
  3. Installing Weblogic 10.3.3 on Oracle Enterprise Linux
  4. Installing Oracle SOA Suite 11.1.1.3 on Oracle Enterprise Linux
  5. Configuring a New Oracle SOA Domain on Oracle Enterprise Linux

Prerequisites

  1. Installed Oracle VM VirtualBox.
  2. Installed Oracle Enterprise Linux (OEL) on VirtualBox as described in the Part I of this series.
  3. Installed Java 1.6.0_20 on OEL as described in Part II of this series.
  4. Installed WebLogic 10.3.3 as described in Part III of this series.
  5. Installed Oracle SOA Suite 11.1.1.3 as described in the Part IV of this series.
  6. You should have a valid database somewhere which is properly configured with the Repository Creation Utility (RCU).

Configuration Steps

Navigate to "MIDDLEWARE_HOME/Oracle_SOA1/common/bin" directory.
Right-click inside the directory and select "Open in Terminal".
Enter "./config.sh".
Select "Create a new WebLogic domain" and Next.
Check the appropriate products as shown in the screenshot.
Accept default domain name and directories.
Next.
Enter your password for weblogic.
Ensure that the Java 1.6 64 bit that we installed in the Part II of this series is available and selected in the list.
Next.
Select all the schema and provide the appropriate values for the following:
  • Service Name
  • Hostname
  • Password
  • Port

Next.
Next.
Next.
Create.
Done.

Running and Testing the Servers

Navigate to "MIDDLEWARE_HOME/user_projects/domains/base_domain/bin".
Right-click then select "Open in Terminal".
To start the Admin Server, enter "./startWebLogic.sh". (Note: Case-sensitive)
To start the SOA Server, Click File>Open Tab to open a new terminal tab on the same directory.
Enter "./startManagedWebLogic.sh soa_server1".
Enter username: "weblogic" and the password you entered in the steps above.
The SOA Server is already running when you see in the log "SOA Platform is running and accepting request".
If you are connected to a network then you can test logging-in to enterprise manager from a remote pc or from your host OS. But before so, we need to know the ip of our Oracle Enterprise Linux virtual machine.
To know the ip, in a terminal window, enter "/sbin ifconfig".
Get the ip information for use in the step below.
Open a new browser and enter "http://the_ip_above:7001/em"
Successfully log-in to enterprise manager from remote pc.

Sunday, June 20, 2010

Installing Oracle SOA Suite 11.1.1.3 on Oracle Enterprise Linux

Introduction

With the current trend of having 64 bit as the new desktop standard, then you would most likely come into a situation where you have a hard time deciding what OS to buy that can support your software development tasks, while not being left behind. That OS selection decision is made simpler by the advent of Virtualization wherein you can have multiple OS on a single hardware machine.
In this series, I will share with you how I was able to set-up and run Oracle SOA Suite on my Windows 7 Professional 64 bit OS through Oracle VM VirtualBox.
This is Part IV of the following series:
  1. Setting-up Oracle Enterprise Linux on Oracle VM VirtualBox 3.2.4
  2. Installing Java 1.6 on Oracle Enterprise Linux
  3. Installing Weblogic 10.3.3 on Oracle Enterprise Linux
  4. Installing Oracle SOA Suite 11.1.1.3 on Oracle Enterprise Linux
  5. Configuring a New Oracle SOA Domain on Oracle Enterprise Linux

Prerequisites

  1. Installed Oracle VM VirtualBox.
  2. Installed Oracle Enterprise Linux (OEL) on VirtualBox as described in the Part I of this series.
  3. Installed Java 1.6.0_20 on OEL as described in Part II of this series.
  4. Installed WebLogic 10.3.3 as described in Part III of this series.

Enabling a regular user to enter "sudo" command

Login as a regular user(in my case "pino").
Open a new Terminal window.
Enter "su" to login as root.
Enter password for root.
Modify the sudoers file.
Enter "nano /etc/sudoers".
(Warning! Be careful in editing the sudoers file.)
(The sudoers file should be edited using the visudo command but since I am not so familiar with it, I used nano.)
Just after the line "root ALL=(ALL) ALL" add like "pino ALL=(ALL) ALL"
Save changes on exit.

Installation Steps for Oracle SOA 11g PS1

Put the unzip version of the Oracle SOA 11.1.1.2 installer in the installer folder we created in the Part III of this series (in my case "/home/pino/installers").
Browse the Disk1 folder of the installer.
Right click inside the Disk1 directory and select "Open in Terminal".
Enter "./runInstaller -jreLoc $JAVA_HOME"
Accept default directory
A prompt appears. Leave the prompt at ease then open a new Terminal window from the menu- Applications>Accessories>Terminal.
Enter "sudo sh /home/pino/oraInventory/createCentralInventory.sh".
You should be able to read "The execution of the script is complete".
Go back to the prompt window and press "Ok"
Next.
Next.
Next.
Install.
Next.
Finish. We are done installing the Oracle SOA Patchset 1 but we need yet to upgrade to Patchset 2.

Installation Steps for Oracle SOA 11g PS2

Put the unzip version of Oracle SOA 11.1.1.3 Patchset installer into the installer directory we created in Part III of this series.
Browse the Desk1 folder of the installer.
Right click and then select "Open in Terminal".
Enter "sh runInstaller -jreLoc $JAVA_HOME"
Next.
Ensure that selected Middleware Home is the same as the one we specified when we installed the Oracle SOA Patchset 1.
Next.
Install.
Next.
Finish. We are done upgrading to Patchset 2.