Thursday, October 2, 2014

OAF - Personalize Differently!

Was working on Oracle EBS OAF personalization, and wow it is one hack of a framework. For a long time, I thought APEX and ADF had to the bottom of intelligence pyramid, but OAF broke that by million points!

There are tons of restriction on what you can do and not to do with OAF personalization framework, and yeah you can get access to controller and do a lot more as you get access to pageContext and webBean, but there are still tons of limitation. E.g. adding table column with multiple icons in it (not advance table) or adding switcher column on existing table with icons, etc.. Here is another approach which might work better. Integrate it with JQuery and then use JQuery's power to do anything you like on the page. As we all know, with JQuery - sky is the limit!

Here is how we did it :

Step 1 : Upload JQuery to EBS

  • Copy JQuery related files to EBS server ($OA_MEDIA/xx_demo/images):
    • Copy all images which comes with JQuery to
  • Copy all JS files to EBS server ($OA_HTML/xx_demo/js):
    • jquery-1.11.0.min.js
    • jquery-ui-1.10.3.custom.min.js
  • Copy all CSS files to EBS server $OA_HTML/xx_demo/css
    • jquery.ui.all.css (alternatively you can all copy jquery-ui.min.css)
  •  Update CSS files for the image reference. 
    • Search/Replace images/ -> /OA_MEDIA/xx_demo/images/
  •  Create a custom JS file
    • This file will contain all your customization needs to be done.
    • I created xx_demo_oaf_customization.js
    • Copied this file to $OA_HTML/xx_demo/js
    • This file will have function called xx_oaf_customize which will be called when page is fully loaded

Step 2 : Load JQuery on EBS page

This is sensitive piece. There are multiple way to do so, and each one has it's plus/minus:

Option 1 : Modify OAPageBean class
  • Decompile $JAVA_TOP/oracle/apps/fnd/framework/webui/OAPageBean.class
  • Under addJavascriptForNewFeatures function, you can add JQuery libraries as below:
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-1.11.0.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-ui-1.10.3.custom.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/xx_demo_oaf_customization.js");            
  • Recompile OAPageBean.java and put it back, in this case the JQuery will be loaded on all OAF page in system
  • This is probably not very good idea


Option 2 : Customize/Extend the controller

  • Write a controller and extend it. Controller will provide access to webBean and pageContext
  • From page context, we can add JS as below 
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-1.11.0.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-ui-1.10.3.custom.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/xx_demo_oaf_customization.js");            
  •  We can attach this controller to a specific page and then JQuery will be loaded on that page

Option 3 : Customize oafcoreR121.js
  • Any OAF page includes $OA_HTML/cabo/oajsLibs/oafcoreR121.js, so I added a few lines of code in oafcoreR121.js to load 
    • JQuery related javascript
    • CSS files and 
    • then call xx_oaf_customize function (in xx_demo_oaf_customization.js file) when page is completely done with loading. 
  • Code I put inside oafcoreR121.js is as below or you can download here:
      /* XX OAF Customization to load JQuery : START */  
      /* generic function load script */  
      /* note: we could certainly use below, but jquery is not yet loaded. */  
      /* loadScript(jsURL, callback); */  
      function xx_loadScript(url, callback) {  
           var head = document.getElementsByTagName('head')[0];  
           var script = document.createElement('script');  
           script.type = 'text/javascript';  
           script.src = url;  
           script.onreadystatechange = callback;  
           script.onload = callback;  
           head.appendChild(script);  
      }  
      /* XX load jquery core js */  
      xx_loadScript("/OA_HTML/xx_demo/js/jquery-1.11.0.min.js", xx_jquery_js_loaded);  
      /* XX load jquery UI js */  
      function xx_jquery_js_loaded() {  
           xx_loadScript("/OA_HTML/xx_demo/js/jquery-ui-1.10.3.custom.min.js", xx_jquery_ui_js_loaded);  
      }  
      /* XX load oaf customization js */  
      function xx_jquery_ui_js_loaded() {  
           xx_loadScript("/OA_HTML/xx_demo/js/xx_demo_oaf_customization.js", xx_jquery_xx_demo_oaf_customization_js_loaded);  
      }  
      /* XX load css, and call entry point */  
      function xx_jquery_xx_demo_oaf_customization_js_loaded() {  
           $("<link/>", { rel: "stylesheet", type: "text/css", href: "/OA_HTML/xx_demo/css/jquery.ui.all.css"}).appendTo("head");  
           $(document).ready(function() {  
                xx_oaf_customize();  
           });  
      }  
      /* XX OAF Customization to load JQuery : END */  


Step 3 : Personalization Begins!
Now you can do anything you would normally do as JQuery guru. For small example, I had a table below in EBS, and I just wanted to add column with multiple rows.



This is how I did inside xx_demo_oaf_customization.js file: - download here.

 function xx_oaf_customize() {  
      if( $("[id$=DefaultFormName]") && $("[id$=DefaultFormName]").attr('action') ) {  
           if( $("[id$=DefaultFormName]").attr('action').indexOf("IcxPorRcvHomePG") > -1 ) {  
                // iProcurment.Receiving Page Found  
                console.log(' Receipt Page Found ' );  
                if( $("[id$=ReceiptsTableRN]") ) {  
                     // Table Region Found  
                     console.log(' ReceiptsTableRN region found ' );  
                     if( $("[id$=ReceiptsTableRN]").find("table.x1h") ) {  
                          console.log(' Table found ' );  
                          $("[id$=ReceiptsTableRN]").find("table.x1h").find("th:last").after("<th class='x1t x4m' scope='col'> <span id='XX_OASH__643' class='x24'>Demo Actions</span></th>");  
                          $("[id$=ReceiptsTableRN]").find("table.x1h").find("tr").each( function() {  
                               if( $(this).find('td') ) {  
                                    $(this).find('td:last').after("<td class='x1p x50'> <a id='XX1:ViewDetailImage:0' href='www.yahoo.com' onclick='' name='XX1:ViewDetailImage:0'> <img border='0' align='middle' title='View Details' src='/OA_MEDIA/eyeglasses_24x24_transparent.gif'> </a> <a id='XX2:ViewDetailImage:0' href='www.google.com' onclick='' name='XX2:ViewDetailImage:0'> <img border='0' align='middle' title='View Details' src='/OA_MEDIA/xx_demo/images/details_close.png'> </a> <a id='XX3:ViewDetailImage:0' href='www.cnn.com' onclick='' name='XX3:ViewDetailImage:0'> <img border='0' align='middle' title='View Details' src='/OA_MEDIA/xx_demo/images/details_open.png'> </a></td>");  
                               }  
                          });  
                     }  
                }  
           }  
      }  
 }  


Now OAF page has the column I need:



Wednesday, September 10, 2014

Restarting Weblogic when DataSource is down

We observed that if one/more of the data source is down (if database is down or expired password), when we restart the managed server it doesn't start and remains in Admin state. The reason behind that is the Initial and Minimum capacity specified in Data Source. E.g. for majority of the cases, when we create data-source, following values are set to 1 by default, which was causing problem.



Changing them to 0, resolves the restart issue. There are still tons of constant error in logs related to the data source which is down, but at least server comes up fine.


Tuesday, April 22, 2014

Change hostname in weblogic datasource / connection pool without restarting entire weblogic

A while ago, I posted to change password without restart weblogic, we saw similar issue when we had to change the hostname of the datasource. Changing the hostname in datasource didn't show as effective right away, however after following below steps, it worked like a charm:

  • Change the hostname in connection pool on WebLogic Console for a specific datasource 
  • Untarget the datasource from all servers
 
  • Retarget the datasource to all servers where it was configured before
  • Shutdown Datasource
  • Start Datasource

Tuesday, April 1, 2014

Shutdown composite

Just a sample code to shutdown your composite in your Java Embed activity:

try {       
   oracle.soa.management.facade.Locator locator = oracle.soa.management.facade.LocatorFactory.createLocator();         
   oracle.soa.management.util.CompositeFilter compositeFilter = new oracle.soa.management.util.CompositeFilter();         
   compositeFilter.setCompositeDN("default/CompositeShutdown!1.0");         
   java.util.List<oracle.soa.management.facade.Composite> composites = locator.getComposites(compositeFilter);         
   for(oracle.soa.management.facade.Composite composite: composites) {         
      composite.stop();         
   }       
} catch (Exception e) {         
   e.printStackTrace();        
}

Thursday, February 13, 2014

Force local optimization in cluster

It is done by default in single node installation, but some of configuration needs to be done specially for cluster.


  •  (optional) : Make sure composite.xml have oracle.webservices.local.optimization for this as true (which is default), or doesn't have this property at all so true would be default value.



  •  Configure serverURL as Load Balancer URL





  • Configure Frontend HTTP Host and Frontend HTTP Port as Load Balancer Server and Port.




  • Make sure in EM console, the invocation shows as "local" as below