Tuesday, April 13, 2010

Invoking ApplicationModule from a servlet using ADF Model

Sometimes you may need to invoke services exposed by an ApplicationModule from a non ADF web application. This blog post discusses an interesting approach for such use cases where the business logic is residing in a servlet and which in turn invokes the methods exposed through the ApplicationModule, during the course of execution.

One obvious solution is to create ApplicationModule on the fly and release it at the end of the request, as shown below. This code will work if your entire transaction gets committed within a single request. In other words, this is ideal for those use cases which does not call for maintaining state across requests from the same client.

try{
   ApplicationModule applicationModule = Configuration.createRootApplicationModule(qualifiedAMDefName, configName);
   //Business logic goes here.......
}catch(Excpetion ex){
   //Handle Error
}finally{
   Configuration.releaseRootApplicationModule(applicationModule, false);
}

Please note that you may need to configure the ServletADFFilter, to ensure the proper initialization of ADFContext while accessing ApplicationModule from a non ADF based servlet (as in the above use case scenario).
This filter reads the configuration from WEB-INF/adf-config.xml and initializes ADFContext. However applications using ADFBindingFilter can skip this filter, as this is already taken care by the binding filter.

What if you need to maintain the conversation state across multiple requests?

This is a bit tricky situation. As you might be updating Entity Objects from separate requests and committing the transaction at the end of the conversation, it's important to pass a session identifier while looking up the ApplicationModule to restore the state. There are set of APIs available which may help you to pass session cookie while creating ApplicationModule. You may need to follow certain system level contracts and APIs to create a 'stateful' ApplicationModule. Needless to say, this approach call for good understanding of the execution path of the binding layer and related APIs to make it working. So let me try explaining yet another interesting approach which may free you from all these hazards.

Using binding layer to access ApplicationModule

Idea is to make use of the binding layer to invoke services(methods) defined at the ApplicationModule. Apparently you may need same set of meta files as used by an ADF Fusion Web application, to automate this process. If you build an ADF Face application, these files are generated by the JDeveloper at the design time. In this specific case, you may need to create them manually(or to avoid the manual error, you may create a dummy project with a page and necessary ApplicationModule method bindings, and copy the generated adfmsrc folder containing the meta files to your Web project).


Now, let us take a step back and see who is responsible for wiring ApplicationModule with view layer in the case of a typical Fusion web application. Fusion applications are configured to use a servlet filter ADFBindingFilter which actually orchestrates the automatic acquisition and release of an
appropriate application module instance based on declarative binding meta-data.
The method ADFBindingFilter.doFilter() sets up the ADF processing state, and the method ADFBindingFilter.initializeBindingContext() creates an instance of oracle.adf.model.BindingContext by reading the CpxFileName init parameter from the web.xml file. So, next step is configuring this binding filter. You are free to subclass ADFBindingFilter, if you want to customize the run time behavior of this binding guy. Add this to web.xml with proper url-pattern, so that let the run time pick up this filter and execute it before running your page/servlet.


<filter>
  <filter-name>CustomBindingFilter</filter-name>
  <filter-class>web.filter.CustomBindingFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>CustomBindingFilter</filter-name>
  <url-pattern>/adf/*</url-pattern>
</filter-mapping>

Servlet code that access ApplicationModule may look like as shown below

BindingContext bindingContext=null;
try {
    bindingContext = BindingContext.getCurrent();
    DCBindingContainer dcBindingContainer =
    bindingContext.findBindingContainer("view_dummyPageDef");
    JUCtrlRangeBinding depts =
    (JUCtrlRangeBinding)dcBindingContainer.findCtrlBinding("DepartmentsView1");
    dcBindingContainer.refreshControl();
    // Business logic goes here...
 }finally {
   // Clean up goes here...
}

Now you can try running your servlet. Please make sure that servlet URL contains the patterns that you configured for the binding filter. For example, please see '/adf/' appearing in the below URL, which result in interception of the binding filter before invoking the servlet.
http://127.0.0.1:7101/AMWithServlet-UI-context-root/adf/sampleservlet

You can download the sample workspace from here. Unzip the project and run the SampleServlet.java

[Runs with Oracle JDeveloper 11g R1 PS1 + HR Schema]

This article inspired by an example from Steve Muench

11 comments:

Norman Stonewall said...

Please note that you may need to configure the ServletADFFilter, to ensure the proper initialization of ADFContext while accessing ApplicationModule from a non ADF based servlet (as in the above use case scenario).


Could you explain this process in more detail? I have been beating my head against the wall trying to find out how to get this to work from a project that does not use ADF.

Jobinesh said...

You may need to add oracle.adf.share.http.ServletADFFilter to the web.xml file(use filter element in web.xml) and provide url-pattern mapping (use filter-mapping element in web.xml) for the same. You can refer the sample attached, which contains mapping for CustomBindingFilter, similar way create mapping for ServletADFFilter. Please see this link to get an idea on web.xml file http://download.oracle.com/docs/cd/E13222_01/wls/docs81/webapp/web_xml.html

Norman Stonewall said...

Thanks for the information and your site. I have read alot of your articles and find them very descriptive and helpful.

Unfortunately I think the way I am trying to use the application module is not possible. We have a class with a main method that is started as a cron job in unix. In the main method I am trying to access the application module without a front end. We have found a work around, however I wanted to keep exploring this solutiong.

Thanks for the great articles.

Jobinesh said...

Thanks Norman!
Have checked the below example(#37) from Steve
http://blogs.oracle.com/smuenchadf/examples/#37
This seems to be an ideal solution for you use case

Norman Stonewall said...

Jobinesh,
Thanks for pointing me in the right direction. We were able to meet our requirements by using a java Timer and Timertask to kick off the jobs. Still it was bothering me that I could not come up with a solution such as the one you suggested.
I was way off base with how I was trying to solve this problem. I was grabbing the JNDI datasource using ORMI and trying to use those connection values to connect a DBTransactionImpl2 and use it to create an application module. It got to a point where ADF didn't even give me an error message, haha.

Once again thank you and I can't wait for your next update.

Term Papers said...

I have been visiting various blogs for my term papers writing research. I have found your blog to be quite useful. Keep updating your blog with valuable information... Regards

Anonymous said...

I use it in Maplet .I got it from here
http://sourceforge.net/projects/shine-enterpris/

Maher said...
This comment has been removed by the author.
Maher said...

i have already a page that have a page definition when i use it is id in my servlet i get this error :
java.lang.NullPointerException
Automatically initializing a DefaultContext for getCurrent.
Caller should ensure that a DefaultContext is proper for this use.
Memory leaks and/or unexpected behaviour may occur if the automatic initialization is performed improperly.
This message may be avoided by performing initADFContext before using getCurrent().

Sašo Celarc said...

Very useful and helpful.
What is equivalent approach for JDev 10g?
In 10g I can't use BindingContext.getCurrent isn't?

Olive Arnold said...

oh... thanks you for your share.


php development