The wrong and right way to avoid invokeMethod in OAF Controllers
If you look at the tutorials supplied by Oracle with OA Framework, you’ll see in their Controllers that they use invokeMethod to indirectly call methods in the Model (ie: the BC4J Application Module). I understand why Oracle recommends that you do it this way – it ensures that there are no dependencies between the various parts of the Model, View and Controller.
Here is a simple example:
package abc.oracle.apps.xyz.po.webui; import oracle.apps.fnd.framework.server.OAApplicationModuleImpl; import oracle.apps.fnd.framework.webui.OAControllerImpl; class updatePo extends OAControllerImpl { public void processRequest(pageContext, webBean) { OAApplicationModule am = pageContext.getApplicationModule(webBean); am.invokeMethod(”initQuery”); } }
Things get trickier when you need to pass parameters:
package abc.oracle.apps.xyz.po.webui; import oracle.apps.fnd.framework.server.OAApplicationModuleImpl; import oracle.apps.fnd.framework.webui.OAControllerImpl; import java.io.Serializable; class updatePo extends OAControllerImpl { public void processRequest(pageContext, webBean) { OAApplicationModule am = pageContext.getApplicationModule(webBean); Serializable[] params = { 135, “POSTED” }; am.invokeMethod(”initQuery”, params); } }
It gets even more complex if you want to pass in a non-primitive value as one of the parameters. You need to build up an array of Class objects, each documenting the class of the corresponding parameter in the array of objects. All very messy! And, if you’re relatively new to Java like me, you’ll want to get as much help when you’re writing any Java code. JDeveloper has a very good Code Assistant that prompts you with all the available methods once you’ve typed in the name of a class or the name of an instance of that class. However, this is of no use whatsoever if you’re using invokeMethod.
My first solution was to import the relevant AM Implementation class that had been generated for me and cast the value returned by getApplicationModule to that class. This meant that the editor and compiler knew exactly what type of AM I’d got and the Code Assistant can help out.
package abc.oracle.apps.xyz.po.webui; import abc.oracle.apps.xyz.po.server.PurchaseOrderAMImpl; import oracle.apps.fnd.framework.webui.OAControllerImpl; class updatePo extends OAControllerImpl { public void processRequest(pageContext, webBean) { PurchaseOrderAMImpl am = (PurchaseOrderAMImpl)pageContext.getApplicationModule(webBean); am.initQuery(123, “POSTED”); } }
This is much neater than before and it work fine, but it breaks Oracle’s rule about dependencies, in that you have a reference to a server class in a webui class. Don’t forget the “Encapsulation Onion”! Up until recently, I didn’t worry too much about this: I had a neat solution and that was enough for me. However, recently, whilst trawling the online help for something else I came across a technique that doesn’t break any rules and still gives you all the benefits of the Code Assistant.
The solution is to create a Java Interface that describes the methods that you are going to define in your AMImpl class. An interface is much like a PL/SQL package specification in that it doesn’t contain any code. This interface will live in the package above both the webui and server classes:
package abc.oracle.apps.xyz.po; interface PurchaseOrderAM { public void initQuery(int poNumber, String status); }
Your AMImpl class then changes to link it to the new interface:
package abc.oracle.apps.xyz.po.server; import oracle.apps.fnd.framework.server.OAApplicationModule; import abc.oracle.apps.xyz.po.PurchaseOrderAM; class PurchaseOrderAMImpl extends OAApplicationModule implements PurchaseOrderAM { public void initQuery(int poNumber, String status) { // some code } }
Finally, your Controller is altered to reference the AM interface, rather than the AMImpl class:
package abc.oracle.apps.xyz.po.webui; import abc.oracle.apps.xyz.po.PurchaseOrderAM; import oracle.apps.fnd.framework.webui.OAControllerImpl; class updatePo extends OAControllerImpl { public void processRequest(pageContext, webBean) { PurchaseOrderAM am = (PurchaseOrderAM)pageContext.getApplicationModule(webBean); am.initQuery(123, “POSTED”); } }
Now, this is probably bread-and-butter stuff for a Java expert, but for anyone coming from a traditional Forms, Reports and PL/SQL background, this sort of thing is not obvious! Despite the title of this article, I’m not saying that this is the “right” way to do things, it’s just one approach, that I personally quite like.
Comments are now closed... Please contact us if you have any queries.