Let’s start explaining the use case:
An ADF Application is storing an Object in the application scope and it needs to be always up to date. The object may be updated by externals sources at any time so it needs to be constantly refreshed to avoid stale data. So, how do you keep up to date this object? We have created a simple application where the ApplicationModule reads the first line of a file and store it in the application scope. This object needs to be updated all the time in order to get the latest data in case the file is modified later on.
In order to achieve the solution, a mix of documentation, blog posts and strategies were used. To start with, we need a job which tasks is to keep the application scope object up to date. For this, we found really useful the following blog entry from Andrejus Baranovskis http://andrejusb.blogspot.com/2013/03/optimizing-long-running-adf-operations.html.
So let’s start with the screenshots:
This is a method in our application module implementation. As I said earlier, it will read the first line of a file and store it in the application scope.
We then created a Context Initializer class which basically will trigger the job to start working as soon as the application is deployed using the TimeManager object;
Next, the class which will be doing the job. The code is self-explanatory. An application module instance is create which then is used to call the syncMessage method to update the application scope object.
We then consume the object in our page as a outputText with the value
We make sure our announcement file has a text on it:
We run the application and once it’s deployed we can immediately see the following messages in the log:
<Info> <ky.oralution.appscoperfrsh.view.contextInit.ContextInitializer> <BEA-000000> <Task created to update announcements> <Info> <ky.oralution.appscoperfrsh.view.jobs.UpdateJob> <BEA-000000> Syncroning Data> <Info> <ky.oralution.appscoperfrsh.model.bc.services.AppModuleImpl> <BEA-000000> <Before: null> <Info> <ky.oralution.appscoperfrsh.model.bc.services.AppModuleImpl> <BEA-000000> <After: Thank you for reading...> Syncronized. Took: 0>
We run the page expecting to see our message with no problem:
No, you are not blind… The message doesn’t appear and if we don’t change the implementation will never appear. Reason is, as we found out later is that the Job, scheduled by the TimerManager, runs in different context. We can see this by adding the highlighted line of code in our applicationModule syncMessage method:
Now, we run the application again. We can see in the log the following entries:
<Info> <ky.oralution.appscoperfrsh.view.jobs.UpdateJob> <BEA-000000> Syncroning Data> <Info> <ky.oralution.appscoperfrsh.model.bc.services.AppModuleImpl> <BEA-000000> <Before: Thank you for reading...> <Info> <ky.oralution.appscoperfrsh.model.bc.services.AppModuleImpl> <BEA-000000> <After: Thank you for reading...> defaultApplicationName
But then, using the UI, we call the syncMessage manually and surprisingly we get our text in the screen;
We now check the log entries;
<Info> <ky.oralution.appscoperfrsh.model.bc.services.AppModuleImpl> <BEA-000000> <Before: null> <Info> <ky.oralution.appscoperfrsh.model.bc.services.AppModuleImpl> <BEA-000000> <After: Thank you for reading...> RefreshAppScopeObjectAutomatically
As you can see, the application names are not the same
defaultApplicationName != RefreshAppScopeObjectAutomatically
At this point, we have two options to go forward and accomplished our task.
- Use coherence (or third-party cache solution) to cache instead the application scope (We tested with coherence and it works just fine!)
- Tweak the implementation so the call to the application module method is made within the context of our application.
Since option 1 is trivial and it works, I decided to keep going and implement the 2nd option as well. This time, we will use the TimerManager to invoke a servlet and do the update from there by using the existing binding layer of the application. For this, we followed the following blog post from Timo Hahn http://tompeez.wordpress.com/2011/12/16/jdev11-1-2-1-0-handling-imagesfiles-in-adf-part-3/. Please refer to it (point 2) to learn more about this approach.
We create the pageDefinition for our sync process and added our method call.
We then proceed to create a servlet and execute from there the syncMessage method we exposed in our dummy page definition.
We need now to register the servlet in our web.xml;
We now update our Context Initializer to pass the servlet url to out modified UpdateJob;
Now we update our UpdateJob class to invoke the servlet:
Now, we all is set to run the application one more time, this time expecting to see the message in screen and expect the value to maintain updated:
Now, we update the message in announcements.txt
Once we save the file, and refresh the browser (after waiting few seconds to give time to the job to run):
The only drawback of this approach that I see is that you need to hardcode the servlet URL. You could use a web.xml init param to store this but still you will need to hardcode it somewhere unless you are willing to implement a complicated way to get the whole URL for the servlet.
We would also like to thank kdario and Cvele_new_account (from OTN Community) for their help through out this solution.