Monday, June 25, 2012

Parametrizing WebCenter spaces customization


I'm using webcenter spaces, and I'm on PS5.

I need to customize some Webcenter task-flows, but I don't want the customization to take effect globally. I am using spaces, so I would like to localize customization to a space, or to an individual instance of a task-flow. For example, I have some MDS customization in the document task-flows, like in the wiki, I want to hide a bunch of features and controls/command buttons for all users except moderators.


I'll use the wiki as an example for this post. The wiki uses the folderViewer BTF (taskflow call) and the folderViewer BTF uses the DocumentViewer BTF (in the case of wiki). So a lot of these customizations are on the DocumentViewer taskflow's JSF fragments.
If I customize the right JSF fragments, everything can be shown/hidden based on the requirement. So far so good. Now the real problem is that these customizations are global, meaning for this webcenter spaces instance  or any WCS instance that uses this MDS DB, these customizations apply.

However, it would be nice to be able to parametrize (to an extent, and mostly at a very coarse grained level) some of the customizations so that I can customize these taskflows on a per Space level or per instance level. With Yannick's suggestion to add taskflow paramerets by customizing the taskflows themselves, I can do this at a per taskflow instance level , which is excellent !

Here is what I did :

Taking the example of the Wiki taskflow, here is the structure of the taskflow (you can see this by looking at the wiki taskflow from your library, in customization developer role)

Wiki Taskflow --calls--> FolderViewer Taskflow --calls--> DocumentViewer Taskflow

I want the customizations to be applied by default (just because that is the most common scenario for me), but want to give the page/space owners the ability to disable the customizations and expose the vanilla/out-of-the-box features of the taskflow too(a not-so-common thing in my implementation). To do this, when the person placing a wiki page on thier space, they should be able to set a parameter to disable all our internal customizations. This parameter obviously needs to be defined on the wiki tasklfow.


  • Step 1
    • Define new parmeter on the wiki taskflow.
    • Name the parameter something sensible, say 'disableCustomizations', make the type Boolean and set the value to #{pageFlowScope.disableCustomizations}. Setting the value is not mandatory if you dont mind the value being in pageFlowScope, which is the default scope it will be put in to at runtime, but I just prefer to be explicit.
    • Though the user sets the parameter on the wiki, the actual customizations are done on the DocumentViewer Taskflow's JSF fragments. So we need a way to propagate this parameter to the actual place/taskflow where it will be used.
    • Now one thing to note is that this  parameter value #{pageFlowScope.disableCustomizations} will not be available to the taskflow being called, DocumentViewer in our case. This is simple beacause taskflow calls and regions containing taskflows, when executed get thier own pageFlowScope that is different from the caller's pageFlowScope. I really dont want to keep a paremeter in the session scope, so I decided to add a parameter to  FolderViewer taskflow and from there propagate the value again to the actual DocumentViewer by adding a parameter to that as well.
  • Step 2
    • Add a paremeter to the FolderViewer Taskflow. Pass the user's input parameter to the FolderViewer, by customizing the taskflow call.
  • Step 3
    • Repeat step 2, but for propagating the value from FolderViewer to DocumentViewer.
    • Add a paramater to the DocumentViewer taskflow. Pass the parameter value recieved by FolderViewer to the DocumentViewer by customizing the taskflow call.
  • Step 4
    • Customize the DocumentViewer jsffs by incorporating the parameter value. Set the rendered attribute on the controls and components that I want to be able to toggle based on the parameter value.

Now deploy the customization and you should see a new parameter on the task-flow, and setting the parameter can toggle the components we have customized. The best thing is the new parameter can be set on per-instance of a taskflow, so I can have two Wiki taskflows on the same page, both looking different !

To recap, the idea here is to create your customization to be dependent on a parameter, which itself can be created through customization. Since customizations are global, the parameter is globally available across the webcenter instance, but since the actual, end user customization is dependent on the parameter, the customizations are applied only when the parameter is set. This enables us to finely control when the customizations are applied and when they are not.

A bunch of thanks to Yannick, for pointing me in the right direction from the OTN forums!
https://forums.oracle.com/forums/message.jspa?messageID=10377526#10377526

Wednesday, May 30, 2012

oracle.jbo.InvalidOperException: JBO-25221

This is a rather simple one, but it took me an hour to finally realize what was going on.

I got this error when trying to execute a custom method on my AM from a backing bean.
To execute the method, I got the OperationBinding from the binding container and called execute on it.
When testing I got this error.

oracle.jbo.InvalidOperException: JBO-25221

The error ,message basically explains that the method could not be found or is not supported. That should have been a good indicator, but I "knew" the method existed. What happened was that I had created the binding first, then changed the method signature on the AM and the client interface, but forgot to re-create the binding. So the parameters(in my case) that the binding had no longer was available on the AM's client interface.

Another thing to note here is the use of generics.


Wednesday, February 8, 2012

Updating a flag when deleting an entity in ADF BC / BC4J


Soft delete, is the process when the user deletes an entity, its deactivated or hidden, but not actually deleted. Its a fairly common use case, and comes in various flavors. In the most common case, a delete/active flag is flipped to signal that the row or entity has been deleted or inactivated. In other cases, the row is moved to another table, or some other action is taken in response to a deletion. In ADF most of these requirements can be met with ease. Lets look at the most common situation where a flag is flipped.

There are a couple of approaches I found by googling, some more involved than the others.
Jobinesh's Post on soft delete with undo.
http://jobinesh.blogspot.com/2011/05/soft-deletion-of-rows.html

Hussain's post on soft delete that sets up standard history column for the active flag and enables tooling
http://husaindalal.blogspot.com/2009/11/generic-way-to-handle-soft-delete.html

Jobinesh's method is particularly useful for implementing an Undo functionality, and it does not commit the transaction. Hussain's method needs quite a bit of setup and when you have existing tables that don't follow standards, it may be difficult to implement. My own approach, based on Jobinesh's method, seems to me, is the most simplistic, and I think its the most generic and offers most flexibility. It basically abstracts the method Jobinesh describes in to a base class that can be selectively extended by EOs that need SoftDelete.

I will create a new base VOImpl calss that can be extended by all VOs that I want to support soft delete for. Not all VOs need to extend this. If a VO extends it, the VO is forced to implement a method that updates the value of one of its columns. This also does away with standardizing a column name for the "Active flag" and also different VOs can use different standards. This may be useful when the DataModel already exists and the existing tables have different column names for the flag column.

The crux of this method is overriding the remove() method on the EntityImpl , and converting a delete in to an update in the doDML() method of the EntityImpl both of which are now abstracted by the base class and the actual entities simply implement the logic of that column to use as the flag column and what sort of flag to set. Allowing the Entity to decide this helps when different entities need to use different flags.
The relevant code is below.
    public abstract void setActiveFlagColumn(boolean isActive);
    

    @Override
    protected void doDML(int operation, TransactionEvent transactionEvent) {
        if (operation == EntityImpl.DML_DELETE){          
          operation = EntityImpl.DML_UPDATE;
        }
          super.doDML(operation, transactionEvent);
        
    }


    @Override
    public void remove() {
        setActiveFlagColumn(false);
        super.remove();
    }

The method setActiveFlagColumn(boolean) is implemented by the actual EntityImpl class and it should be implemented based on the spacific table. In the example application to keep things simple, I chose to just update the PhoneNumber field with the value 'false'. A setter method for the actual Flag column will be available in the actual EntityImpl.
public void setActiveFlagColumn(boolean isActive) {
      // Make appropriate conversion to represent this value (0/1,Y/N,T/F,A/I... etc.)
      setPhoneNumber(String.valueOf(isActive));

  }
Example application
The example application is based on the HR schema, but since the HR schema does not contain any tables with a flag column, and I wanted to keep the work required to run the example to a minimum, I chose to use one of the existing columns as my flag column. Its the "Phone" number column in the EMPLOYEES table. When a record is deleted, the example application updates that record with phoneNumber=false. The VO in the example is setup to ignore records with "false" as the value for phone number. Again, this is just so that I want to avoid making you alter your schema just to run an example application. This also goes to demonstrate how generic the approach is. The example can be downloaded here :
http://myadfnotebook.googlecode.com/files/SoftDelete.zip

To run the example, just open the ViewController Project and  run the page empView.jspx.
In the example you will notice that the delete does not commit the transaction and I have a separate commit button. In many cases you'll want to delete and commit in the same action. Check out Shay's blog post on performing two declarative operations on one button to achieve that.