Sunday, September 30, 2012

oracle.jbo.RowInconsistentException (JBO-25014)


oracle.jbo.RowInconsistentException: JBO-25014: Another user has changed the row with primary key oracle.jbo.Key[SI00892 FU9897O]

 After a lot of pondering, head banging and googling I somehow removed this exception and the following post is the collective summary of the solution.

This exception may be caused when some other user changes the database entry you are currently working on and by the time you commit the changes ,this exception occurs as the value was already changed in the database.

The basic scenario for such exception -


 1.You called the commit method.The particular EO instance is marked as changed. (Optimistic locking is enabled)

2.The current values of the corresponding row in the database do not match the original values queried for the row .


So if your ADF-based application is getting this exception, then the reason is either one of the following:
  1. Some other user has actually updated the row since you retrieved it from the database.
  2. Your application may contain a problem that makes BC4J think that the row has been changed when logically no other user has really changed it. 
So in such case RIE is thrown. Well , in case another user has changed the row then this exception is required, keeping in mind the Data Integrity point of Database Transactions .


But what if this exception is thrown when no other user actually changed anything on that particular row ? For instance in a development environment , where developers are using their own database , and no other user could possibly be using the database records. Thus arises the problem of Phantom User .

The common reasons for which this error can be thrown when logically you believe it should not, are:
  1. You have database triggers modifying the values of some of the columns on which your entity's persistent attributes are based, and you have failed to correctly indicate that to BC4J with the "Refresh on Insert" and/or "Refresh on Update" flags on the entity object attribute definition.
When we insert or update a record in the database via ADF, ADF keeps a copy of the record written to the database. However the cached record is instantly out of date as the database triggers have modified the record that was actually written to the database. Thus when we update the record we just inserted or updated for a second time to the database, ADF compares its original copy of the record to that in the database, and it detects the record has been changed – giving us JBO-25014.
  1. You have (or have added) database level column default values for attributes and failed to correctly indicate that to BC4J with the "Refresh on Insert" property for the corresponding attribute.
  1. You have created a view object with "Expert Mode" custom SQL statement, and have gotten the SELECT list of your custom SQL statement out of sync with the view object attributes.
  1. You are using an attribute whose type is a custom domain class and have failed to correctly implement the equals() method for your custom domain class.
  1. Your are using a ROWID-valued attribute in your entity and have not marked it to "Refresh on Insert" and "Refresh on Update". Some features of the database like partitioning can cause the value of ROWID to be updated when data in the row is updated.

  2. If the attribute which is inconsistent is actually the accessor for an association, in particular, an association that is a composition with “Lock Top-Level Container” set on it i.e, when any composed entity is locked, the containing entity gets locked too. So if we try to acquire a lock on one of the 'composing' entity object instances, and someone else has changed the top-level containing row, we’ll get a RowInconsistentException on the top-level row even if we haven’t changed that row.

Solution :
=> In case it is caused by triggers ,set the refresh flags appropriately for any related entity object attributes that your triggers are changing.

=> Close any open RowSetIterators which have been used, as unlike Rollback where open Iterators are closed by framework itself, on Commit Iterators are not closed automatically by framework. So we need to explicitly close any open iterators we used.

=> Re-querying all View Objects on commit :
Though this is an expensive technique as it requeries every time after commit for all the view Objects of the AM.


  1. <AppModule
  2. xmlns="http://xmlns.oracle.com/bc4j"
  3. Name="AppModuleAM"
  4. Version="11.1.2.60.81"
  5. ClearCacheOnRollback="true"
  6. RequeryOnCommit="true"
  7. ComponentClass="myApp.model.module.AppModuleAMImpl"
  8. ComponentInterface="myApp.model.common.AppModuleAM"
  9. ClientProxyName="myApp.model.client.AppModuleAMClient" 
=> Another way is to execute the View Object after the commit operation to bring back updated data to ViewObject. For this we need to override the afterCommit() of ViewObject class as follows:


  1. public void afterCommit(TransactionEvent){
  2. executeQuery();
  3. }

This would call the view Object's executeQuery() method which would bring the latest data from the database thus avoiding any data mismatch between the cached copy and the original data from the database.

=> In case the entity attribute accessor is the culprit we should refresh the values in the overridden lock() method as follows:


  1. public void lock() {
  2. try {
  3. super.lock();
  4. } catch (RowInconsistentException e) {
  5. refresh(REFRESH_WITH_DB_ONLY_IF_UNCHANGED | REFRESH_CONTAINEES);
  6. super.lock();
  7. }
  8. }
References:

Monday, September 3, 2012

How to show inline FacesMessage in page in Oracle ADF -

When we want to show any information , warning or error , we use FacesMessage Component. you can see on Show FacesMessage how we do this.
But sometimes we want to show Message in the same page, it means inside the page, In this tutorial we will see how to show Inline Message in ADF

FacesMessage look like this
Information Message Error Message Fatal Error Warning Message


But now we will use Inline Message, For this follow these steps

  • Create a Fusion Web Application
  • Now in ViewController create new Page
  • Now drop a button on page
  • Write this code on button ActionListener

  • public void showMessageButton(ActionEvent actionEvent) {
     FacesMessage msg=new FacesMessage("This is a FacesMessage that+ 
    "shows Fatal Error --JavaCup");
     msg.setSeverity(FacesMessage.SEVERITY_FATAL);
     FacesContext fctx=FacesContext.getCurrentInstance();
     fctx.addMessage(null, msg);
     }
    


  • Now It will work as FacesMessage, to show Message inside page you have to do some more
  • Drop a af:messages component on page from Component Pallette

  • af:messages
  • now select af:messages and go to property inspector and set Inline- true

  • Inline true
  • Now Run your page and click button it will look like this

  • InlineMessage
    Download Sample Application

Thursday, August 30, 2012

Dealing with Date format

By default whenever a date field is dragged onto the page, an <af:convertDateTime> component is added to it which decides the pattern and other prerequisites (like Locale,Timezone etc) . So the simplest way to change the display pattern for date is to -

1.Select <af:convertDateTime> component.
2.Edit the Pattern
3.Change it to your required pattern ,say dd/MM/yyyy as in my case.

 
But this has to be done wherever the date field is present , so instead of changing the format on every component it is recommended to be changed from the model layer itself, so that changes reflect at all levels.


Convert Date at Model layer-

Date format can be changed at the Model layer either in EO or VO level attributes. If we change it in the EO level, it gets reflected in VO level as well.
By default on selecting Format Type :Simple Date under UIHints tab in EO for the date attribute, we can select
the required date format from the preconfigured format list provided .


 
But what if we want custom format like dd/MM/yyyy or dd/MMM/yy ?

So in this case though we can add custom pattern on the view page using <af:convertDateTime> (discussed earlier),but it won't be available in case of search queries ie to format the pattern for the bind variables (search fields).
So for such requirement, we need to Locate formatinfo.xml under the following directory( For Jdeveloper 11.1.2.1.0)

C:\Documents and Settings\[USER]\ApplicationData\JDeveloper\system11.1.2.1.38.60.81\o.BC4J\formatinfo.xml

 
Edit formatinfo.xml and modify the required date class:


  1. <DOMAIN CLASS="oracle.jbo.domain.Date">
  2. <FORMATTER name="Simple Date" class="oracle.jbo.format.DefaultDateFormatter">
  3. <FORMAT text="yyyy-MM-dd" />
  4. <FORMAT text="yyyy-MM-dd G 'at' hh:mm:ss" />
  5. <FORMAT text="EEE, MMM d, ''yy" />
  6. </FORMATTER>
  7. </DOMAIN>


After we add required format using <FORMAT> tag the code would look as follows:


  1. <DOMAIN CLASS="oracle.jbo.domain.Date">
  2. <FORMATTER name="Simple Date" class="oracle.jbo.format.DefaultDateFormatter">
  3. <FORMAT text="yyyy-MM-dd" />
  4. <FORMAT text="yyyy-MM-dd G 'at' hh:mm:ss" />
  5. <FORMAT text="EEE, MMM d, ''yy" />
  6. <FORMAT text="dd-MM-yyyy" />
  7. <FORMAT text="dd-MMM-yyyy" />
  8. </FORMATTER>
  9. </DOMAIN>

Now when we browse through our list of Formats in 'UIHints' tab, we can find our custom format pattern available.


So now we can select dd/MM/yyyy as our format for date and the change reflects to all levels .

 
 Yet another way to change the date format would be to use the Locale specific date formatting .i.e without changing individual date attribute's format.
It can be accomplished by adding the following localization property inside trinidad-config.xml (Present under WEB-INF folder of View Controller)


  1. <formatting-locale>nl-BE</formatting-locale>

This would change all date types present in your project to dd/MM/yyyy from the default MM/dd/yyyy . But the only problem with this is it changes all the Numeric formatting types also to the 'nl-BE' locale (IANA-formatted locale). Other IANA-formatted locale for changing date to dd/MM/yyyy is 'en-GB' .

Note: 
<formatting-locale>:  Defines the date and number format appropriate to the selected locale. ADF Faces and Trinidad, will by default, format dates and numbers in the same locale used for localized text. If you want dates and numbers formatted in a different locale, you can use an IANA-formatted locale (for example, ja, fr-CA). The contents of this element can also be an EL expression pointing at an IANA string or a java.util.Locale object.

Wednesday, August 29, 2012

Reset/Cancel a Form

Sometimes we need to cancel a form or reset all the editable form fields. The following post provides few methods for doing so.

1. Rollback - the traditional way. It rollbacks to the previous state for the changes that are not committed yet.
        It closes all open Row Iterators and executes the View Object.
        Either use it on a  buttons Action Listener or directly drag the Rollback operation from the Data Control.
        Only problem is it rollbacks all the uncommitted transactions instead of only resetting the form field.
        It invokes ROLLBACK from database and executes View Object to return latest data from DB for all  rows.
        This works well when we want to undo multiple changes at once.

2.  <af:resetButton> - Resets the content of a form for all editable components, updating all editable components with the current values of the model on the server.


  1. <af:resetButton text="resetButton" id="rb1"/>
 
3. Using ResetUtils  - It resets all editable value holders present inside UIComponent 'myForm' .The UIComponent is searched for within the component tree and the first one encountered is reset back to the original value.


  1. public void resetActionListener(ActionEvent actionEvent){
  2. UIComponent myForm = actionEvent.getComponent();
  3. oracle.adf.view.rich.util.ResetUtils.reset(myForm);
  4. }

4. Using af:resetActionListener.  Drag the af:resetActionListener component onto the cancel button and set immediate=true on the cancel button so client validation is bypassed .

  1. <af:commandButton text="Cancel" id="cb1" action="#{bindings.Rollback.execute}" inlineStyle="font-weight:bolder;" immediate="true" >
  2. <af:resetActionListener/>
  3. </af:commandButton>

5. Queue an action on an existing button containing  af:resetActionListener attached to it on the page.The button's binding is present in the bean.


  1. public void resetActionUsingQueueListener(ActionEvent aEvent){
  2. //Queue the binded button's action to this button's action event.
  3. ActionEvent actionEvent = new ActionEvent(this.getResetButton());
  4. actionEvent.queue();
  5. }

6. Create instance of resetActionListener in bean and call processAction ()

  1. public void resetAct(ActionEvent actionEvent) {
  2. ResetActionListener ral = new ResetActionListener();
  3. ral.processAction(actionEvent);
  4. }

7.Another way is Cancel by undoing changes - set "immediate=true" on the cancel button.
Open the ViewImpl class and add the following code which first determine whether or not the row is new or an existing row and accordingly either removes/refresh it.


  1. public String getRowStatus(Row row){
  2. DepartmentsViewRowImpl rwImpl = (DepartmentsViewRowImpl)row;
  3. String rwStatus = translateStatusToString(rwImpl.getEntity(0).getEntityState());
  4. return rwStatus;
  5. }
  6. private String translateStatusToString(byte b) {
  7. String ret = null; switch (b) {
  8. case Entity.STATUS_INITIALIZED: {
  9. ret = "Initialized"; break;
  10. }
  11. case Entity.STATUS_MODIFIED: {
  12. ret = "Modified"; break;
  13. }
  14. case Entity.STATUS_UNMODIFIED: {
  15. ret = "Unmodified"; break;
  16. }
  17. case Entity.STATUS_NEW: {
  18. ret = "New"; break;
  19. } }
  20. return ret;
  21. }

Provide the ActionListener ('onCancel') for Cancel Button on the page  -


  1. public void onCancel(ActionEvent actionEvent) {
  2. FacesContext fctx = FacesContext.getCurrentInstance();
  3. DCBindingContainer bindings = (DCBindingContainer) BindingContext.getCurrent().getCurrentBindingsEntry();
  4. DCIteratorBinding iter = bindings.findIteratorBinding("DepartmentsView1Iterator");
  5. Row rw = iter.getCurrentRow();
  6. OperationBinding getRowStatusBinding = bindings.getOperationBinding("getRowStatus");
  7. String rwStatus = (String)getRowStatusBinding.execute();
  8. if ("NEW".equalsIgnoreCase(rwStatus)){
  9. iter.removeCurrentRow();
  10. iter.refreshIfNeeded();
  11. } else{
  12. rw.refresh(Row.REFRESH_UNDO_CHANGES);
  13. }
  14. fctx.renderResponse();
  15. }

8. Custom bean method to reset the UIcomponent (form) :


  1. public void resetAction(ActionEvent aE) {
  2. AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
  3. resetValueInputItems(adfFacesContext,searchForm);
  4. }
  5. private void resetValueInputItems(AdfFacesContext adfFacesContext, UIComponent component){
  6. List<UIComponent> items = component.getChildren();
  7. for ( UIComponent item : items ) {
  8. //resetValueInputItems(adfFacesContext,item); //uncomment in case of nested UIComponents
  9. if ( item instanceof RichInputText ) {
  10. RichInputText input = (RichInputText)item;
  11. if ( !input.isDisabled() ) {
  12. input.resetValue() ;
  13. adfFacesContext.addPartialTarget(input);
  14. }
  15. } else if ( item instanceof RichInputDate ) {
  16. RichInputDate input = (RichInputDate)item;
  17. if ( !input.isDisabled() ) {
  18. input.resetValue() ;
  19. input.setValue("");
  20. adfFacesContext.addPartialTarget(input);
  21. }
  22. } else if (item instanceof RichSelectOneChoice){
  23. RichSelectOneChoice input = (RichSelectOneChoice)item;
  24. if ( !input.isDisabled() ) {
  25. input.resetValue() ;
  26. input.setValue("");
  27. adfFacesContext.addPartialTarget(input);
  28. }
  29. }
  30. } //end -for each loop
  31. }

Sunday, August 5, 2012

Deployment Failure

Case: Recently I came across a deployment error-
[Application MyApp stopped and undeployed from Server Instance IntegratedWebLogicServer]

Remote deployment failed (oracle.jdevimpl.deploy.common.Jsr88RemoteDeployer)
# Cannot run application MyApp due to error deploying to IntegratedWebLogicServer..

Solution :
This Errror may occur when the applications UIModel project has created a web.xml file on it's own.
The only solution I could find was to delete the duplicate web.xml file from the project and redeploy the application. And it worked!

Note:JSRException may also occur in case the connection is not available.

Wednesday, July 25, 2012

Switcher Component in ADF


The switcher component dynamically decides which facet component should be rendered. It has two properties. The switcher will render the facet matching "facetName"; however, if no such facet exists (or "facetName" is null), and "defaultFacet" has been set, then that facet will be used instead.

Steps:

1.Create VOs based on different tables in the Model Layer:
 



2. In View Controller, create a View Page .Drag a Panel Group Layout from the Component Palette onto the page.

3. Drag the Switcher component from the Component Palette into the Panel Group layout.

4.Right Click the af:Switcher component > Insert Inside af:Switcher > Facet



5.Provide a Name for the Facet. Create 2 Facets and provide corresponding names for both.




6.Drag the 'Dept' VO from the Data Controls onto the Facet with name 'Query1' and create a table using the VO.



7.Repeat the Step for 'Emp' VO and create the table in 'Query2' facet. The structure would look as the figure below :





8. Select the Switcher Component and change the DefaultFacet property to 'Query1'.




9.In the FacetName property of the Switcher, write #{requestScope.table}



 
10. Drag a 'Choice' component from the component palette before the Panel group layout .
Provide 2 static values (Facet Names that have been created earlier are taken as Item Values)in the create list tab as shown in the following figure.




11. Change the AutoSubmit property of the Select One Choice to 'true' and it's Value property should be set to #{requestScope.table} .

12. Also Provide PartialTrigger property of the Panel Group Layout with the id of the Select One Choice component.

13. Run the Page. When we select 'Query1' we get the Dept table(default) and on selecting 'Query 2', Emp table is displayed.