Wednesday, December 31, 2014

ADF: Show/Hide af:outputText based on Interval

Recently I came across a requirement to show/hide text on page based on certain time interval. I achieved it by using af:poll component. Even though we can do it through setInterval function available in javascript, I faced some problem with clearInterval in different browsers.

af: poll: if you set timeout as -1 it will poll infinitely.

Java Script:
        <af:resource type="javascript">
          var _ivtxt;
          function updateText(evt) {
              evt.cancel();
              var potll = evt.getSource();
              _ivtxt = potll.findComponent("otiv4");
              var vis = _ivtxt.getProperty("visible");
              if (vis) {
                  _ivtxt.setVisible(false);
              }
              else {
                  _ivtxt.setVisible(true);
              }
          }
 </af:resource>


JSF:
           <af:poll interval="#{pageFlowScope.showText? 1000 : -1}"
                       id="p5" timeout="-1">
                 <af:clientListener method="updateText" type="poll"/>
           </af:poll>

           <af:outputText value="Sample Text" id="otiv4" clientComponent="true"/>

ADF:JBO-33001:oracle.jbo.ConfigException

Recently I created a ADF application having multiple models and single view controller project. I exported same application as ADF Library and added to another application. When I tried to run the application, I faced following error.

Error:
javax.el.ELException: oracle.jbo.JboException: JBO-29000: Unexpected exception caught: oracle.jbo.ConfigException, msg=JBO-33001: Configuration file /com/bc4j.xcfg is not found in the classpath.
        at com.sun.el.parser.AstValue.invoke(AstValue.java:191)
        at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:297)
        at org.apache.myfaces.trinidad.component.UIXComponentBase.broadcastToMethodExpression(UIXComponentBase.java:1459)
        at oracle.adf.view.rich.component.UIXDialog.broadcast(UIXDialog.java:97)

 To fix this problem, I made following changes to View Controller project's Deployment profile.

Solution:
1. Open ViewController project properties dialog, select Deployment option and edit the required deployment profile.
2. In Deployment Profile Edit window, select Library Dependencies node and click the Edit Dependencies icon
3. In Edit Dependencies window, add a dependency on the build output path or on one or more deployment archives.

Sunday, November 30, 2014

ADF: Add New row after Selected row

ADF 11g 11.1.1.7.0

By default, if you execute 'CreateInsert' operation on selected iterator, a new empty row will be added before selected row. But if you need to add a row after selected row, you need to perform little programmatic logic to achieve it.

Following code block will help you to do it:

    public void addRowAfter(ActionEvent actionEvent) {
        // Add event code here...
        DCBindingContainer DCbindings =
            (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
        DCIteratorBinding dciter =
            (DCIteratorBinding)DCbindings.get("EmployeesVO1Iterator");
        //access the underlying RowSetIterator
        RowSetIterator rsi = dciter.getRowSetIterator();
        //get handle to the last row
        Row currRow = rsi.getCurrentRow();
        //obtain the index of the last row
        int currRowIndex = rsi.getRangeIndexOf(currRow);
        //create a new row
        Row newRow = rsi.createRow();
        //set new row attribute value
        newRow.setAttribute("PrimaryId", currRowIndex );
        //initialize the row
        newRow.setNewRowState(Row.STATUS_INITIALIZED);
        //add row to last index + 1 so it becomes last in the range set
        rsi.insertRowAtRangeIndex(currRowIndex + 1, newRow);
        //make row the current row so it is displayed correctly
        rsi.setCurrentRow(newRow);
    }

Friday, October 31, 2014

WebCenter Content: CheckIn Form Performance Issue with Author Field

Applicable to WebCenter Content(UCM) 11.1.1.8.0

Recently I faced an performance issue with WebCenter Content Checkin form after integration with OID(LDAP).

Whenever I clicked on New CheckIn link, the page used to take 3-4 minutes and sometimes throws following out of memory error in logs.

Error:
java.io.IOException: !csUnableToExecMethod,processFilterEvent
     at idcservlet.common.ClassHelperUtils.convertToIOException(ClassHelperUtils.java:167)
     at idcservlet.ServletUtils.executeContentServerIntegrateMethodOnConfig(ServletUtils.java:1686)
Caused by: java.lang.OutOfMemoryError: allocLargeObjectOrArray: [C, size 16400
      at java.io.BufferedWriter.(BufferedWriter.java:87)
      at java.io.BufferedWriter.(BufferedWriter.java:70)

I was able to resolve out of memory error by increasing JVM size for content server. Luckily I found 3 different solutions(based on requirements) to improve performance and avoid JVM size increase.

Cause: The issue found to be due to huge set of users loaded from OID to Content Server USERS table and wheneven checkin form loads, the page tries to load all the users from USERS table in Author field drop down list.

Solution:
1. Restrict Author Field to display only LOCAL users

The data presented in the metadata option lists is controlled by Tables and Views within the Configuration Manager.  One specific view used for the Author field is called "docauthors" and it contains the values of all the users in the system including external users.

If this view is modified and only "Local" users are included the list is much more manageable and checkin page response times should be much faster.

Steps:
  • Open Configuration Manager --> Views --> docAuthors --> Edit --> Options 
  • Set dUserAuthType = LOCAL and publish schema
  • View the checkin page. View the options under the Author drop down and the external users should now be missing

2. Create Global Rule and Set Author field as Info Only

Set Author field as Info only, so that CheckIn form won't load all the users from USERS table. It will display only logged in user in read-only format.
  • Open Configuration Manager --> Rules --> click Add
  • Provide Rule name and switch to Fields tab. Click Add
  • Select Field Name 'Author'(dDocAuthor) and click OK
  • Select Field type as 'Info Only' and click OK
  • Click OK to save & apply Rule

3. Disable Users Sync

In 11.1.1.8.0 version, by default there's a scheduled job that runs, SynchronizeUsers. It does a query to the OID(LDAP) server and populates the UCM schema Users table.

Steps:
  • In the config.cfg file add this parameter: DisableSyncUsers=true
  • Restart the UCM
  • In the Users table, drop the rows that have EXTERNAL set in the dUserAuthType column

Note: Do not drop the rows that have LOCAL or GLOBAL set in the dUserAuthType column

Sunday, September 28, 2014

BPEL 1.1: While Activity & Traverse through Collection

Following topic will help you to understand one of BPEL Advanced activities 'While' usage & how to traverse/iterate through data collection.

1. Create two variables in your BPEL as mentioned below.

    <variable name="Counter" type="xsd:int"/>
    <variable name="Nodes" type="xsd:int"/>

2. Add Assign activity to flow.

>  set Counter variable value to 1 &
>  count the number of nodes in data collection and assign to Nodes variable
 
    <assign name="InitializeVariables">
      <copy>
        <from expression="1"/>
        <to variable="Counter"/>
      </copy>
      <copy>
        <from expression="ora:countNodes('InputVariable','EmpCollection','/ns2:EmpCollection/ns2:Emp')"/>
        <to variable="Nodes"/>
      </copy>
    </assign>

Note: Typically use the expression builder to identify the element then replace the bpws:getVariableData with ora:countNodes.

3. Add While activity and specify condition as needed.

   condition="bpws:getVariableData('Counter') &lt;= bpws:getVariableData('Nodes')"
 
4. Access record at particular index in the array using []. Specify Counter variable to iterate through each record in collection.

    /ns2:EmpCollection/ns2:Emp[bpws:getVariableData('Counter')]/ns2:empId

5. Increment Counter in the end. Sample While Activity block provided below.

    <while name="WhileMultiNodes"
           condition="bpws:getVariableData('Counter') &lt;= bpws:getVariableData('Nodes')">
      <sequence name="Sequence">
        <assign name="AssignValue">
          <copy>
            <from variable="InputVariable"
                  part="EmpCollection"
                  query="/ns2:EmpCollection/ns2:Emp[bpws:getVariableData('Counter')]/ns2:empId"/>
            <to variable="empID"/>
          </copy>
        </assign>
        <assign name="IncrementCounter">
          <copy>
            <from expression="bpws:getVariableData('Counter') + 1"/>
            <to variable="Counter"/>
          </copy>
        </assign>
      </sequence>
    </while>

ADF: Export only Selected Rows from Table

Following post helps you to export only selected rows from ADF table.

We all know that af:exportCollectionActionListener operation allows to export rows from ADF table. by default it exports all the rows exists in the table.

Default af:exportCollectionActionListener usage:

  <af:commandButton text="Export Rows" id="cb1">
          <af:exportCollectionActionListener exportedId="t1"   -- t1 refers to ID for the ADF table
                                                               type="excelHTML"
                                                               filename="exportedRows.xls"/>
  </af:commandButton>

If you want to export the selected rows only from ADF table,  af:exportCollectionActionListener tag contains another attribute named exportedRows.

exportedRows --> valid values 'all' or 'selected'have to modify our above code as shown below.

  <af:commandButton text="Export Selected Rows" id="cb1">
          <af:exportCollectionActionListener exportedId="t1"
                                                               type="excelHTML"
                                                               filename="exportedRows.xls"
                                                               exportedRows="selected"/>
  </af:commandButton>

Sunday, August 31, 2014

Visual Workflow - ADF Hierarchy Viewer

Recently I have been asked to provide a visual workflow diagram based on series of events triggered in a Business process. In order to develop this visual flow, I used one of ADF Data visualization components i.e. hierarchy viewer.

This is how it looks:

Steps followed:
1. Defined a data model required for this component
2. Changed visual properties of component to render diagram as per values inserted into data objects
3. Used default images available with framework to display status


Sunday, July 27, 2014

OPSS Trust - Identity Propagation over HTTP/S

When we are interacting with external systems over HTTP/S protocol, some times we need to exchange the identities (valid credentials) over protocol to authorize the requested action. Using OPSS trust between two different Weblogic domains, we can propagate identities across HTTP/S enabled applications by providing and validating tokens. The OPSS trust service uses identity asserter available in Weblogic.

Pre-requisites: Same identity should exists on both the domains.

OPSS Trust configuration steps:

Source Domain Configuration:

1. Create a certificate based on source domain and add it to Key Store
>keytool -genkeypair -alias <domainName> -keypass welcome1-keyalg RSA -dname "<domainName>" -keystore default-keystore.jks -storepass welcome1

2.Export source domain certificate
>keytool -export -alias <domainName> -file <domainName>.cer -keystore default-keystore.jks -storepass welcome1

3. Copy the generated default-keystore.jks to ${domain.home}/config/fmwconfig

4. Check the keystore service configured in the file jps-config.xml points to the generated default-keystore.jks

<serviceInstance name="keystore" provider="keystore.provider" location="./default-keystore.jks">
            <description>Default JPS Keystore Service</description>
            <property name="keystore.provider.type" value="file"/>
            <property name="keystore.file.path" value="./"/>
            <property name="keystore.type" value="JKS"/>
            <property name="keystore.csf.map" value="oracle.wsm.security"/>
            <property name="keystore.pass.csf.key" value="keystore-csf-key"/>
            <property name="keystore.sig.csf.key" value="sign-csf-key"/>
            <property name="keystore.enc.csf.key" value="enc-csf-key"/>
     </serviceInstance>

5. Create a map/key pair used to open the keystore and another map/key pair used to issue tokens. The following commands illustrate these operations using the OPSS script createCred:
  • connect("weblogic","welcome1","t3://localhost:7001")
  • createCred(map="oracle.wsm.security", key="keystore-csf-key", user="keystore", password="welcome1", desc="Keystore Password")
  • createCred(map="oracle.wsm.security", key="sign-csf-key", user="orakey", password="welcome1", desc="Signing key")
6. Add a grant like the following to the policy store, which allows the client application to use the trust service API:

<grant>
  <grantee>
    <codesource>    
<url>file:${oracle.deployed.app.dir}/<MyApp>${oracle.deployed.app.ext}</url>
    </codesource>
  </grantee>
  <permissions>
    <permission>          
<class>oracle.security.jps.service.trust.TrustServiceAccessPermission</class>
        <name>appId=*</name>
        <actions>issue</actions>
     </permission>
    </permissions>
</grant>

7. Weblogic Identity Asserter Configuration:
Copy the WebLogic identity asserter JAR jps-wls-trustprovider.jar to the location ${domain.home}/lib/mbeantypes,as illustrated by the following command, and then restart the WebLogic Server.

>cp ${common.components.home}/modules/oracle.jps_11.1.1/jps-wls-trustprovider.jar ${domain.home}/lib/mbeantypes

8. Reboot Weblogic server and Login to WLS console.

Navigate to Security Settings > Security Realms > myrealm > Providers Tab > Authentication, and click New to open the Create a New Authentication Provider dialog.

In that dialog, enter TrustServiceIdentityAsserter in the name box, and select TrustServiceIdentityAsserter from the pull-down in the type box; then click OK.

9. Trust service configuration:
Open $DOMAIN_HOME\config\fmwconfig\jps-config.xml

        <propertySet name="trust.provider.embedded">
            <property name="trust.aliasName" value="<domainName>"/>
            <property name="trust.issuerName" value="<domainName>"/>
            <property name="trust.clockSkew" value="60"/>
            <property name="trust.token.includeCertificate" value="false"/>
            <property name="trust.token.validityPeriod" value="1800"/>
            <property name="trust.provider.className" value="oracle.security.jps.internal.trust.provider.embedded.EmbeddedProviderImpl"/>
        </propertySet>

10. Include $WLS_HOME\oracle_common\modules\oracle.jps_11.1.1\jps-api.jar in application classpath and add following code to application generate token.

public static final String AUTH_TYPE_NAME = "OIT";
String user = "weblogic"; 
URL url = "http://host:port/destinationApp"; 

JpsContextFactory ctxFactory = JpsContextFactory.getContextFactory();
JpsContext jpsCtx = ctxFactory.getContext();
final TrustService trustService = jpsCtx.getServiceInstance(TrustService.class);
final TokenManager tokenMgr = trustService.getTokenManager();
final TokenContext ctx = tokenMgr.createTokenContext(
    TokenConfiguration.PROTOCOL_EMBEDDED);
UsernameToken ut = WSSTokenUtils.createUsernameToken("wsuid", user);
GenericToken gtok = new GenericToken(ut);
ctx.setSecurityToken(gtok);
ctx.setTokenType(SAML2URI.ns_saml);
Map<String, Object> ctxProperties = ctx.getOtherProperties();
ctxProperties.put(TokenConstants.CONFIRMATION_METHOD,
    SAML2URI.confirmation_method_bearer);
AccessController.doPrivileged(new PrivilegedAction<String>() {
    public String run() {
        try {
            tokenMgr.issueToken(ctx);
        } catch (Exception e) {        
            e.printStackTrace();
        }
        return null;
    }
});
Token token = ctx.getSecurityToken();
String b64Tok = TokenUtil.encodeToken(token);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setReadTimeout(10000);
connection.setRequestProperty("Authorization", AUTH_TYPE_NAME + " " + b64Tok);
connection.connect();
BufferedReader rd = new BufferedReader(new InputStreamReader(
    connection.getInputStream()));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = rd.readLine()) != null) {
    sb.append(line);
}
connection.disconnect();
System.out.println(sb.toString());

Destination Domain Configuration:

Note: Here I assumed that, destination domain already have default-keystore configured. Else repeat source domain steps to configure default-keystore and credential store.

11. Repeat step 7 & 8 on destination domain

12. Import source domain certificate into destination domain key store
>keytool -importcert -alias <domainName>-file <domainName>.cer -keystore $DOMAIN_NAME/config/fmwconfig/default-keystore.jks -storepass welome1

13.  Repeat previous Step 9 on destination domain

Now test the integration by accessing source application and make a request to destination.

Credential Store Access from BPEL

Some times we need to pass restricted information like credentials in web service request payload to end point systems. In this case, we have to manage the credentials in source system to pass it in run time. As a best practice, we can store this information in Credential Key store or in database(encrypted). Here I will share the steps to manage it in Credential Key Store.

Create Credential Map and Key in Key Store:
1. Login to Weblogic EM. Go to WLS domain --> Security --> Credentials.
2. Click Create Map. Provide map name.
For Ex: kakarla_map
3. Select newly created map and click on Create Key. Provide Key name and credentials info.
For Ex: Key --> kakarla_key
4. Click on Ok to save changes.

Add jps-manifest.jar file path to BpelClassPath:
1. Login to Weblogic EM. Go to soa-infra --> SOA Administration --> BPEL Engine Properties --> More BPEL Configuration Properties.
2. Add jps-manifest.jar file path to BpelClassPath attribute. Typically jps-manifest.jar file will be located in $WLS_HOME/oracle_common/modules/oracle.jps_11.1.1/jps-manifest.jar
3. Click on apply to save changes

Update system-jazn-data.xml (In case OPSS store refers to file system):
1. Login to host machine and go to domains home.
2. Change to $DOMAIN_HOME/config/fmwconfig. Edit system-jazn-data.xml
3. Add following permission to <jazn-policy> grant section
Note: If you want to restrict the access to specific map & key, replace * with map/key name.
    <permission>
          <class>oracle.security.jps.service.credstore.CredentialAccessPermission</class>
          <name>context=SYSTEM,mapName=kakarla_map,keyName=*</name>
          <actions>read</actions>
   </permission>
4. Reboot weblogic admin & managed servers.

Add Java Embedded activity to BPEL process to retrieve CSF key:
1. Add jps-manifest.jar file from JDeveloper installation to SOA project.
2. Add following imports to BPEL component.
    <bpelx:exec import="oracle.security.jps.service.credstore.*"/>
    <bpelx:exec import="oracle.security.jps.*"/>
    <bpelx:exec import="java.security.PrivilegedAction"/>
    <bpelx:exec import="java.security.AccessController"/>
3. Add following code snippet to Java Embedded activity to access key store.
Note: CSF Map & Key (created in prev steps) hard coded here for example,
try  
{  
    String csfUserName = "";  
    String csfPassword = "";  
    JpsContext ctx = JpsContextFactory.getContextFactory().getContext();  
    final CredentialStore cs = (CredentialStore)ctx.getServiceInstance(CredentialStore.class);  
    CredentialMap cmap = cs.getCredentialMap("kakarla_map");  
    Credential cred = cmap.getCredential("kakarla_key");  
    if ((cred instanceof PasswordCredential)) {  
        PasswordCredential pcred = (PasswordCredential)cred;  
        char[] p = pcred.getPassword();  
        csfUserName = pcred.getName();  
        csfPassword = new String(p);  
        addAuditTrailEntry(csfUserName);  
        addAuditTrailEntry(csfPassword);          
   }  
} catch (Exception e) {  
   addAuditTrailEntry(e.getMessage());  
}
4. Use setVariableData and getVariableData methods in Java Embedded to set and get values in BPEL variables.
5. Deploy BPEL process to SOA server and test.
6. If you are getting any BPEL compilation errors, check BpelClassPath value set in previous steps.

Saturday, June 21, 2014

BEA-149500:An exception occurred while registering the MBean null

Last week, when I tried to deploy an ADF application to ADF managed server on WLS instance, I found below error in server logs. Of course, the application activated and worked without any other errors. But following error repeated every time when I tried deployment.

Error:

<Jun 17, 2014 9:32:01 AM CDT> <Error> <JMX> <BEA-149500> <An exception occurred while registering the MBean null.
java.lang.IllegalArgumentException: Registered more than one instance with the same objectName : com.bea:ServerRuntime=ADF_server,Name=HR,Type=ApplicationRuntime new:weblogic.j2ee.J2EEApplicationRuntimeMBeanImpl@1b017725 existing weblogic.j2ee.J2EEApplicationRuntimeMBeanImpl@1baabd61 at weblogic.management.jmx.ObjectNameManagerBase.registerObject(ObjectNameManagerBase.java:168)
 at weblogic.management.mbeanservers.internal.WLSObjectNameManager.lookupObjectName(WLSObjectNameManager.java:131)  at weblogic.management.jmx.modelmbean.WLSModelMBeanFactory.registerWLSModelMBean(WLSModelMBeanFactory.java:87) at weblogic.management.mbeanservers.internal.RuntimeMBeanAgent$1.registered(RuntimeMBeanAgent.java:105)         at weblogic.management.provider.core.RegistrationManagerBase.invokeRegistrationHandlers(RegistrationManagerBase.java:180)
     
Truncated. see log file for complete stacktrace>

Solution:

After checking the Weblogic configuration, I found that the data source name is same as the deployed application name causing the problem. To fix the error, I changed the application name and deployed it. No more errors.

To avoid this error in future, use unique names across Application,Data Source & JMS Queue etc..

Create tree menu structure and control page navigation in ADF

If we want to create tree menu structure, try following steps.

Steps:
1. Create TreeItem Object
2. Create Menu Bean class and add Menu nodes.
3. Add af:tree component to page and link to tree model.
4. Define navigation action

Tree Item Object
----------------------------------------------
import java.util.List;

public class TreeItem {
    private String text, action;
    private List<TreeItem> children;

    public TreeItem() {
        super();
    }

    public TreeItem(String text, String action) {
        super();
        this.text = text;
        this.action = action;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getAction() {
        return action;
    }

    public void setChildren(List<TreeItem> children) {
        this.children = children;
    }

    public List<TreeItem> getChildren() {
        return children;
    }
}


Menu Bean
--------------
import java.util.ArrayList;

import java.util.List;

import org.apache.myfaces.trinidad.model.ChildPropertyTreeModel;
import org.apache.myfaces.trinidad.model.TreeModel;

public class MenuBean {
    private Object instance = null;
    private transient TreeModel model = null;
    private ArrayList<TreeItem> root;

    public MenuBean() {
        root = new ArrayList<TreeItem>();
        TreeItem node1 = new TreeItem("Menu Header", "node1");
        root.add(node1);

        ArrayList<TreeItem> node1Children = new ArrayList<TreeItem>();
        TreeItem node1Child1 = new TreeItem("MenuChild1", "child1");
        node1Children.add(node1Child1);
        TreeItem node1Child2 = new TreeItem("MenuChild2", "child2");
        node1Children.add(node1Child2);
        TreeItem node1Child3 = new TreeItem("MenuChild2", "child3");
        node1Children.add(node1Child3);

        node1.setChildren(node1Children);

        setListInstance(root);
    }

    public TreeModel getModel() {
        if (model == null)
            model = new ChildPropertyTreeModel(instance, "children");
        return model;
    }

    public void setListInstance(List instance) {
        this.instance = instance;
        model = null;
    }
}


Page:
--------------
          <f:facet name="LeftFacet">
            <af:panelBox text="PanelBox1" id="pb1" showHeader="never"
                         showDisclosure="false">
              <af:tree value="#{menuBean.model}" var="node"
                       initiallyExpanded="true" rendered="#{sessionScope.isUserValid}">
                <f:facet name="nodeStamp">
                  <af:commandLink text="#{node.text}"
                                  actionListener="#{appBean.navNodeClicked}"
                                  id="ct1" partialSubmit="true"
                                  inlineStyle="font-weight:bold; font-size:9.0pt;"/>
                </f:facet>
              </af:tree>
            </af:panelBox>
          </f:facet>

Action:
-------------
    public void navNodeClicked(ActionEvent actionEvent) {
        // Add event code here...
        String linkId = ((RichCommandLink)actionEvent.getSource()).getText();

        AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
        Map<String, Object> pageFlowScope = adfFacesContext.getPageFlowScope();

        if (linkId.equals("MenuChild1")) {
            pageFlowScope.put("targetFlow", "child1");
        } else if (linkId.equals("MenuChild2")) {
            pageFlowScope.put("targetFlow", "child2");
        } else if (linkId.equals("MenuChild3")) {
            pageFlowScope.put("targetFlow", "child3");
        }
    }

Region in Page:
---------------
          <f:facet name="RightFacet">
            <af:group id="g1">
              <af:panelBox text="PanelBox1" id="pb2" showHeader="never"
                           showDisclosure="false">
                <af:region value="#{bindings.roottaskflow1.regionModel}"
                           binding="#{appBean.rightRegion}" id="r1"
                           partialTriggers="::pb1"
                           rendered="#{sessionScope.isUserValid}"/>
              </af:panelBox>
            </af:group>
          </f:facet>

Taskflow Parameter Definition in Page definition file:
--------------------------------------
  <executables>
    <variableIterator id="variables"/>
    <taskFlow id="roottaskflow1"
              taskFlowId="/WEB-INF/root-taskflow.xml#root-taskflow"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding"
              Refresh="ifNeeded">
      <parameters>
        <parameter id="targetFlow" value="#{pageFlowScope.targetFlow}"/>
      </parameters>
    </taskFlow>
  </executables>

Taskflow Definition:
---------------------------
<?xml version="1.0" encoding="UTF-8" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="root-taskflow">
    <default-activity id="__1">navigation</default-activity>
    <input-parameter-definition id="__10">
      <name id="__11">targetFlow</name>
      <value>#{pageFlowScope.targetFlow}</value>
      <class>java.lang.String</class>
    </input-parameter-definition>
    <router id="navigation">
     <case>
        <expression>#{pageFlowScope.targetFlow eq 'child1'}</expression>
        <outcome id="__12">child1</outcome>
      </case>
      <case>
        <expression>#{pageFlowScope.targetFlow eq 'child2'}</expression>
        <outcome id="__13">child2</outcome>
      </case>
      <case>
        <expression>#{pageFlowScope.targetFlow eq 'child3'}</expression>
        <outcome id="__17">child3</outcome>
      </case>    
      <default-outcome>child1</default-outcome>  
    </router>
    <task-flow-call id="child1-taskflow">
      <task-flow-reference>
        <document>/WEB-INF/child1-taskflow.xml</document>
        <id>child1-taskflow</id>
      </task-flow-reference>
    </task-flow-call>
    <task-flow-call id="child2-taskflow">
      <task-flow-reference>
        <document>/WEB-INF/child2-taskflow.xml</document>
        <id>child2-taskflow</id>
      </task-flow-reference>
    </task-flow-call>
    <task-flow-call id="child3-taskflow">
      <task-flow-reference>
        <document>/WEB-INF/child3-taskflow.xml</document>
        <id>child3-taskflow</id>
      </task-flow-reference>
    </task-flow-call>
    <control-flow-rule id="__2">
      <from-activity-id id="__3">navigation</from-activity-id>
      <control-flow-case id="__5">
        <from-outcome id="__6">child1</from-outcome>
        <to-activity-id id="__4">child1-taskflow</to-activity-id>
      </control-flow-case>
      <control-flow-case id="__7">
        <from-outcome id="__9">child2</from-outcome>
        <to-activity-id id="__8">child2-taskflow</to-activity-id>
      </control-flow-case>
      <control-flow-case id="__35">
        <from-outcome id="__33">child3</from-outcome>
        <to-activity-id id="__34">child3-taskflow</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>

Expand/Collapse Tree Structure

Following post will help you to expand/collapse Tree Structure programmatically.

Create a tree structure surrounded by panel collection and having two buttons in panel collection tool bar to perform expand/collapse operations.

          <af:panelCollection id="pc1" styleClass="AFStretchWidth">
            <f:facet name="toolbar">
              <af:toolbar id="t1">
                <af:commandToolbarButton text="Expand All" id="cbb2"
                                         actionListener="#{empBean.expandEmpTreeTable}"
                                         disabled="#{bindings.EmployeeVO1.estimatedRowCount eq 0}"/>
                <af:spacer width="10" height="10" id="s21"/>
                <af:commandToolbarButton text="Collapse All" id="cbb3"
                                         actionListener="#{empBean.collapseEmpTreeTable}"
                                         disabled="#{bindings.EmployeeVO1.estimatedRowCount eq 0}"/>
              </af:toolbar>
            </f:facet>
            <af:treeTable value="#{bindings.EmployeeVO1.treeModel}" var="node"
                          selectionListener="#{bindings.EmployeeVO1.treeModel.makeCurrent}"
                          rowSelection="single" id="tt1"
                          styleClass="AFStretchWidth"
                          binding="#{empBean.empTree}" columnStretching="last"
                          disableColumnReordering="true" expandAllEnabled="true"
                          rowBandingInterval="1" summary="Display Employee Info"
                          autoHeightRows="24" partialTriggers="::cbb2 ::cbb3"
                          initiallyExpanded="true"
                          horizontalGridVisible="false">
              <f:facet name="nodeStamp">
                <af:column id="c1" width="250" headerText="Employee Name"
                           rowHeader="true" inlineStyle="text-align:start;">
                  <af:outputText id="ot1" value="#{node.EmpName}"
                                 rendered="#{node.EmpName != null ? true : false}"/>
                </af:column>
              </f:facet>
              <af:column id="c3" width="180" headerText="Email"
                         inlineStyle="#{node.NewCount gt '0' ? 'color:#00ff00;': 'color:#003D5B;'}">
                <af:outputText id="ot3" value="#{node.Email}"
                               rendered="#{node.Email != null ? true : false}"/>
              </af:column>
              <af:column id="c7" width="180" headerText="Selected"
                         inlineStyle="text-align:center;">
                <af:group id="g1">
                  <af:selectBooleanCheckbox id="sb2" autoSubmit="true"
                                            value="#{node.empCheck}"
                                            rendered="#{node.empCheck != null ? true : false}"
                                            label=" "/>
                </af:group>
              </af:column>
              <af:clientListener method="expandNode" type="selection"/>
            </af:treeTable>
          </af:panelCollection>

Create binding to tree table in bean class and add following methods to perform selected operation.

    private RichTreeTable empTree;

    public void setEmpTree(RichTreeTable empTree) {
        this.empTree = empTree;
    }

    public RichTreeTable getEmpTree() {
        return empTree;
    }

    private void expandTreeChildrenNode(RichTreeTable rt,
                                        JUCtrlHierNodeBinding node,
                                        List<Key> parentRowKey) {
        ArrayList children = node.getChildren();
        List<Key> rowKey;
        if (children != null) {
            for (int i = 0; i < children.size(); i++) {
                rowKey = new ArrayList<Key>();
                rowKey.addAll(parentRowKey);
                rowKey.add(((JUCtrlHierNodeBinding)children.get(i)).getRowKey());
                disclosedTreeRowKeySet.add(rowKey);
                if (((JUCtrlHierNodeBinding)(children.get(i))).getChildren() ==
                    null)
                    continue;
                expandTreeChildrenNode(rt,
                                       (JUCtrlHierNodeBinding)(node.getChildren().get(i)),
                                       rowKey);
            }
        }
    }

    public void collapseEmpTreeTable(ActionEvent actionEvent) {
        empTree.getDisclosedRowKeys().clear();
    }

    public void expandEmpTreeTable(ActionEvent actionEvent) {
        // Add event code here...
        if (this.empTree != null) {
            disclosedTreeRowKeySet = new RowKeySetImpl();

            CollectionModel model = (CollectionModel)empTree.getValue();

            JUCtrlHierBinding treeBinding =
                (JUCtrlHierBinding)model.getWrappedData();
            JUCtrlHierNodeBinding rootNode = treeBinding.getRootNodeBinding();
            disclosedTreeRowKeySet = empTree.getDisclosedRowKeys();
            if (disclosedTreeRowKeySet == null) {
                disclosedTreeRowKeySet = new RowKeySetImpl();
            }
            List<JUCtrlHierNodeBinding> firstLevelChildren =
                rootNode.getChildren();
            for (JUCtrlHierNodeBinding node : firstLevelChildren) {
                ArrayList list = new ArrayList();
                list.add(node.getRowKey());
                disclosedTreeRowKeySet.add(list);
                expandTreeChildrenNode(empTree, node, list);
            }
            empTree.setDisclosedRowKeys(disclosedTreeRowKeySet);
        }
    }

Expand/Collapse tree with single click:
Following Java script will help you to expand/collapse tree with single click

      <af:resource type="javascript">
        function expandNode(evt) {
            var tree = evt.getSource();
            var rwKeySet = evt.getAddedSet();
            var firstRowKey;
            for (rowKey in rwKeySet) {
                firstRowKey = rowKey;
                break;
            }
            if (tree.isPathExpanded(firstRowKey)) {
                tree.setDisclosedRowKey(firstRowKey, false);
            }
            else {
                tree.setDisclosedRowKey(firstRowKey, true);
            }
        }
      </af:resource>

Add clientlistener to treetable to trigger java script and set type as selection

       <af:clientListener method="expandNode" type="selection"/>

Monday, May 26, 2014

Set & Send SOAP Headers in BPEL

Recently, I am supposed to call a external web service and it require few values in SOAP header to validate the request message. I did following changes in my BPEL process to set & send SOAP headers in request message.

Request Message Format:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header>
<ns2:UsernameToken xmlns:ns2="http://siebel.com">LK@XYZ.COM</ns2:UsernameToken>
<ns3:PasswordText xmlns:ns3="http://siebel.com">Test123</ns3:PasswordText>
   </soapenv:Header>
   <soapenv:Body>
      <..........................SOAP BODY..............>
   </soapenv:Body>
</soapenv:Envelope>

I followed below steps to set these headers in BPEL.

  • Created a XSD having required headers as elements in schema. For ex:

            <?xml version="1.0" encoding="UTF-8" ?>
           <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                               xmlns:ns2="http://siebel.com"
                               targetNamespace="http://siebel.com"
                               elementFormDefault="qualified">
              <xsd:element name="UsernameToken" type="xsd:string"/>
              <xsd:element name="PasswordText" type="xsd:string"/>
           </xsd:schema>

  • In BPEL process created a two variables based on above schema to hold header values.

           <variable name="UsernameToken" element="ns2:UsernameToken"/>
           <variable name="PasswordText" element="ns2:PasswordText"/>

  • Assigned values for these variables using assign activity.

         <assign name="AssignInput">
            <copy>
                <from expression="'LK@XYZ.COM'"/>
                <to variable="UsernameToken" query="/ns2:UsernameToken"/>
           </copy>
           <copy>
               <from expression="'Test123'"/>
               <to variable="PasswordText" query="/ns2:PasswordText"/>
             </copy>
          </assign>

  • Set these variables as Headers in Invoke activity. The Invoke activity looks as below, after adding headers.

          <invoke name="InvokeWS"
            inputVariable="WS_InputVariable"
            outputVariable="WS_OutputVariable" partnerLink="TestWebService"
            portType="ns1:AutoRequest" operation="process"
            bpelx:invokeAsDetail="no"
            bpelx:inputHeaderVariable="UsernameToken,PasswordText"/>


Saturday, May 24, 2014

JDeveloper 11.1.1.7.0 - View Object Possible Bug

Build JDEVADF_11.1.1.7.0_GENERIC_130226.1400.6493

Recently I tried to add a new transient attribute to existing view object based on entity object. While doing that, I figured out that, by default the view object setting attribute type as 'String' even though I selected type as Boolean. Of course, we can change the attribute type as required by editing the attribute. But, I am sharing this just for a note.

Select view object and click on New attribute to open new attribute window. Select type as Boolean and updatable property as Always. Click on Ok.

But the attribute type set as String

Select attribute and click on Edit sign to edit attribute. You will find updatable property also set as Never.

InvalidOperException: JBO-25221


I created a method (that has a typed list parameter for ex: List<ObjectType>) in Application Module class and exposed to view layer through client interface. Add exposed method from AM class using method Action binding in page definition file. And when I tried to execute this operation binding during run time, some times I got below error message in log files and some times not, but method did not get executed in either cases.

Created AM Method:
--------------------------
    public String insertEmpDetails(ArrayList<empObj> empDetailsList) {
       //business logic
   }
 
Operation Binding in Page definition file:
---------------------------------------------------
    <methodAction id="insertEmpDetails"
                  InstanceName="HRDBAppModuleDataControl.dataProvider"
                  DataControl="HRDBAppModuleDataControl"
                  RequiresUpdateModel="true" Action="invokeMethod"
                  MethodName="insertEmpDetails" IsViewObjectMethod="false"
                  ReturnName="data.HRDBAppModuleDataControl.methodResults.insertEmpDetails_HRDBAppModuleDataControl_dataProvider_insertEmpDetails_result">
      <NamedData NDName="empDetailsList"
                 NDType="java.util.ArrayList&lt;com.oracle.types.empObj>"/>
    </methodAction>

Warning in Log file:
------------------------
[2014-05-20T19:03:57.827+05:30] [AdminServer] [WARNING] [] [oracle.adf.controller.faces.lifecycle.Utils] [tid: [ACTIVE].ExecuteThread: '11' for queue: 'weblogic.kernel.Default (self-tuning)'] [userId: <anonymous>] [ecid: e9d0884f97c30d1a:-5af6bfa2:1461863bfa1:-8000-0000000000000562,0] [APP: DecomApp] [DSID: 0000KOPpMe_33FLaEPf9ES1JUjfG00000I] ADF: Adding the following JSF error message: Method HRDBAppModuleDataControl.dataProvider.insertEmpDetails() not supported[[
oracle.jbo.InvalidOperException: JBO-25221: Method HRDBAppModuleDataControl.dataProvider.insertEmpDetails() not supported
    at oracle.adf.model.binding.DCInvokeMethod.invokeMethod(DCInvokeMethod.java:581)
    at oracle.adf.model.binding.DCDataControl.invokeMethod(DCDataControl.java:2143)
    at oracle.adf.model.bc4j.DCJboDataControl.invokeMethod(DCJboDataControl.java:3118)
    at oracle.adf.model.binding.DCInvokeMethod.callMethod(DCInvokeMethod.java:261)
    at oracle.jbo.uicli.binding.JUCtrlActionBinding.doIt(JUCtrlActionBinding.java:1635)
    at oracle.adf.model.binding.DCDataControl.invokeOperation(DCDataControl.java:2150)
    at oracle.jbo.uicli.binding.JUCtrlActionBinding.invoke(JUCtrlActionBinding.java:740)
    at oracle.adf.controller.v2.lifecycle.PageLifecycleImpl.executeEvent(PageLifecycleImpl.java:407)
    at oracle.adfinternal.view.faces.model.binding.FacesCtrlActionBinding._execute(FacesCtrlActionBinding.java:252)
    at oracle.adfinternal.view.faces.model.binding.FacesCtrlActionBinding.execute(FacesCtrlActionBinding.java:210)
    at com.oracle.empBean.submitReq(empBean.java:889)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)


After multiple trails, I found problem in page definition and changed NDType as shown below. This time, it worked properly.

Updated Page Definition file:
------------------------------------
    <methodAction id="insertEmpDetails"
                  InstanceName="HRDBAppModuleDataControl.dataProvider"
                  DataControl="HRDBAppModuleDataControl"
                  RequiresUpdateModel="true" Action="invokeMethod"
                  MethodName="insertEmpDetails" IsViewObjectMethod="false"
                  ReturnName="data.HRDBAppModuleDataControl.methodResults.insertEmpDetails_HRDBAppModuleDataControl_dataProvider_insertEmpDetails_result">
      <NamedData NDName="empDetailsList"
                 NDType="java.util.ArrayList"/>
    </methodAction>

Not sure, what's causing the problem. I found this workaround to solve my problem.

Sunday, April 13, 2014

ADF - BAM Data Control

Recently, I tried to integrate BAM server data objects with ADF application using BAM data control. When I tried to deploy and test the application, I faced couple of issues as mentioned below.

1. Error in log file:
<Mar 10, 2014 3:54:54 PM IST> <Warning> <oracle.adf.controller.faces.lifecycle.Utils> <BEA-000000> <ADF: Adding the following JSF error message: Failed handling event FirstGetOnDataProvider on QueryConfigured
        at oracle.tip.tools.ide.bam.dc.rt.provider.paging.PagingCollection.iterator(PagingCollection.java:152)
        at oracle.adf.model.bean.DCDataVO.buildProviderIterator(DCDataVO.java:1319)
        at oracle.adf.model.bean.DCDataVO.access$100(DCDataVO.java:86)
        at oracle.adf.model.bean.DCDataVO$DCObjectAdapter.refreshIterator(DCDataVO.java:2923)
        at oracle.adf.model.bean.DCDataVO.executeQueryForCollection(DCDataVO.java:404)

        at oracle.tip.tools.ide.bam.dc.dt.adapter.BAMDataControlDataVO.executeQueryForCollection(BAMDataControlDataVO.java:83)

2. Not able to configure BAM server connection as ADF connection configuration page is not visible in EM console.

Solution:
Step 1: The application must be deployed using MDS. Enable MDS customization as shown below and deploy the application to WLS server. While deploying application, you may need to choose MDS repository


Step 2: Login to EM and select respective ADF application. From the Application deployment menu, select ADF –> ADF Connections. Create BAM server connection and test application.



Refer to following documentation to understand ADF & BAM intergration....Click here

af:query - CSS

By default, when we drag and drop view object instance as af:query declarative component on a page, the af:query renders view object attributes based on column length/precision as shown below.


In case, if we want to align the fields properly in view layer irrespective of model layer attribute values, CSS will help us to do that.
  •  Create a CSS file and declare it in trinidad-skins.xml
<?xml version="1.0" encoding="UTF-8" ?>
<skins xmlns="http://myfaces.apache.org/trinidad/skin">
  <skin>
    <id>myskin.desktop</id>
    <family>customskin</family>
    <extends>fusionFx-v1.desktop</extends>
    <style-sheet-name>css/employees.css</style-sheet-name>
  </skin>
</skins>
  • Open trinidad-config.xml file and override existing values with custom skin created in above step
<?xml version="1.0" encoding="UTF-8"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
    <skin-family>customskin</skin-family>  
</trinidad-config>
  • Open CSS file and add following values based on number of attributes defined in View object
.CustomQueryPanel tr[_rowkey='0'] input{
width: 320px !important;
}
.CustomQueryPanel tr[_rowkey='1'] input{
width: 320px !important;
}
.CustomQueryPanel tr[_rowkey='2'] input{
width: 320px !important;
}
.................................
.................................
.CustomQueryPanel tr[_rowkey='9'] input{
width: 320px !important;
}
  • Open JSP page and add the CSS class name mentioned in CSS file to styleClass property of af:query and run page
      <af:query id="qryId1" headerText="Search" disclosed="true"
            value="#{bindings.ImplicitViewCriteriaQuery.queryDescriptor}"
            model="#{bindings.ImplicitViewCriteriaQuery.queryModel}"
            queryListener="#{bindings.ImplicitViewCriteriaQuery.processQuery}"
            queryOperationListener="#{bindings.ImplicitViewCriteriaQuery.processQueryOperation}"
       resultComponentId="::pc3:resId1" rows="5" maxColumns="2" styleClass="CustomQueryPanel"/>

Result:

In case, if we want to restrict a view object attribute to not to display in query able attributes list, we have two options.

Option 1.  Create a custom view criteria and use it as a query component in a page
Option 2.  Go to view object declaration and make attribute property 'Queryable' as false.




Wednesday, April 9, 2014

Introduction to OPAM

Oracle Privileged Account Manager (OPAM) is a secure password management solution designed to generate, provision, and manage access to passwords for privileged accounts like Linux/Unix "root" or Oracle database "SYS" accounts. It enables auditing and establishes accountability for users who normally share privileged account credentials, and additional user Session Management and Recording.

Important Features:
  • Rich set of target connectors for Information Technology (IT) resources such as operating systems (e.g., UNIX), database servers (e.g., Oracle Database, Microsoft SQL Server, or IBM DB2), user directories (e.g.,Oracle Directory Services or Microsoft Windows Active Directory Services), network devices (routers, load balancers, firewalls), and enterprise resource planning applications (e.g., human capital management). Privileged users include system, database, and network administrators, support personnel (e.g., help desk), as well as application owners.
  • Leverage Oracle Database Vault and Transparent Data Encryption (TDE) for additional security.
  • Metadata information and passwords managed by OPAM are encrypted and persisted in an Oracle Database.
Sample Screenshots:
Add Target:


Add Accounts


Grant Account Access to Users/Groups


CheckOut History

Password Policies


Usage Policies


Password CheckOut



Password CheckIn



Tuesday, February 11, 2014

SOA Suite - Disable B2B

Log on the Enterprise Manager and navigate to soa-infra.  Right click and open the SOA Infrastructure menu, then SOA Administration, then B2B Server Properties.



Click on the More B2B Configuration Properties link.

Go to the Operations tab. Click on the addProperty operation to define a new property.

In the key field, enter b2b.donot_initialize.  In the value field, enter true. Click on the Invoke button to add the property.

Restart server.

Also disable the DBMS job that refreshes the B2B materialized view by executing following command in database.

alter materialized view dev_soainfra.b2b_system_mv refresh on demand;