Wednesday 14 December 2011

ADF Task Flow Input Parameter Definition common pitfall

When you define an input parameter to your ADF Task Flow and you get the following error on calling/loading the Task Flow:
javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation
Then, you've probably forgot to set the value of the Input Parameter to:
#{pageFlowScope.inputParameterName}
I don't know why but I keep on forgetting this, hence I've recorded it here now.

Tuesday 15 November 2011

ADF 11.1.2.1.0 : No row found for rowKey

If you get the following error on a View Object with a key of data type, Number:
<FacesCtrlHierBinding$FacesModel><makeCurrent> ADFv: No row found for rowKey: [oracle.jbo.Key[89898989898]]
Then, check the Access Mode of the View Object under General settings; the Tuning section. The Access Mode is set to Scrollable by default, but for some reason it was set to Range Paging in my case. Changing it back to Scrollable solved it for me!

Tuesday 8 November 2011

Toad for Oracle and literals containing the ampersand character

The best way to construct a valid varchar literal that won't be rejected by the script execution in Toad for Oracle is to do something like this:
'john'||chr(38)||'sally'
The fact that the chr() function returns the ampersand as execution result makes it less problematic.

The above solution works well when you have to deal with the ampersand character within a stored procedure or forms script where you can assign the value to a declared variable.

However, when it comes to a normal update statement the following works much better:
update my_table set url='/faces/myservlet?paramOne=valueOne' || '&' || 'paramTwo=valueTwo' where id='myId'

When the ampersand stands alone in a literal value it seems to get bypassed as special character!!

Hope you find it handy ampersandy!

Friday 4 November 2011

Toad for Oracle 9.7.2.5 : Can't initialize OCI. Error -1

IF you are running Toad for Oracle on Windows 7 and you're getting the error: "Can't initialize OCI. Error -1", when you're trying to initiate a connection, THEN go to the "Properties" of the Toad shortcut in the Start Menu and in the Compatibility Tab tick the check box next to the text: "Run this program as an administrator." When you run Toad again you would be able to open connections!! 

PS: Also make sure your ORACLE_HOME environment variable is set to the correct location. Could be a show stopper in your case like in mine. :-)

Friday 28 October 2011

ADF : Dynamically disabling Command Link component as contained in Rich Tree node stamp

I decided to use a RichCommandLink component as part of my RichTree(Menu) node stamp in order to produce actions to navigate in the application. Later on the requirement came that some sections of the system should be viewable and hence even the link to these sections should be disabled in order to prevent users from going there in the first place. Of course, the question remains why the menu was not configured correctly in the first place, because why show the tree node if it's going to be disabled anyway...but that's besides the point for this post. :-P

Well to dynamically disable the command links as contained in the tree, I've followed the following approach:
  • Set a JSP/JSTL core variable equal to some property in the node binding on which you can base the check for the view privilege. In my case I used a variable called: "mapParameter", with its value as a concatenated string containing the node's view name.
<c:set var="mapParameter" value="isViewable:#{node.ViewName}" />
  • Then, I used the above parameter in the EL Expression as value for the disabled property of the command link. You'll notice I've made the backing bean (called: menuBean) implement the Map interface in order to check for view privileges with the view name as parameter.
<af:commandLink text="#{node.Name}" id="cl1" action="someAction"
          disabled="#{((menuBean.hasPrivilege[mapParameter] == 'false') ? true : false)}"/>


I know Maps are probably not the best way in making things happen in backing beans, but I've managed to do all sorts of weird and wonderful things by using the Map method. :-)

ADF Automatically disabling and setting read only status on UI components based on fine-grained privileges

I had to automatically apply the view/select type of privilege to implemented ADF pages. The idea was to not have to put EL expression as value to all appropriate components' "disabled" or "read only" properties (hence it should happen 100% automatically). So, my solution for now was to implement a phase listener and within the afterPhase() method execute the following java code:
  // checking whether security privileges are applicable in the sense that
  // - the user is logged in
  // - the security privileges have been loaded for the user.
  if(isLoggedIn() && (null != JSFUtils.getFromSession(SessionKeys.VIEW_ENTITY_PRIVS.getKey()))) {
      // first get a reference to the View Root component
      FacesContext fCtx = phaseEvent.getFacesContext();
      UIViewRoot viewRoot = fCtx.getViewRoot();
      // We have a template component which wraps all our implemented ADF forms     
      UIComponent template = viewRoot.findComponent("p_tmpl");
      if (template != null) {
        // the template knows which Form Bean (backing bean) is tied to the current view/response
        FormBean formBean = (FormBean) template.getAttributes().get("formBean"); 
        if (formBean != null) {
          // The Form Bean knows which logical view is in question.
          String viewName = formBean.getViewNameForPrivilege();         
          if (!FormBean.NO_PRIVS_APPLIED.equals(viewName)) {
            boolean privsExist = PrivilegesUtil.doesViewEntityPrivilegesExist(viewName);
            logger.error("View PRIVS Exist?["+privsExist+"]");           
            // then if no view privileges exist for this view...we disable and set to read only all the components
            if (!privsExist) {
              logger.error("Going to cascade!!");
              this.cascadeDisableAndReadOnly(template.getFacetsAndChildren());
                } // end if (privileges don't exist)
          } // end if NOT default view name
        } // end if (formBean != null)
      } // end if (template != null)
  } // end if (isLoggedIn() && Privileges have been loaded)

For completeness sake I've provided the implementation of the cascadeDisableAndReadOnly() method:
private void cascadeDisableAndReadOnly(final Iterator<UIComponent> children) {
    if(children != null) {
      while (children.hasNext()){
          UIComponent child = children.next();              
          // PLEASE NOTE: One can add some skip conditions based on the nature and instance of the child 
          // FOR EXAMPLE: Skip any OraFormsFaces Form as child
          if (child instanceof Form) {
              logger.error("Detected OraFormsFacesForm...");
              continue;
          }
          cascadeReadOnly(child.getFacetsAndChildren());       
          child.getAttributes().put("readOnly", true);
          child.getAttributes().put("disabled", true);
      } // end while (children.hasNext())
    } // en if (children != null)
}

Friday 21 October 2011

ADF Tree : Row Disclosure Listener which expands below the disclosed node

I recently had the scenario where more than one level of a tree node had to be expanded on the disclosure of a particular tree node.

For example, say you have the below tree:
-A
-B
  - B1
  - B2
    - B2a
    - B2b
  - B3
-C
then, on the disclosure of tree node B you would want all the children (B1, B2, B3, B2a, B2b) of tree node B to be expanded.

I've done it as follow. I've added the following property to my ADF Tree:
rowDisclosureListener="#{treeBean.discloseTreeNodeChildren}" 

Then I implemented the referenced method in my "treeBean" backing bean. It assumes a tree binding called: "tree." See the listing below:
public void discloseTreeNodeChildren(RowDisclosureEvent event) {
    RowKeySet addedKeys = event.getAddedSet();
    Iterator addedIt = addedKeys.iterator();
    if(addedIt.hasNext()) {
        Object addedKey = addedIt.next();
        TreeModel treeModel = (TreeModel) this.tree.getValue();
        JUCtrlHierNodeBinding nodeBinding = (JUCtrlHierNodeBinding)
               treeModel.getRowData(addedKey);
        String isLeaf = (String) nodeBinding.getAttribute("isLeaf");
       
          // you have to have a condition to break the series of disclosure events!!
        if(!(new Boolean(isLeaf))) {
            TreeUtility.expandAllNodes(nodeBinding, this.tree.getDisclosedRowKeys(), 0, 1);
            AdfFacesContext.getCurrentInstance().addPartialTarget(this.tree);
        }
    }
}

Now the utility method called: "expandAllNodes()" I've implemented as such:
protected static void expandAllNodes(JUCtrlHierNodeBinding nodeBinding, RowKeySet
          disclosedKeys) {   
    List<JUCtrlHierNodeBinding> childNodes = (List<JUCtrlHierNodeBinding>)
        nodeBinding.getChildren();     
    ArrayList newKeys = new ArrayList();
    if(childNodes != null) {
        for(JUCtrlHierNodeBinding node : childNodes) {           
            newKeys.add(node.getKeyPath());
            // recursive call to the method expandAllNodes()         
            expandAllNodes(node, disclosedKeys);
        }
    }
    disclosedKeys.addAll(newKeys);     

}
A handy addition to the utility method above is to add currentLevel and targetLevel integers in order to apply a boundary to the expansion of nodes. This gives the ability to only expand n levels below the disclosed node...comes in handy in those "big oak trees!!" :-)

Thursday 29 September 2011

ADF TreeModel utility methods

In all my latest dealings with the RichTree component in ADF I've found the following three (3) utility methods to be very handy (let's say they were implemented in a utility class called: Utility):

  // Returns the last RowKey object from the given set
  private static Object lastRowKeyFromSet(RowKeySet set) {
    Iterator it = set.iterator();
    Object rowKeyPointer = null;
    while (it.hasNext()) {
      rowKeyPointer = it.next();
    }
    return rowKeyPointer;
  }




  // Returns a String value of the given node attribute as found in the specified TreeModel at the specified row index.
  public static String retrieveNodeAttributeFromRowIndexForTreeModel(String attributeName, int rowIndex,
    TreeModel treeModel) {
    String attr = null;

    if(rowIndex > -1) {
      Object rowData = treeModel.getRowData(rowIndex);
      JUCtrlHierNodeBinding nodeBinding = (JUCtrlHierNodeBinding) rowData;

      if(nodeBinding != null) {
        attr = (String) nodeBinding.getAttribute(attributeName);
      }
    }
    return attr;
  }


  // Returns a String value of the given node attribute as found in the specified TreeModel at the last row key
  // as per the specified row key set.
  public static String retrieveNodeAttributeFromRowKeySetForTreeModel(String attributeName, RowKeySet set,
    TreeModel treeModel) {
    String attr = null;

    if ((set != null) && (set.size() > 0)) {     
      Object currentRowKey = Utility.lastRowKeyFromSet(set);     
      JUCtrlHierNodeBinding nodeBinding = (JUCtrlHierNodeBinding) treeModel.getRowData(currentRowKey);     
      if (nodeBinding != null) {       
        attr = (String) nodeBinding.getAttribute(attributeName);       
      }
    }   
    return attr;
  }


Hope you find it handy!

Wednesday 21 September 2011

OraFormsFaces Forms Servlet URL set dynamically by java code

I've recently had to integrate with Oracle Forms as part of an ADF Web Application. Of course, the framework of choice is OraFormsFaces. But, I got particularly annoyed with the HtmlFormRenderer and the way it looks up the value of the Forms Servlet URL. This renderer currently only looks at the Java Namespace in the JNDI Context of the Web Application (the recommended way to configure OraFormsFaces as per the developer guide; environment entry in the web.xml, which gets loaded into the namespace: java:comp/env) AND the Input Parameters of the ServletContext. Nothing wrong with this approach, but there is no allowed way to change this value after the initial configuration has been done (I mean the JEE spec does not allow change of these values after start-up of your app). Hence, no way around this!

Well, I wanted to change the Forms Servlet URL value dynamically as per the environment within which my Web Application gets deployed to. So, I extended the HtmlFormRenderer of OraFormsFaces to also lookup this value from an additional place as to the JNDI Context and the Servlet Context (input parameters). My extension allows a lookup from the accessible attributes of the Servlet Context. Hence, one can implement a ServletContextListener which can set this attribute key-value pair on successful initialization of the Servlet Context, by simply making use of the setAttribute()-method on the Servlet Context interface.
On the consumer side of the Servlet Context, the extended HtmlFormRenderer will then simply do a getAttribute() with the standard OraFormsFaces-key for the Forms Servlet URL.

I thought this solves the problem in the neatest way possible, as I only had to change the "faces-config.xml", which resides in the OraFormsFaces JAR file, to point to my custom HtmlFormRenderer instead of the standard one shipped with OraFormsFaces.

I've included code sniplets of my java code from both the ServletContextListener and the HtmlFormRenderer, respectively:

(ServletContextLister)
  public void contextInitialized(ServletContextEvent ctxEvent) {
    ServletContext servletCtx = ctxEvent.getServletContext();
    servletCtx.setAttribute(OraFormsFacesUtil.FORMS_SERVLET_URL_KEY,
      OraFormsFacesUtil.getFormsServletURL());
  }


(HtmlFormRenderer)
  public class MyHtmlFormRenderer extends HtmlFormRenderer {
    .

    .
    public String getFormsServletURL(FacesContext facesContext) {
      String url = null;   
      ServletContext servletContext = (ServletContext)
           facesContext.getExternalContext().getContext();
      url = (String) servletContext.getAttribute(
          OraFormsFacesUtil.FORMS_SERVLET_URL_KEY);
      if (url != null) {
        String result = url != null ? url.trim() : null;
        return result;     
      } else {
        return super.getFormsServletURL(facesContext);
      }
    }
  }
You'll notice I've used a helper class (OraFormsFacesUtil) to keep the Forms Servlet URL attribute key in one place and to implement the functionality which determines what the Forms Servlet URL value must be.

Currently, I'm thinking a better solution would be to implement the above solution similar to the way the FormsCredentialProvider configuration works. This way, you will also simply specify a class name in the web.xml of your Web Application which adheres to a given interface (i.e. FormsServletURLProvider, declaring a method with signature: String getFormsServletURL(), for example) which can then be used to retrieve the URL programmatically. Then the implementation can rely on configuration or java code; for the developer to decide.

OraFormsFaces using a reusable Task Flow to load Forms in a common way

I've implemented a common/reusable Task Flow to load Oracle Forms in a generic way in order to function as part of an ADF Web Application in an integrated fashion.

Below is the source-xml of the Task Flow I've implemented (oraFormsFaces-tfd.xml):
<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="oraFormsFaces-tfd">
    <default-activity>loadForm</default-activity>
    <input-parameter-definition id="__2">
      <name>formName</name>
      <value>#{pageFlowScope.formName}</value>
      <class>java.lang.String</class>
      <required/>
    </input-parameter-definition>
    <input-parameter-definition id="__3">
      <name>ipAddress</name>
      <value>#{pageFlowScope.ipAddress}</value>
      <class>java.lang.String</class>
      <required/>
    </input-parameter-definition>
    <input-parameter-definition id="__4">
      <name>formDescription</name>
      <value>#{pageFlowScope.formDescription}</value>
      <class>java.lang.String</class>
      <required/>
    </input-parameter-definition>
    <managed-bean id="__1">
      <managed-bean-name>oraFormsFacesBean</managed-bean-name>
      <managed-bean-class>com.myflow.common.view.oraformsfaces.OraFormsFacesBean</managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>
    <view id="loadForm">
      <page>/oraformsfaces/loadForm.jspx</page>
    </view>
  </task-flow-definition>
</adfc-config>


You'll notice the following from the above listing:
  1. I've defined three (3) input parameters to my Task Flow for it was required by the specific use case. An interesting thing to mention is that I had to set these input parameters dynamically. This is actually done in a very simple way by calling the above Task Flow with an URL as follow: "/faces/adf.task-flow?adf.tfId=oraFormsFaces-tfd&adf.tfDoc=/WEB-INF/oraformsfaces/oraFormsFaces-tfd.xml", and then simply appending the input parameters as request parameters to the URL as such: "?formName=MYORAFM&ipAddress=10.4.5.6.7&formDescription=My Oracle Form".
  2. I've implemented a backing bean (OraFormsFacesBean) within the Task Flow to facilitate the representation of user friendly information to the user AND to expose the values to be passed to OraFormsFaces and Forms as available attributes retrievable from the backing bean. As you'll see later these attributes are referenced from the loadForm.jspx page.
  3. The page, /oraformsfaces/loadForm.jspx, facilitates the load of the actual OraFormsFaces Form component. Please see the source of this page in the listing below:
loadForm.jspx
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
          xmlns:off="http://commit-consulting.com/OraFormsFaces/tags">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document title="SPIF" id="loadFormDoc" maximized="true">
      <af:pageTemplate id="p_tmpl" viewId="/templates/defaultFormTemplate.jspx">
        <f:facet name="mainContent">
          <af:panelGroupLayout id="pgl2" styleClass="AFStretchWidth">
            <off:form formModuleName="#{pageFlowScope.formName}" id="oraFormsFacesForm" clipApplet="true"
                      autoClipTop="menu" loadingImage="preset5" autoSize="true" uniqueAppletKey="piet">
              <off:formParameter id="userID" value="#{oraFormsFacesBean.username}"/>
              <off:formParameter id="companyCode" value="#{oraFormsFacesBean.companyCode}"/>
              <off:formParameter id="clientIP" value="#{pageFlowScope.ipAddress}"/>
              <off:formParameter id="companyCode" globalName="companyCode" value="#{oraFormsFacesBean.companyCodeCode}"/>
              <off:formParameter id="userid" globalName="userid" value="#{oraFormsFacesBean.username}"/>
            </off:form>
          </af:panelGroupLayout>
        </f:facet>
        <f:attribute name="formBean" value="#{oraFormsFacesBean}"/>
        <f:attribute name="displayLinksFacet" value="false"/>
      </af:pageTemplate>
    </af:document>
  </f:view>
</jsp:root>



Please note how I've used the OraFormsFacesBean as backing bean to provide the values:
  • formName
  • username (send through as 'userID')
  • companyCode (also send through as 'companyCode')
  • ipAddress (send through as 'clientIP')
Also important to note, is that there are two (2) Global Forms Parameters (going directly through to Oracle Forms) and three (3) OraFormsFaces Parameters of which I only passed through the latter in order for it to get used by the off_lib.pll (PLS/SQL library) provided as part of the OraFormsFaces installation.

For completeness sake, I'll explain what I did in the off_lib.pll PL/SQL library. First observe the script sniplet below:
    elsif eventName = 'initapplet' then
          -- add code here that is execute when the applet is first started
          -- in a browser session. This is not re-executed when the applet is
          -- reused on subsequent pages.
          --null;
          v_params := offParams.getParameters;
          v_userID := offParams. getParamValue(v_params, 'userID');
          v_companyCode := offParams. getParamValue(v_params, 'companyCode');
          v_clientIP := offParams. getParamValue(v_params, 'clientIP');
          APPSYS.AUTH.prepare_session(v_userID,v_companyCode,v_clientIP);

You'll notice that I've added a few statements in the 'initapplet'-clause of the "eventName-if-statement" as found in the handleGlobalEvent(eventName in varchar2) procedure in off_lib.pll. These statements simply extract the parameters which were sent through and then send them through to an implemented Stored Procedure that will do the necessary preparation work for the Oracle Forms to function in an integrated fashion. For example, this procedure will set the session identifier on the database session to the value of 'userID' for auditing purposes.

I hope someone finds this as useful as I did!
 

Using an Environment Info Provider to switch between Data Sources

I had to deal with a requirement where the Application Module had to choose between two configured Data Sources depending on some Principal as configured in the Security Context's Subject. The way to do it is as follow:

1. Implement a class that extends oracle.jbo.common.ampool.EnvInfoProvider
2. Override the "public Object getInfo(String infoType, Object env)"-method
    i.e. body for the above method to be overridden below:

    if(EnvInfoProvider.INFO_TYPE_JDBC_PROPERTIES.equals(infoType)) {
      String dsName = DEFAULT_DS_JNDI_NAME; // the default Data Source' JNDI Name    
      if(ADFContext.getCurrent().getSecurityContext().getSubject() != null) {
        Subject sbj = ADFContext.getCurrent().getSecurityContext().getSubject();
        // utitlity method to extract the FcoCode principal from the given subject
        String fcoCode = AMUtil.interrogateSubjectForFinanceCompanyPrincipal(sbj);       
        if(fcoCode != null) {
            // calling private method to determine the appropriate Data Source JNDI Name
            // based on FcoCode
          dsName = this.determineDataSourceFromFcoCode(fcoCode);        
        }       
      }     
      ((Hashtable) env).put(Configuration.JDBC_DS_NAME, dsName);
    }
    return null;


3. Configure the Application Module to make use of the implemented Environment Info Provider by
    specifying your custom class as value for property: "jbo.envinfoprovider".
    (see screenshot below)




 

Friday 9 September 2011

OraFormsFaces Java trusted/untrusted warning message

Problem statement:
The OraFormsFaces' applet contains code which is trusted and also untrusted. So, as per Java's help pages: "As of the Java SE 6 Update 19 release, when a program contains both signed and unsigned components, a warning dialog is raised. " Therefore, if it is the case with your scenario as it was with mine you'll see the following warning dialog when you load a OraFormsFaces Form component in ADF:

Resolution:
You have to open the Java Console from Control Panel (in the case of Windows) and change the security settings in order to make the display of the warning message go away.
To access the Java Control Panel goto, Start menu > Control Panel > Java Control Panel > Advanced > Security. See the Mixed code heading towards the bottom of the Java Control Panel.
You can tick any of the two "Enable - hiding warning..." options provided. In my case I picked: "Enable - hide warning and run with protections." I've included a screenshot of the Java Console Security options as it is displayed in Windows 7:

I hope you've found it useful! :-)

Friday 19 August 2011

OraFormsFaces 3.1.7 and what appears to be a Forms Parameter not passed through

Today I was so relieved when I discovered that the OraFormsFaces Forms Parameter which I've added on my JSF page, is indeed working as it should.

Let me depict the scenario:
I wanted to implement a simple JSF page loading a form via OraFormsFaces with the Forms Instance configured to use the crypto user with the cipher key as stated in the OraFormsFaces 3.1.7 development guide. Obviously, I also implemented a FormsCredentialProvider custom Java class to facilitate the cipher key, username & password (proxy login, of course). However, everytime the form loads I get a Forms error indicating that the Forms parameter was not set.
LATER ON I figured out that this Forms Parameter gets set based on a query which requires a Global Forms Parameter. So, I've changed the Forms Parameter on my JSF page to be a Global Forms Parameter, but still it gave me the same error.
THEN, I've noticed the "100"-sqlerror which forms part of the error and realised the SQL query which needs to set the actual Forms Parameter returns 0 rows. Hmmm...that had me thinking for a minute and it suddenly dawned on me; it must be the proxy user authenticating on behalf of the real user who does not have select privileges for the query to execute and return the expected resultset. So, I corrected that and then all was fine in OraFormsFaces JSF land! :-)

Thursday 11 August 2011

ADF Security : Logout action

ADF Security provides an Authentication Servlet with a logout method in order to invalidate the JAAS Session that was initialised on login. For completeness sake I've listed the java code you can use in your backing bean's action method in order to call this servlet method:
public String onLogout() {
    FacesContext facesCtx = FacesContext.getCurrentInstance();
    ExternalContext eCtx = facesCtx.getExternalContext();
    String url = eCtx.getRequestContextPath() +
        "/adfAuthentication?logout=true&end_url=/faces/index.jspx";
    try {
        eCtx.redirect(url);
    } catch(IOException e) {
        e.printStackTrace();
    }
    facesCtx.responseComplete();   
    return null;
}

Wednesday 10 August 2011

ADF Task Flows and the wildcard (*)-"To Activity ID"

Be careful when you use the wildcard (*) (or nothing for the from outcome or from action properties, for that matter) as ID for the "To Activity ID" to direct the flow back to a Router ADF Task Flow component. Due to the wildcard all activities/actions will be consumed by the Router component and hence only direct the flow as it is configured in the Router component's cases.

I had a case where an action, which originated from outside the page flow scope, did not actually get performed/handled by loading another task flow or page and I eventually figured that the router component consumes the action and makes it ineffective.

Tuesday 2 August 2011

JDeveloper 11.1.2.0 : Looking up a binding reference to inherited/referenced Application Module as Data Provider via the Data Control binding

The whole idea of getting an Application Module reference, programmatically, via the bindings by using the appropriate Data Control name is common practice in ADF. As we all know you do it like this:
DCBindingContainer dcBindingContainer = (DCBindingContainer) getBindingContainer();
DCDataControl dataControl = dcBindingContainer.findDataControl("DataControlName");
ApplicationModule appMod = (ApplicationModule) dataControl.getDataProvider();
// (where 'ApplicationModule' will be a specific ApplicationModule class of yours; i.e. 'CommonAM')
However, if you are trying to find a reference to a referenced/inherited Application Module via its Data Control before you've ever used any bindings on this Data Control, the programmatic approach will return a null reference.
The solution is to drag and drop any sub-element from the given Data Control onto a page of yours and that will ensure that the needed BC4J configuration for the use of the Data Control will be added to the file: "DataBindings.cpx" (of course you can delete the created component on your page afterwards)

Here's an example BC4J config:
<BC4JDataControl id="CommonAMDataControl" Package="com.common.model.am"
                     FactoryClass="oracle.adf.model.bc4j.DataControlFactoryImpl" SupportsTransactions="true"
                     SupportsFindMode="true" SupportsRangesize="true" SupportsResetState="true"
                     SupportsSortCollection="true" Configuration="CommonAMLocal" syncMode="Immediate"
                     xmlns="http://xmlns.oracle.com/adfm/datacontrol"/>


Once this BC4J configuration has been added...your java code will lookup the bindings successfully. 

Friday 29 July 2011

ADF Security : JDeveloper 11g Release 2 (11.1.2.0)

Although I know about the bug with the anonymous role in JDeveloper 11.1.1.5, I used the standard wizard approach in applying ADF Security to my application. However that did not work at all. 
After Weblogic started up and my page should load, Weblogic seemed to be caught in an endless loop?!?!?!?

These were my steps to get it working:

  1. I only configured one (1) realm called: 'myrealm' and also made it the default realm (in other words, I deleted the realm named 'jazn'). After I did this and tried to test it, I got the known error (InvocationTargetException) which occurs when JDeveloper tries to migrate the security policies when your realm contains the 'anonymous-role'
  2. After deleting the 'anonymous-role' from my realm (<app-roles>-section in jazn-data.xml) the security policies were migrated successfully on the next startup of Weblogic.
Hooohooo!! :-)

My GO at creating a Menu Tree in ADF.

My steps:
  1. I created a SQL based View object called: MenuNodeVO.
  2. I created a View Object Link in order to setup a hierarchy that the ADF Tree can use.
  3. I created a root node View Criteria for MenuNodeVO to make it possible for only root nodes to be returned for the root nodes in the tree.
  4. I dragged and dropped my parent View Object from the Data Control to my page as an ADF Tree.
  5. I created a binding for the RichTree component in the page's backing bean.
  6. I implemented code to set the bind parameters on the view objects involved in the tree hierarchy. 
 1. I created a SQL based View object called: MenuNodeVO
This MenuNodeVO makes use of a hierarchical SQL query as such:
select id,
       parent_id,
       name,
       action,
       menu_type
from nodes
connect by prior id = parent_id
start with parent_id = -1
...(where 'nodes' denotes a table/select-statement containing all possible menuitems)

NB! After creation of MenuNodeVO set the key attribute for the view object. (i.e. id)

ALSO, I had bind parameters as part of the query amounting to 'nodes' in the query stated above and hence these bind parameters had to be set programmatically in order for the tree component to show up with the nodes and not be empty or break! So, be sure to set these bind parameters in the backing bean of your .jspx page.

Here's how to do it!

2. I created a View Link in order to setup a hierarchy that the ADF Tree can use.
There are a lot of examples out there which makes use of a different view object for each level in the tree. But, in order for the tree we're planning to create to be truly recursive and go from 1..n levels, one actually needs to use the same View Object for each level in the tree...I think it makes sense.
So, that's what I did. I created a View Link linking my MenuNodeVO to itself using id-to-parent_id as the source to destination view object attributes. Another important thing is to make sure that you specify the child view object accessor name as part of the View Link creation, as you might need it later on (In my case I did). I called it something like 'MenuChildNodes' as the name for the destination view object accessor.


3. I created a root node View Criteria for MenuNodeVO to make it possible for only root nodes to be returned for the root nodes in the tree.
This simply involved creating a View Criteria as you would usually do and adding a group item as criteria which specifies
that the parent_id should be equal to whatever the null parent id would be. (i.e parent_id = -1)
In other words, the criteria should ensure that only menu items with null parent ids should be returned by the MenuNodeVO execute query.
I named the criteria: "RootMenuNodeVOCriteria"

4. I dragged and dropped my parent View Object from the Data Control to my page as an ADF Tree.
Drag and drop is drag and drop. Nothing more to be said about that - LOL.
On the Edit Tree Binding Dialog (which appears when you drag and drop) however, you have to make sure of the following:

  • The parent node View Object iterator should be selected as your Root Data Source.
  • You should only have one Tree Level Rule specified as MenuNodeVO, with the destination child View Object accessor name in parenthesis next to it [i.e com.model.vo.MenuNodeVO(<MenuChildNodesVO>) ]
  • As usual make sure that you pick the display attributes for the Tree as you require.
5. I created a binding for the RichTree component in the page's backing bean.
The following are important:

  • You go to the binding property in the Property Inspector in JDeveloper while your RichTree component is selected.
  •  You click the down arrow next to the property and then 'Edit'.
  •  Then you select your managed bean and the property in the 'Edit Property: Binding' dialog as shown below:

6. I implemented code to set the bind parameters on the view objects involved in the tree hierarchy.
For completeness sake I've provided the getter and setter for the RichTree property binding as I've implemented in my backing bean:

public void setMenuTree(RichTree menuTree) {
    DCIteratorBinding iter = ADFUtils.findIterator("ParentNodeIteratorName");
    ViewObject parentVO = iter.getViewObject();
    parentVO.ensureVariableManager().setVariableValue("BindVariableName", "bindVarValue");

    ViewCriteria vc = parentVO.getViewCriteriaManager().getViewCriteria("RootMenuVOCriteriaName");
    vc.ensureVariableManager().setVariableValue("BindVariableName", "bindVarValue");
    parentVO.applyViewCriteria(vc, false) /* do not append the view criteria */;

    ViewObject childVO = ADFUtils.getChildViewObjectFromParent(parentVO, "ChildNodeVOAccessorName");
    childVO.ensureVariableManager().setVariableValue("BindVariableName", "bindVarValue");

    // then only set reference to given RichTree
    this.menuTree = menuTree;
}

public RichTree getMenuTree() {
    return menuTree;
}

Thursday 28 July 2011

ADF Tree : Setting Bind Parameters on a hierarchy of View Objects in your backing bean code

I was faced with the scenario of a hierarchical View Object defined in a View Link in order to make use of it in an ADF Tree component. The re-used/recursive View Object has bind parameters which are required and have to be set.

It was easy enough to get to the "parent" View Object in the hierarchy by using the Iterator binding as in the code sniplet below:

// get reference to the parent view object via the iterator binding
DCIteratorBinding iter = ADFUtils.findIterator("IteratorName");
ViewObject parentVO = iter.getViewObject();

// set bind parameter value on parent view object
parentVO.ensureVariableManager().setVariableValue("BindParameterName",      "BindParameterValue");

However, the "child" View Object in the hierarchy remained without its bind parameters set. Eventually, I got to the following solution (PLEASE NOTE: You have to use the destination view object accessor name as it is defined in the View Link you've created):

// get reference to the child view object via the parent view object binding
ViewObject childVO = ADFUtils.getChildViewObjectFromParent(parentVO, "ChildVOAccessorName");

// set bind parameter value on child view oibject
childVO.ensureVariableManager().setVariableValue("BindParameterName", "BindParameterValue");

The ADFUtils utility methods used above were implemented as follow:

public static DCIteratorBinding findIterator(String name) {
    DCIteratorBinding iter = getDCBindingContainer().findIteratorBinding(name);
    if(iter == null) {
      throw new RuntimeException("Iterator '" + name + "' not found");
    }
    return iter;
}

public static ViewObject getChildViewObjectFromParent(ViewObject parentVO, String childAccessorName) {
    AttributeDef attrDef = parentVO.findAttributeDef(childAccessorName);
    if(attrDef == null) {
      throw new RuntimeException("Attribute Definition '" + childAccessorName + "' not found on parent view object");
    }   
    return attrDef.getAccessorVO(parentVO);
}

Thursday 21 July 2011

The same old Time Zone error resurrects itself

This time I got it when I tried to create a View Object (JDeveloper 11g release 2 - 11.1.2.0). Or rather let me specify. I tried to create a View Object of a SQL query and on validation of the query obviously it probed the data source and it was exactly then when I got an "ORA"-error moaning about the time zone.

After trying the usual Project Settings --> Run/Debug Profile --> Launch Settings and setting the Java time zone variable there, it only started working once I've put the following line in the file "jdev.conf" (located in: /Middleware/jdeveloper/jdev/bin):

AddVMOption  -Duser.timezone="+2.00"  (it was "+2.00" in my case... :-) )

Then after a restart of JDeveloper it was working fine!

Tuesday 28 June 2011

JInitiator 1.3.1.22 crashing on IE 7 and later

After you've loaded the forms URL (i.e. http://<hostname>/forms/frmservlet?config=<config_name>) and installed JInitiator  you have to replace the jvm.dll file in the hotspot directory of the JInitiator installation with the Java Client Plugin's jvm.dll file. For example I've copied a 'jvm.dll' file from my Java\jre1.5.0_22\bin\client directory and pasted that into my JInitiator's hotspot folder (JInitiator 1.3.1.22\bin\hotspot).

That solves it and on the next load of the forms URL (i.e. http://<hostname>/forms/frmservlet?config=<config_name>)  it will load the forms applet successfully.

Friday 3 June 2011

JDeveloper 11.1.1.5 Integrated Weblogic JDKLog4J error

Problem:

After you have changed the logging implementation of your Integrated Weblogic to be Log4J (for whatever good reason), you might see the following exception get thrown on startup: 

<2011/06/03 16:10:35> <Error> <Log Management> <BEA-170022> <Log4j failed to initialize for DefaultServer. The default JDK Logging implementation will be used.
java.lang.ClassNotFoundException: weblogic.logging.log4j.JDKLog4jAdapterFactory

 Solution:

Put the following JARs in your DefaultDomain's lib folder:                                    (<user_home>/JDeveloper/system11.1.1.5.37.60.13/DefaultDomain/lib)  

wllog4j.jar [fetch it from Weblogic's server/lib/ directory]

com.bea.core.apache.log4j_1.2.13.jar [fetch it from JDEV's Middleware/modules/ directory]

Now the Log4J instance of the Integrated Weblogic initialize properly.