Friday, December 4, 2015

BPM: Error While Deploying Business Process with Timer Event

Applicable to FMW 11.1.1.7.0:

Recently I changed JVM to use JDK 1.7 instead of JRockit in one of SOA 11g domains and faced below class not found error when I tried to redeploy a BPM process having Timer event.

Error:
<Dec 1, 2015 2:40:31 AM CST> <Error> <oracle.soa.bpel.engine> <BEA-000000> <post deploy failed for component TestSchedulerProcess
javax.ejb.EJBException: EJB Exception: : java.lang.NoClassDefFoundError: oracle/bpm/runtime/quartz/trigger/TriggerWithReschedule
        at oracle.bpm.bpmn.engine.model.runtime.util.SchedulingHelper.schedule(SchedulingHelper.java:46)
        at oracle.bpm.bpmn.engine.model.TimerStartEventScheduler.schedule(TimerStartEventScheduler.java:172)
        at oracle.bpm.bpmn.engine.ejb.impl.BPMNServerManagerBean.scheduleStartTimerEvents(BPMNServerManagerBean.java:261)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Caused by: java.lang.ClassNotFoundException: oracle.bpm.runtime.quartz.trigger.TriggerWithReschedule
        at weblogic.utils.classloaders.GenericClassLoader.findLocalClass(GenericClassLoader.java:297)
        at weblogic.utils.classloaders.GenericClassLoader.findClass(GenericClassLoader.java:270)

Not sure why WLS failed to load BPM runtime classes. I was able to resolve this issue by adding BPM Runtime JAR's path to CLASSPATH in setDomainEnv.sh file as mentioned below.

Fix: setDomainEnv.sh
PRE_CLASSPATH="/<ORALCE_HOME>/soa_111/soa/modules/oracle.bpm.runtime.classloader-util.jar:${PRE_CLASSPATH}"
export PRE_CLASSPATH

Sunday, November 29, 2015

WLS: Change JVM (JDK/JRockit) - Linux

Following post will help you to upgrade/change the JDK used by an Oracle WebLogic Server to a newer version.
  • Upgrade JDK:
  1. Make sure new JDK version is certified with Weblogic version and other products installed in the same domain.
  2. Install New JDK.
  3. Shutdown Weblogic domain
  4. Modify all occurrences of the original JDK directory path value with the new JDK directory path value for the files listed below.

  5. Note 1: Take backup of below mentioned files before change.
    Note 2: Following command will help you find list of files referring to JAVA_HOME
    find . -type f -name "*.sh" -exec grep -il JAVA_HOME {} \;

    11g:
    ----
    • MW_HOME/wlserver_10.3/common/bin/commEnv.sh
    • MW_HOME/wlserver_10.3/common/nodemanager/nodemanager.properties
    • MW_HOME/utils/bsu/bsu.sh
    • MW_HOME/utils/quickstart/quickstart.sh
    • MW_HOME/utils/uninstall/uninstall.sh
    • MW_HOME/wlserver_10.3/.product.properties
    • DOMAIN_HOME/user_projects/domains/<mydomain>/bin/setDomainEnv.sh

    12c:
    ----
    • MW_HOME/install/envVars.properties
    • MW_HOME/oracle_common/common/bin/commEnv.sh
    • MW_HOME/oracle_common/common/bin/setHomeDirs.sh
    • MW_HOME/oui/bin/compareInventory.sh
    • MW_HOME/oui/bin/install.sh
    • MW_HOME/oui/bin/viewInventory.sh
    • MW_HOME/wlserver/.product.properties
    • DOMAIN_HOME/bin/setDomainEnv.sh
    • DOMAIN_HOME/nodemanager/nodemanager.properties

  6. Clear cache and tmp folders resides in each server folder.

  7. Note: Incase Weblogic version is 10.3.6 and the targetJDK version is JAVA 7 (version 1.7.0_x),following jar files needs to be copied manually from $MW_HOME/modules to the directory $JAVA_HOME/jre/lib/endorsed

    • javax.annotation_1.0.0.0_1-0.jar
    • javax.xml.bind_2.1.1.jar
    • javax.xml.ws_2.1.1.jar

  8. Startup Weblogic domain

  9. Simple way: You can install new JDK and rename new JDK folder name to old JDK name to skip JAVA_HOME path update in all the files mentioned in step 4.
  • Change JRockit to JDK and Vice-versa:
    Refer below steps along with above mentioned JDK upgrade steps.
  1. Refer below 2 steps and update following two files as needed:
    • setDomainEnv.sh
    • commEnv.sh
  2. If you are changing from JRockit  to JDK:
  3. JAVA_HOME="/usr/bin/jdk1.6.0_31"
    JAVA_VENDOR="Sun"
    export JAVA_HOME
    export JAVA_VENDOR
  4. If you are changing from JDK  to JRockit:
  5. JAVA_HOME="/usr/bin/jrockit_28.1"
    JAVA_VENDOR="Oracle"
    export JAVA_HOME
    export JAVA_VENDOR

Refer to Oracle Security Alerts page to get latest patch updates.

Thursday, November 12, 2015

WLS: Configure Watch and Notification

Following post will help you to setup monitoring about the state of your system and send notification to selected recipients.

To set up your notification alerts you will need to cover these main steps:

1. Configure New Mail Session
2. Configure New Diagnostic Module
3. Configure New Collected Metrics
4. Configure New Notification
5. Configure New Watch rule

In the below example, we will set up alerts on the state of the Weblogic Managed Server to get an alert when a server is in the RUNNING state.

1. Configure Mail Session:
    Note: Mail sessions facilitate the process of using the JavaMail APIs, which provide applications and other J2EE modules with access to Internet Message Access Protocol (IMAP)- and Simple Mail Transfer Protocol (SMTP)-capable mail servers on your network or the Internet.

  • Go to Services -> Mail Sessions and click "New" to create a new Mail Session
  • Provide Name, JNDI Name and JavaMail Properties as follow:

Name – WLSMailAlert
JNDI Name – WLSMailAlert

JavaMail Properties:
mail.debug="true"
mail.transport.protocol=smtp
mail.smtp.user=admin
mail.smpt.password=password
mail.smtp.port=25
mail.smtp.from="admin@abc.com"
mail.smtp.host=smtp.abc.com
  • Click Next and Target the mail session to a server(s) and click finish. This is needed to expose the mail session on the servers JNDI tree.


2. Configure New Diagnostic Module
    Note: Most of the times Weblogic will have Diagnostic Module (Module-FMWDFW) configured and targeted to all servers. Below steps will help you if its not exists.
  • Go to Diagnostics -> Diagnostics Modules and click "New" to create a new diagnostics module.
  • Provide a Name and Description.

          Name – WLDMServerState
          Description – WLDF module to monitor server state

  • Select the newly created module and select "Targets" tab. Select required server(s) as Target and Save.



3. Configure New Collected Metrics
    Note: This metric is collected at a configurable interval, by default 300000ms i.e 300 sec(5 mins).  It means recipients will be notified for every 5 mins.
  • Select the newly created module "WLDMServerState" and select "Collected Metrics" tab. Click New.
  • Select "ServerRuntime" and click "Next"
  • Select "weblogic.management.runtime.ServerRuntimeMBean" from the drop down and click "Next"
  • From the list of collected attributes select "State" and shuffle it to the "Chosen" category. Click "Next"
  • From the instances list, select the required servers and shuffle to the "Chosen" category. Click "Finish"



4. Configure New Notification

  • Select the newly created module "WLDMServerState" and go to "Watches and Notifications" tab  --> "Notification" sub-tab
  • Click "New" to create a new Notification
  • Select "SMTP (E-Mail)"for Type and click "Next"
  • Provide a name – WLSServerStateAlert and Select "Enable Notification" check box. Click "Next"
  • Configure the following properties for the "SMTP Properties" page

           Mail Session Name – select "WLSMailAlert" (Created in step 1)
           E-Mail Recipients – admin@abc.com

  • Click "Finish"



5. Configure New Watch rule

  • Select the newly created module "WLDMServerState" and go to "Watches and Notifications" tab  --> "Watches" sub-tab
  • Click "New" to create a new Watch. Provide the name and select the type as "Collected Metrics". Also make sure the watch is enabled.

           Name – ServerStateWatch

  • Click "Next". Click "Add Expressions"
  • Select "ServerRuntime" and click “Next”
  • Select "weblogic.management.runtime.ServerRuntimeMBean" from the dropdown and click "Next"
  • Select the instance for the appropriate server from the list for "Instance" dropdown and click "Next"
  • Select "State" from "Message Attribute", Operator as "=", and Value as "RUNNING"
  • Click "Finish" to go back to "Create Watch" page
  • Rule value will be similar to (${ServerRuntime//[weblogic.management.runtime.ServerRuntimeMBean]com.bea:Name=adf_server1,Type=ServerRuntime//State} = 'RUNNING')
  • Click "Next" in "Create Watch" page. Select "Use an automatic reset alarm" and click "Next". (Set the "Automatic reset period" reduce mail alerts)
  • From the list of available notifications select "WLSMailAlert" and shuffle it to the "Chosen" category. Click "Finish"



Note: This Watch will be triggered every time the server state is RUNNING. And notification will be sent based on the Collected Metric’s sampling period i.e. 5 mins.

Note: Check Domain log file to find alerts related to configured watch.
Sample Domain Log:
####<Nov 11, 2015 11:03:16 PM CST> <Notice> <Diagnostics> <hostxxx.abc.com> <adf_server1> <[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <f50689c08d6d3474:5aefc86b:150f9d35d8e:-8000-0000000000000800> <1447304596087> <BEA-320068> <Watch 'ServerStateWatch' with severity 'Notice' on server 'adf_server1' has triggered at Nov 11, 2015 11:03:16 PM CST. Notification details:
WatchRuleType: Harvester
WatchRule: (${ServerRuntime//[weblogic.management.runtime.ServerRuntimeMBean]com.bea:Name=adf_server1,Type=ServerRuntime//State} = 'RUNNING')
WatchData: com.bea:Name=adf_server1,Type=ServerRuntime//State = RUNNING
WatchAlarmType: AutomaticReset
WatchAlarmResetPeriod: 60000

Sample Notification:



Sunday, July 19, 2015

DB: ORA-02064: distributed operation not supported

Recently I faced an error when I tried to execute a remote procedure having transaction control statements(Commit/Rollback) over DB link. 

Error: ORA-02064: distributed operation not supported

Reference note for this error code:
1.One of the following unsupported operations was attempted:1. array execute of a remote update with a subquery that references a dblink, or
2. an update of a long column with bind variable and an update of a second column with a subquery that both references a dblink and a bind variable, or
3. a commit is issued in a coordinated session from an RPC procedure call with OUT parameters or function call


In my case, the reason was the remote procedure was performing insert and commit.

Solution:
Modified the remote procedure to create Autonomous Transaction using PRAGMA AUTONOMOUS_TRANSACTION syntax as shown below.

CREATE OR REPLACE PACKAGE test_pkg AS
    PROCEDURE test_proc (p_param OUT VARCHAR2 );
END test_pkg;

CREATE OR REPLACE PACKAGE BODY test_pkg AS
    PROCEDURE test_proc (p_param OUT VARCHAR2) IS
    l_var NUMBER;
    PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
    ...
    --BUSINESS LOGIC
    ...
    EXCEPTION
       WHEN OTHERS THEN
      ..
   END test_proc;
END test_pkg;

http://docs.oracle.com/database/121/LNPLS/autotransaction_pragma.htm

The term "automous transaction" refers to the ability of PL/SQL temporarily suspend the current transaction and begin another, fully independent transaction (which will not be rolled-back if the outer code aborts).  The second transaction is known as an autonomous transaction. The autonomous transaction functions independently from the parent code.

An autonomous transaction has the following characteristics:

> The child code runs independently of its parent
> The child code can commit or rollback & parent resumes
> The parent code can continue without affecting child work

Friday, July 10, 2015

WLS: non-XA JDBC Datasource

Recently I faced below error, when tried to test SOA composite, due to incorrect configuration of the data source.

Error:
Unable to roll back JCA LocalTransaction due to:
Internal Exception: java.sql.SQLException: Cannot call Connection.rollback in distributed transaction.  Transaction Manager will commit the resource manager when the distributed transaction is committed.

Fix: 
Unchecked 'Supports Global Transactions' option in the data source configuration. This is applicable in case  Non-XA driver used to configure data source.


ADF: Date & Time

Provided different examples to get date & time in ADF.

Groovy:
Current Database Date: DBTransaction.currentDbTime
Current Date: adf.currentDate
Current DateTime: adf.currentDateTime
Add Dates to Current Date: adf.currentDateTime.plus(10)


Java:
Find AM or PM: 
Calendar.getInstance().get(Calendar.AM_PM)
returns 0 for AM & 1 for PM

Get Today's date:
Date d = new Date(Date.getCurrentDate())

Get todays Timestamp:    
Timestamp myTimestamp = new Timestamp((new java.util.Date()).getTime());

Get the database time and set VO attirbute:
vo.setAttribute("OrderDate", ((DBTransactionImpl)getDBTransaction()).getCurrentDbTime());

Get current database time in the middle tier:
oracle.jbo.server.DBTransactionImpl.getCurrentDbTime()

Get current time as java.sql.Date:
new java.sql.Date(((DBTransactionImpl)(this.getDBTransaction())).getCurrentDbTime().getTime());

Get Date value in Page/Fragments:
Add following values to adfc-config.xml or taskflow.xml:

<managed-bean>    
<managed-bean-name>currentDate</managed-bean-name>    
<managed-bean-class>java.util.Date</managed-bean-class>    
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>  

And refer as below
<af:outputText value="#{currentDate}">    
<f:convertDateTime pattern="MM/dd/yyyy" type="date"/>
</af:outputText> 

Friday, May 29, 2015

OHS: Find Version

If you have registered Oracle HTTP Server (OHS) with Domain, you can easily find the version through EM console.

Following steps will help you, if you want to find it through CLI.

Steps:

1. Check ORACLE_HOME and LD_LIBRARY_PATH environment variables.
    echo $ORACLE_HOME
    echo $LD_LIBRARY_PATH
2. If environment variables not set, set as below
    export ORACLE_HOME=<Directory where OHS is installed> 
    Ex:  export ORACLE_HOME=<FMW_HOME>/ohs_111

For 10g & 11g: Includes OPMN lib directory
    export  LD_LIBRARY_PATH=$ORACLE_HOME/ohs/lib:$ORACLE_HOME/opmn/lib:$ORACLE_HOME/lib:$LD_LIBRARY_PATH

For 12c:
    export LD_LIBRARY_PATH=$ORACLE_HOME/ohs/lib:$ORACLE_HOME/lib:$LD_LIBRARY_PATH

3. Go to ORACLE_HOME/ohs/bin directory
    cd $ORACLE_HOME/ohs/bin
    Ex: cd <FMW_HOME>/ohs_111/ohs/bin

4. Execute below command to get OHS version
    ./httpd.worker -version

Sample Output:
10g:
Server version: Oracle-Application-Server-10g/10.1.3.1.0 Oracle-HTTP-Server
Server built:   Sep 18 2006 16:11:17

11g:
Server version: Oracle-HTTP-Server/2.2.21 (Unix)
Server built:   Nov 19 2011 01:19:35
Server label:   APACHE_11.1.1.6.0_LINUX.X64_111109.2001

12c:
Server version: Oracle-HTTP-Server/2.2.22 (Unix)
Server built:   May 13 2014 08:22:41
Server label:   APACHE_12.1.3.0.0_LINUX.X64_140513.0701

Errors:

If you receive below errors, LD_LIBRARY_PATH variable not set properly.
  • ./httpd.worker: error while loading shared libraries: libaprutil-1.so.0: cannot open shared object file: No such file or directory
  • ./httpd.worker: error while loading shared libraries: libiau.so: cannot open shared object file: No such file or directory

Patch List (11g)

1. Set ORACLE_HOME
    export ORACLE_HOME=<Directory where OHS is installed> 
    Ex:  export ORACLE_HOME=<FMW_HOME>/ohs_111

2. Go to OPatch directory
    cd $ORACLE_HOME/OPatch

3. Execute below OPatch command
    ./opatch lsinventory -detail

Thursday, April 30, 2015

ADF: JMX Data Control

Applicable to ADF 11.1.1.7.0 and Weblogic 10.3.6

Recently I had an requirement to display list of OID groups associated with user in OID(LDAP) in an ADF application. And I used JMX(Java Management Extensions) data control to perform query operations on OID configured in Weblogic server. Here is the steps I followed to develop it.

1. Create an ADF application and create JMX connection to weblogic server as shown below.
    URL Provider Path: /jndi/weblogic.management.mbeanservers.domainruntime


2. Create JMX Data Control as shown below.
   

Note: Select OID Provider under AdminServer. Don't select OID provider displaying directly under Security option.

3. Data Control will be displayed as shown below

    DataControl.dcx content:
    <?xml version="1.0" encoding="UTF-8" ?>
    <DataControlConfigs xmlns="http://xmlns.oracle.com/adfm/configuration"
                        version="11.1.1.64.93" id="DataControls"
                        Package="com.lkakarla.view">
    <AdapterDataControl id="UserAdminDC"
                        FactoryClass="oracle.adf.model.adapter.DataControlFactoryImpl"
                        ImplDef="oracle.adfinternal.model.adapter.jmx.JmxDataControlDef"
                        SupportsTransactions="true" SupportsSortCollection="false"
                        SupportsResetState="false" SupportsRangesize="true"
                        SupportsFindMode="true" SupportsUpdates="true"
                        Definition="JmxDataControl" BeanClass="JmxDataControl"
                        xmlns="http://xmlns.oracle.com/adfm/datacontrol">
      <Source>
        <JMXDCProperties xmlns="http://xmlns.oracle.com/adfm/adapter/jmx">
          <MBeanServerConnection connectionName="UserAdmin"/>
           <JMXBean displayName="myrealmOID"
                    objectName="Security:Name=myrealmOID,Location=AdminServer">
            <UpdatableOperations>
              <Operation name="listGroupMembers"/>
              <Operation name="listMemberGroups"/>
            </UpdatableOperations>
          </JMXBean>
        </JMXDCProperties>
      </Source>
     </AdapterDataControl>
   </DataControlConfigs>


4. Create a Page/Fragment and drag & drop method 'listMemberGroups' from Data Control to Page/Fragment. And Add following other methods as method bindings to Page definition. After adding the page definition will be dsipalyed as shown below.

   haveCurrent
   getCurrentName
   advance



5. Sample Page Code:

        <af:resource type="javascript">
          function onCb1EnterKey(event) {
              if (event.getKeyCode() == AdfKeyStroke.ENTER_KEY) {
                  var itField = event.getSource();
                  var btn1 = itField.findComponent('cb1');
                  var partialSubmit = true;
                  AdfActionEvent.queue(btn1, partialSubmit);
                  event.cancel();
              }
          }

        </af:resource>

      <af:showDetailItem text="Search User Roles" id="sdi546"
                       stretchChildren="first" styleClass="AFStretchWidth"
      <af:panelGroupLayout id="pgl2" styleClass="AFStretchWidth">
        <af:spacer width="10" height="10" id="s305"/>
        <af:panelBox text="Search User" id="pb1" styleClass="AFStretchWidth">
          <af:panelFormLayout id="pflu1" styleClass="AFStretchWidth">
            <af:panelLabelAndMessage label="Username" id="plam3" for="iuser1">
              <af:panelGroupLayout id="td1" layout="horizontal">
                <af:inputText id="iuser1"
                              binding="#{pageFlowScope.userBean.userName}"
                              placeholder="Enter User Email ID to Search"
                              autoSubmit="true">
                  <af:clientListener method="onCb1EnterKey" type="keyUp"/>
                </af:inputText>
                <af:spacer width="10" height="10" id="s10"/>
                <af:commandButton id="cb1"
                                  actionListener="#{pageFlowScope.userBean.searchGroups}"
                                  blocking="true" icon="/images/ico_search.png"
                                  text=" " partialSubmit="true"
                                  shortDesc="Click to Search Groups"/>
              </af:panelGroupLayout>
            </af:panelLabelAndMessage>
          </af:panelFormLayout>
        </af:panelBox>
        <af:spacer width="10" height="10" id="s1"/>
        <af:panelBox text="User Groups" id="pb14" styleClass="AFStretchWidth">
          <af:table value="#{pageFlowScope.userBean.userGrpList}" var="row"
                    rowBandingInterval="1" id="t2" partialTriggers="::cb1"
                    columnStretching="column:c1">
            <af:column sortable="false" headerText="Group Name" id="c1"
                       width="200">
              <af:outputText value="#{row.groupName}" id="ot7"/>
            </af:column>
          </af:table>
        </af:panelBox>
      </af:panelGroupLayout>
 
 
6. Sample Bean Code:

    import java.util.ArrayList;
    import java.util.List;
    import javax.faces.event.ActionEvent;
    import oracle.adf.model.BindingContext;
    import oracle.adf.view.rich.component.rich.input.RichInputText;
    import oracle.binding.BindingContainer;
    import oracle.binding.OperationBinding;

    private RichInputText userName;
    private String userGroup;
    private List<UserGroups> userGrpList;

    public void searchGroups(ActionEvent actionEvent) {

        Boolean haveCurrent = false;
        if (null != userGrpList) {
            userGrpList.clear();
        }
        userGrpList = new ArrayList();

        if (null == getUserName().getValue()) {
            System.out.println("User Name is NULL");
        } else {
            if (!getUserName().getValue().toString().isEmpty()) {
                System.out.println("User: " +
                                   getUserName().getValue().toString());
                setUserGroup(listMemberGroups(getUserName().getValue().toString().toLowerCase()));

                if (null != getUserGroup()) {
                    haveCurrent = haveCurrent(getUserGroup());
                    while (haveCurrent) {
                        String group = getCurrentName(getUserGroup());
                        userGrpList.add(new UserGroups(group));
                        advanceCur(getUserGroup());
                        haveCurrent = haveCurrent(getUserGroup());
                    }
                }
                System.out.println("User Group Size: " + userGrpList.size());
            }
        }
    }  
 
    public String listMemberGroups(String userName) {
        String cursor = null;
        BindingContext bctx = BindingContext.getCurrent();
        BindingContainer bindings = bctx.getCurrentBindingsEntry();

        OperationBinding listOp =
            bindings.getOperationBinding("listMemberGroups");

        listOp.getParamsMap().put("memberUserOrGroupName", userName);
        cursor = (String)listOp.execute();

        if (!listOp.getErrors().isEmpty()) {
            List errors = listOp.getErrors();
        }
        return cursor;
    }

    public Boolean haveCurrent(String cursor) {
        Boolean haveCurrent = false;
        BindingContext bctx = BindingContext.getCurrent();
        BindingContainer bindings = bctx.getCurrentBindingsEntry();

        OperationBinding haveCurrOp =
            bindings.getOperationBinding("haveCurrent");

        haveCurrOp.getParamsMap().put("cursor", cursor);
        haveCurrent = (Boolean)haveCurrOp.execute();

        if (!haveCurrOp.getErrors().isEmpty()) {
            List errors = haveCurrOp.getErrors();
        }
        return haveCurrent;
    }

    public String getCurrentName(String cursor) {
        String group = null;
        BindingContext bctx = BindingContext.getCurrent();
        BindingContainer bindings = bctx.getCurrentBindingsEntry();

        OperationBinding getNameOp =
            bindings.getOperationBinding("getCurrentName");

        getNameOp.getParamsMap().put("cursor", cursor);
        group = (String)getNameOp.execute();

        System.out.println("UserGroup: " + group);

        if (!getNameOp.getErrors().isEmpty()) {
            List errors = getNameOp.getErrors();
        }
        return group;
    }

    public void advanceCur(String cursor) {

        BindingContext bctx = BindingContext.getCurrent();
        BindingContainer bindings = bctx.getCurrentBindingsEntry();

        OperationBinding advanceOp = bindings.getOperationBinding("advance");
        advanceOp.getParamsMap().put("cursor", cursor);
        advanceOp.execute();
    }

    public void setUserName(RichInputText userName) {
        this.userName = userName;
    }

    public RichInputText getUserName() {
        return userName;
    }

    public void setUserGroup(String userGroup) {
        this.userGroup = userGroup;
    }

    public String getUserGroup() {
        return userGroup;
    }

    public void setUserGrpList(List<UserGroups> userGrpList) {
        this.userGrpList = userGrpList;
    }

    public List<UserGroups> getUserGrpList() {
        return userGrpList;
    }

   UserGroups Object:
   public class UserGroups {
       private String groupName;

       public UserGroups(String groupName) {
           this.setGroupName(groupName);
       }

       public void setGroupName(String groupName) {
           this.groupName = groupName;
       }

       public String getGroupName() {
           return groupName;
       }
   }

Tuesday, March 17, 2015

ADF: Partial Rollback

Recently I developed a page where I provided edit functionality for a table row in a Popup with Dialog having Save/Cancel buttons.

If user add/edit/remove row and click Save, the data is getting saved. And if user clicks on Cancel button, the updated data/action inside popup needs to be revert back, but its not happening. af:resetActionListener won't help.

Initially I thought of execute rollback when user clicked on Cancel button, but it impacts other changes in the page. So I tried below steps to perform partial rollback to revert only changes done in Popup. Here I used similar save point concept explained in Link.

1. Add following methods to Bean class to create/restore/remove save point.

    public static DCBindingContainer getDCBindingContainer() {
        return (DCBindingContainer)getBindingContainer();
    }

    public static BindingContainer getBindingContainer() {
        return (BindingContainer)resolveExpression("#{bindings}");
    }

    public static Object resolveExpression(String expression) {
        FacesContext facesContext = getFacesContext();
        Application app = facesContext.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesContext.getELContext();
        ValueExpression valueExp =
            elFactory.createValueExpression(elContext, expression,
                                            Object.class);
        return valueExp.getValue(elContext);
    }

    public static FacesContext getFacesContext() {
        return FacesContext.getCurrentInstance();
    }

    public void createSavePoint(ActionEvent actionEvent) {
        DCBindingContainer bindingContainer = getDCBindingContainer();
        DCDataControl dcDataControl = bindingContainer.getDataControl();
        String spID = (String)dcDataControl.createSavepoint();
        AdfFacesContext.getCurrentInstance().getPageFlowScope().put("spID",
                                                                    spID);
        System.out.println("Save Point Created: " + spID);
    }

    public void restoreSavePoint() {
        String spID =
            (String)AdfFacesContext.getCurrentInstance().getPageFlowScope().get("spID");
        DCBindingContainer bindingContainer = getDCBindingContainer();
        DCDataControl dcDataControl = bindingContainer.getDataControl();
        dcDataControl.restoreSavepoint(spID);
        System.out.println("Save Point Restored: " + spID);
    }

    public void removeSavePoint() {
        AdfFacesContext.getCurrentInstance().getPageFlowScope().put("spID",
                                                                    null);
        System.out.println("Save Point Removed");
    }

2. Add button and call createSavePoint method when clicked on it. Since popup and also actionListener to be called at same time, set the showPopupBehaviour's TriggerType as Click.

     <af:commandButton text="Edit" id="cbj4"
                                       partialSubmit="true"
                                      actionListener="#{pageFlowScope.editBean.createSavePoint}">
                          <af:showPopupBehavior triggerType="click"
                                                popupId="editPop"/>
      </af:commandButton>

3. Set ContentDelivery of the af:popup to lazyUncached to display value from the base attribute. Set af:dialog 'type' as 'yesNo' and 'closeIconVisible' to 'false'.Add dialogListener method to listen to actions from Dialog.

      <af:popup id="editPop"
                  contentDelivery="lazyUncached"
                 <af:dialog id="d451" type="yesNo" title="Edit Host"
                                   dialogListener="#{pageFlowScope.editBean.editDialogListener}"
                                   closeIconVisible="false"
                                   affirmativeTextAndAccessKey="Save"
                                   noTextAndAccessKey="Cancel">
 <af:table value=" ..../>
        </af:dialog>
     </af:popup>

4. Add following Dialog Listener method to Bean class to call restore/remove save point methods based on dialog action.

  public void editDialogListener(DialogEvent dialogEvent) {
        Outcome outcome = dialogEvent.getOutcome();
        System.out.println("Outcome: " + outcome);
        if (outcome == Outcome.yes) {
            removeSavePoint();
        } else {
            restoreSavePoint();
        }
    }

5. Add Dialog component id to table's Partial trigger property to refresh when Save/Cancel performed.

   <af:table partialTriggers="d451" value=" ..../>

Tuesday, February 17, 2015

DB 12c: ORA-01950: no privileges on tablespace

Recently I faced following error message when I tried to insert data into a table in 12c.

Error:
SQLException occurred
SQLErrorCode=1950
SQLErrorMesg=ORA-01950: no privileges on tablespace 'XXX_BINSTORE'

Cause:
This is an expected behavior in Oracle Database 12c, even though user granted with RESOURCE role.

In Oracle Database 12c, RESOURCE role no longer grants UNLIMITED TABLESPACE system privilege by default.

Fix:
Grant UNLIMITED TABLESPACE system privilege to the user manually.

Else grant Unlimited quota on specific tablespace

Ex:

GRANT UNLIMITED TABLESPACE TO "lkakarla" ;
(or)
ALTER USER "lkakarla" QUOTA UNLIMITED ON XXX_BINSTORE;

Ref:
http://docs.oracle.com/database/121/DBSEG/release_changes.htm#DBSEG421
http://docs.oracle.com/database/121/DBSEG/authorization.htm#DBSEG4414


Queries to check Table space quota & Privileges granted to user:

SELECT * FROM DBA_TS_QUOTAS;

SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE= 'lkakarla';

Monday, February 16, 2015

OHS 11g: Customize Default Home Page - Shorten URL

If you have Oracle HTTP Server (OHS) as the front-end web server, the default OHS server URL will redirect you to Oracle Fusion Middleware welcome page.


There are many ways to customize default welcome page in OHS 11g. This will also help you to shorten your application URL when giving it to end user.

Method1: Modify httpd.conf default index page entry.

In 11g, the name of the default index page of HTTP Server is "welcome-index.html".

Modify the below lines in the httpd.conf file

 DirectoryIndex welcome-index.html

 to

 DirectoryIndex custom_page.html

where "custom_page.html"  is the name of the static page which you would liked to be displayed instead of the default welcome-index.html page. This custom_page.html should be placed inside the htdocs directory inside the apache folder.

Bounce OHS server to reflect changes.

Method2: Modify "welcome-index.html" existing inside htdocs. No need to Bounce OHS.

1. Go to htdocs folder in your web server environment
2. Backup current "welcome-index.html" file and create new file called "welcome-index.html" with the HTML content you want to show.
3. Now enter the OHS server URL and it will redirect/show the content specified in new "welcome-index.html"

Sample HTML Code:

<html>
<head>
<title>Sample Application</title>
<meta http-equiv="REFRESH" content="0;url=https://www.lkakarla.com/SampleApp/faces/Employees.jspx">
</head>
</html>

Saturday, February 14, 2015

WebLogic: Connection has been administratively disabled

Recently one of ADF applications deployed to Weblogic Managed server throwing following error messages in logs when tried to perform any database operations. Provided root cause for these errors and solution to handle this.

Error Message 1:
oracle.jbo.JboException: JBO-29114 ADFContext is not setup to process messages for this exception. Use the exception stack trace and error code to investigate the root cause of this exception. Root cause error code is JBO-29000. Error message parameters are {0=java.sql.SQLRecoverableException, 1=IO Error: Connection reset}
        Truncated. see log file for complete stacktrace

Caused By: java.sql.SQLRecoverableException: IO Error: Connection reset
        at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:962)
        at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1283)

Caused By: java.net.SocketException: Connection reset
        at java.net.SocketInputStream.read(SocketInputStream.java:168)
        at oracle.net.nt.MetricsEnabledInputStream.read(TcpNTAdapter.java:730)

Error Message 2:
oracle.jbo.JboException: JBO-29114 ADFContext is not setup to process messages for this exception. Use the exception stack trace and error code to investigate the root cause of this exception. Root cause error code is JBO-29000. Error message parameters are {0=java.sql.SQLRecoverableException, 1=Connection has been administratively disabled. Try later.}
        Truncated. see log file for complete stacktrace

Caused By: java.sql.SQLRecoverableException: Connection has been administratively disabled. Try later.
        at weblogic.jdbc.common.internal.ConnectionEnv.checkIfEnabled(ConnectionEnv.java:1025)
        at weblogic.jdbc.wrapper.PoolConnection.checkConnection(PoolConnection.java:63)
        at weblogic.jdbc.wrapper.Connection.preInvocationHandler(Connection.java:100)

Cause:
There could be several reasons. Review Admin/Managed server logs to find the exact error message.

Most of the times, it could be due to unavailability of Database. If DBMS becomes and remains unavailable, the data source will test and try to replace dead connections while trying to satisfy connection requests. This behavior is beneficial because it enables the data source to react immediately when the database becomes available. However, testing a dead database connection can take as long as the network timeout, and can cause a long delay for clients.

To minimize this delay, the WLS data sources include logic that disables the data source after 2 consecutive failures to replace a dead connection. When an application requests a connection from a disabled data source, WebLogic Server throws a PoolDisabledSQLException immediately to notify the client that a connection is not available.

Fix:
Immediate fix:
You need to restart your WebLogic Admin or Managed Server(based on data source targets) to recreate new JDBC connections. All the current transactions are lost, as the database was shutdown.

Permanent fix:
1. High availability of your RDBMS is required to minimize this issue.

2. The WebLogic Server team recommends making sure that "Test on Connection Reserve" is selected in the datasource --> connection pool configuration (under the advanced section). This might have a small performance impact (as each datasource connection request will ping the database to check whether it is active or not before giving the connection to application). However, enabling this would ensure that only a valid connection will be given to the application, and there would not be any stale connection in the environment (another possible cause for - administratively disabled).

The following connection pool configs can be found in Admin console > Services > Data Sources > click on the datasource > Configuration tab > Advanced.
Below shows how the configurations would look appear the domain's datasource file, e.g.: <path_to_domain>/config/jdbc/<datasource_name>-xxxx-jdbc.xml.

The fix has been confirmed working with the following settings:

<test-frequency-seconds>1</test-frequency-seconds>
<test-connections-on-reserve>true</test-connections-on-reserve>
<test-table-name>SQL SELECT 1 FROM DUAL</test-table-name>
<seconds-to-trust-an-idle-pool-connection>1</seconds-to-trust-an-idle-pool-connection>

Saturday, January 31, 2015

ADF: Upload/Download File

Recently when I need to upload/download documents using BLOB data type and allow only few document types to upload. I followed below steps to achieve in ADF.

JSF:

    <af:decorativeBox id="db1" styleClass="AFStretchWidth" theme="light"
                      topHeight="150px" inlineStyle="width:1100px"
                      dimensionsFrom="children">
      <f:facet name="top">
        <af:panelBox id="pb2" showDisclosure="false"
                     text="Upload File Here:"
                     rendered="#{pageFlowScope.Mode eq 'Edit'}">
          <af:panelFormLayout id="pfl1">
            <f:facet name="footer">
              <af:panelGroupLayout id="gh1">
                <af:commandButton text="Upload" id="cb3" partialSubmit="true"
                                  disabled="#{pageFlowScope.miscBean.miscInputFile.value eq null ? true : false}"
                                  actionListener="#{pageFlowScope.miscBean.uploadListener}"
                                  partialTriggers="if1"/>
                <af:spacer width="10" height="10" id="s52"/>
                <af:commandButton text="Reset" id="cb5" immediate="true"
                                  partialSubmit="true"
                                  disabled="#{pageFlowScope.miscBean.miscInputFile.value eq null ? true : false}"
                                  actionListener="#{pageFlowScope.miscBean.resetListener}"
                                  partialTriggers="if1"/>
              </af:panelGroupLayout>
            </f:facet>
            <af:spacer width="10" height="10" id="shi2"/>
            <af:inputFile label="Select File to Upload" id="if1"
                          autoSubmit="true"
                          binding="#{pageFlowScope.miscBean.miscInputFile}"
                          valueChangeListener="#{pageFlowScope.miscBean.fileInputChangeListener}"
                          partialTriggers="cb3 cb5"/>
            <af:spacer width="10" height="10" id="s2"/>
          </af:panelFormLayout>
        </af:panelBox>
      </f:facet>
      <f:facet name="center">
        <af:panelGroupLayout layout="vertical" id="pgl1">
          <af:spacer width="10" height="10" id="s3"/>
          <af:outputText value="Existing Files:" id="ot1"
                         inlineStyle="font-weight:bold;"/>
          <af:spacer width="10" height="10" id="s37"/>
          <af:panelCollection id="pc1" styleClass="AFStretchWidth">
            <af:table value="#{bindings.UploadedFilesVO1.collectionModel}"
                      var="row" rows="#{bindings.UploadedFilesVO1.rangeSize}"
                      rowBandingInterval="1" autoHeightRows="15"
                      selectedRowKeys="#{bindings.UploadedFilesVO1.collectionModel.selectedRow}"
                      selectionListener="#{bindings.UploadedFilesVO1.collectionModel.makeCurrent}"
                      rowSelection="single" id="t1" styleClass="AFStretchWidth" partialTriggers=":::cb3 d51">
              <af:column sortProperty="#{bindings.UploadedFilesVO1.hints.FileName.name}"
                         sortable="true" width="340"
                         headerText="#{bindings.UploadedFilesVO1.hints.FileName.label}"
                         id="c2">
                <!--<af:outputText value="#{row.FileName}" id="ot4"/> -->
                <af:commandLink text="#{row.FileName}" id="cl1">
                  <af:fileDownloadActionListener contentType="#{row.MimeType}"
                                                 filename="#{row.FileName}"
                                                 method="#{pageFlowScope.miscBean.downloadFile}"/>
                </af:commandLink>
              </af:column>
            </af:table>
          </af:panelCollection>
        </af:panelGroupLayout>
      </f:facet>
    </af:decorativeBox>


Bean Method:

    import org.apache.commons.io.IOUtils;
    import org.apache.myfaces.trinidad.model.UploadedFile;

    private String fileName;
    private String contentType;
    private UploadedFile uploadedFile;
    private BlobDomain fileContent;
    private RichInputFile miscInputFile;

    public void fileInputChangeListener(ValueChangeEvent valueChangeEvent) {
        // Add event code here...
        uploadedFile = (UploadedFile)valueChangeEvent.getNewValue();
        // Get the file name
        fileName = uploadedFile.getFilename();
        // get the mime type
        contentType = uploadedFile.getContentType();
        // get blob
        fileContent = getBlob(uploadedFile);
    }

    public BlobDomain getBlob(UploadedFile file) {
        InputStream in = null;
        BlobDomain blobDomain = null;
        OutputStream out = null;
        try {
            in = file.getInputStream();
            blobDomain = new BlobDomain();
            out = blobDomain.getBinaryOutputStream();
            IOUtils.copy(in, out);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.fillInStackTrace();
        }
        return blobDomain;
    }

    public void showMessage(String message, String severity) {
        FacesMessage fm = new FacesMessage(message);
        if (severity == "INFO") {
            fm.setSeverity(FacesMessage.SEVERITY_INFO);
        } else if (severity == "FATAL") {
            fm.setSeverity(FacesMessage.SEVERITY_FATAL);
        } else if (severity == "ERROR") {
            fm.setSeverity(FacesMessage.SEVERITY_ERROR);
        } else if (severity == "WARN") {
            fm.setSeverity(FacesMessage.SEVERITY_WARN);
        }
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, fm);
    }

    public void uploadListener(ActionEvent actionEvent) {
        // Add event code here...

        if (validateFileExt(fileName)) {

            BindingContext bctx = BindingContext.getCurrent();
            BindingContainer bindings = bctx.getCurrentBindingsEntry();
            OperationBinding operationBinding =
                bindings.getOperationBinding("uploadFile");

            String status = null;

            if (operationBinding != null) {
                operationBinding.getParamsMap().put("pFileName", fileName);
                operationBinding.getParamsMap().put("pFileContent",
                                                    fileContent);
                operationBinding.getParamsMap().put("pContentType",
                                                    contentType);
                status = (String)operationBinding.execute();
            }

            if (!operationBinding.getErrors().isEmpty()) {
                //check errors
                List errors = operationBinding.getErrors();
            }

            showMessage(status, "INFO");

            System.out.println("File " + fileName + " uploaded successfully");

        } else {

            String warning =
                "Please only upload files that end in types: .txt .doc .rtf .pdf .jpeg .jpg .bmp .docx .gif .html .htm .png .xls .ppt .xml .xlsx .pptx .sql .sh .pl .lst .zip .tar .ods .odt .xlsm. Please select a new file to upload and submit again.";
            showMessage(warning, "WARN");
        }

        if (null != miscInputFile) {
            miscInputFile.resetValue();
            miscInputFile.setValid(true);
        }
    }

    public Boolean validateFileExt(String fileName) {
        if (fileName.endsWith(".txt") || fileName.endsWith(".doc") ||
            fileName.endsWith(".rtf") || fileName.endsWith(".pdf") ||
            fileName.endsWith(".jpeg") || fileName.endsWith(".jpg") ||
            fileName.endsWith(".bmp") || fileName.endsWith(".docx") ||
            fileName.endsWith(".gif") || fileName.endsWith(".html") ||
            fileName.endsWith(".htm") || fileName.endsWith(".png") ||
            fileName.endsWith(".xls") || fileName.endsWith(".ppt") ||
            fileName.endsWith(".xml") || fileName.endsWith(".xlsx") ||
            fileName.endsWith(".pptx") || fileName.endsWith(".sql") ||
            fileName.endsWith(".sh") || fileName.endsWith(".pl") ||
            fileName.endsWith(".lst") || fileName.endsWith(".zip") ||
            fileName.endsWith(".tar") || fileName.endsWith(".ods") ||
            fileName.endsWith(".odt") || fileName.endsWith(".xlsm")) {
            return true;
        }
        return false;

    }

    public void downloadFile(FacesContext facesContext,
                             OutputStream outputStream) {
        DCBindingContainer bindings =
            (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
        DCIteratorBinding iteratorbinding =
            bindings.findIteratorBinding("UploadedFilesVO1Iterator");
        Row row = iteratorbinding.getCurrentRow();
        System.out.println("Selected: " + row.getAttribute("FileName"));
        BlobDomain blob = (BlobDomain)row.getAttribute("FileContent");
        try {
            IOUtils.copy(blob.getInputStream(), outputStream);
            blob.closeInputStream();
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setMiscInputFile(RichInputFile miscInputFile ) {
        this.miscInputFile = miscInputFile ;
    }

    public RichInputFile getMiscInputFile() {
        return miscInputFile;
    }

    public void resetListener(ActionEvent actionEvent) {
        if (null != miscInputFile) {
            miscInputFile.resetValue();
            miscInputFile.setValid(true);
        }
    }


AM Method:

    public String uploadFile(String pFileName, BlobDomain pFileContent, String pContentType) {

        ViewObjectImpl vo = getUploadedFilesVO1();
        Row row = vo.createRow();

        row.setAttribute("FileContent", pFileContent);
        row.setAttribute("FileName", pFileName);
        row.setAttribute("MimeType", pContentType);

        vo.insertRow(row);
        vo.executeQuery();

        this.getDBTransaction().commit();

        return "File " + pFileName + " Uploaded Successfully";
    }