Timer Task in ADF

July 28th, 2010 | Posted by in Oracle ADF | 17 Comments

On some projects, you find yourself in need to execute certain jobs and tasks at a precise moment in time or at regular time intervals. So, the topic of this article is to present the mechanism to include a timer task in ADF.

The scheduling is done with the help of the CommonJ API which is standard in Weblogic. This example works perfectly in a managed node and there is an alternative to do the same in a Weblogic Cluster. In order to include timer task scheduling feature in ADF, the first step is to properly configure TimerManager in web.xml like this:

<resource-ref>  
        <res-ref-name>tm/TimerManager</res-ref-name>  
        <res-type>commonj.timers.TimerManager</res-type>  
        <res-auth>Container</res-auth>  
        <res-sharing-scope>Unshareable</res-sharing-scope>  
</resource-ref>

The code to look this up would be:

            InitialContext ic = new InitialContext();  
            TimerManager tm = (TimerManager)ic.lookup("java:comp/env/tm/TimerManager"); 

The next step is to add a servlet which will start this TimerManager and its jobs. Important that the servlet is automatically started when the webapp is initialized.

<servlet>
         <display-name>timer</display-name>
         <servlet-name>timer</servlet-name>
         <servlet-class>ro.gebs.demo.TimerServlet</servlet-class>
         <load-on-startup>100</load-on-startup>
</servlet>
<servlet-mapping>
         <servlet-name>timer</servlet-name>
         <url-pattern>/timer</url-pattern>
</servlet-mapping>

There are two different scheduling method groups in the Timer class: scheduling with fixed delay (schedule()) and scheduling with fixed rate (scheduleAtFixedRate()). When you are using methods from the first group, every delay in the task execution will be propagated to the subsequent task executions. In the latter case, all subsequent executions are scheduled according the time of the initial task execution, hopefully minimizing this delay.

Next I’ll present the code for the timer servlet which executes one batch for a task initiated every day, starting immediately.

            InitialContext ic = new InitialContext();  
            TimerManager tm = (TimerManager)ic.lookup("java:comp/env/tm/TimerManager");  
   
            Timer batchRunTimer = null;  
            Boolean batchRunTimerIsRunning = false;
   
            // Execute timer every day, starting immediatelly
            batchRunTimer = tm.schedule(new Batch(), 0, 24 * 60 * 60 * 1000);  
            batchRunTimerIsRunning = true;  
   
            config.getServletContext().setAttribute("batch",batchRunTimer);  
            config.getServletContext().setAttribute("batchRunning",batchRunTimerIsRunning); 

Here is an example of a batch job. The timerExpired method is fired every time when the job time has passed and when the job is canceled the timerCancel method is called.

public class Batch implements Serializable, TimerListener, CancelTimerListener {

    public void timerExpired(Timer timer) {
         System.out.println("Batch timer expired called on " + timer);
    }
    public void timerCancel(Timer timer) {
         System.out.println("Batch timer cancelled called on " + timer);
    }
}

Here is the demo project and the console output for a timer task called at each 10 seconds with its batch job to display a specific message.

Tags: ,

17 Responses to “Timer Task in ADF”

  1. Corneliu says:

    Hi,

    Is there a scheduler task component that can be started and stopped from a page in ADF? So to have like a thread(java) that does something every 30 sec for example and it can be stopped from page at user request?

    Regards Corneliu

    • Simina Dorin says:

      Hi Corneliu,

      You have to do your own management in a backing bean for your page to start/stop a scheduler task.
      E.g. when pressing a button start doing something every 30 sec, and when pressing another button stop the process.

      Best regards,
      Dorin

    • Corneliu says:

      Hi,

      Thanks for the quick reply. I need some timer task that is not “binded” to the application, and I need more tasks not only one. Any user of my application will be able to start then stop one or more timer tasks that will execute something every 30 sec. Do you think that what you described above is suitable for me?

      Regards
      Corneliu

  2. Simina Dorin says:

    Hi,

    Another option is using Quartz. See the following links if it’s what you are looking for:
    http://amithmit.blogspot.com/2011/01/integrating-quartz-scheduler-with-j2ee.html
    http://www.ibm.com/developerworks/library/j-quartz/index.html#download

    Best regards,
    Dorin

  3. Herman says:

    Can you upload a sample project?

  4. Horatiu says:

    Salut,

    Legat de timers/threads, am un blocking-point si am zis sa-ti cer ajutoru…Eu incerc de pe un buton sa pornesc un thread care sa opereze pe baza de date (in background – folosind AppModule). In clasa care extinde Thread in metoda run() iau anumit ViewObject din AM…si cand rulez nu-mi gaseste AppModule..deci nu se poate conecta la db. Daca continutul metodei run() il pun direct pe buton (adica cand apas sa faca ce face threadul..atunci merge). Am incercat si din task-flow method call..toate bindingurile is acolo si pe pagine….is cam blocat :) Ai ceva hint cum pot sa creez un thread care sa modifice db…si totusi in gui sa vad schimbarile automat. Threadul tre sa mearga in background all-the-time. Orice hint va fi apreciat.

    Mersi,
    Horatiu

  5. Simina Dorin says:

    Exista 2 variante pe care le poti incerca:

    1.BindingContext bc = (BindingContext)ADFContext.getCurrent().getSessionScope().get(“data”);
    DCDataControl dc = bc.findDataControl(“XYZAMDataControl”);
    XYZServiceAMImpl am =
    (XYZServiceAMImpl )dc.getDataProvider();
    2.
    public static ApplicationModule getApplicationModule() {
    return oracle.jbo.client.Configuration.createRootApplicationModule(“ro.gebs.model.service.XYZServiceAM”,
    “XYZServiceAMLocal”);
    }

    public static void releaseApplicationModule(ApplicationModule appModule) {
    oracle.jbo.client.Configuration.releaseRootApplicationModule(appModule, true);
    }

  6. Horatiu says:

    Mersi mult.
    Prima varianta nu merge in cazul asta (eu am incercat cu get(“bindings”) inainte…dar se pare ca nu merge nici asa…).

    Iar a doua….am mai vazut-o pe net..dar am gasit ca sa nu o folosesti din backing-beanuri..ca ii cam pacatoasa…si in cazul asta merge :)

    Inca o intrebare..daca am mai multe threaduri..se poate ca folosind acest mecanism + release la AppModule (din varianta a 2-a) sa apare probleme in caz de modificari/stergeri + commit?

    Multam fain!

  7. Simina Dorin says:

    De unde incerci sa folosesti prima varianta?…e un proiect in cadrul aplicatiei diferit de Model sau e alta aplicatie unde ai inclus jar-ul pentru model ca si dependinta.

  8. Horatiu says:

    Dintr-o pagina din ViewControler in acelasi proiect.
    Pe un buton fac call la o metoda din backing bean…care care porneste un thread:
    TestThread test = new TestThread();
    test.start();

    TestThread extends Thread{

    run() {
    ….aici apelez prima varianta cu BindingContext
    }

    si imi returneaza NullPointer aici:
    DCDataControl dc = bc.findDataControl(“AppModuleAMDataControl”);

    Oare tre sa implementez ceva special in AppModuleAMImpl class(care am generat-o default din AppModuleAM).

    Mersi

  9. Simina Dorin says:

    De ce nu incerci sa-ti faci un timer task si in cadrul lui sa iti pui logica ca asa stiu ca ai acces la DataControl. Dintr-o clasa care exinde Thread nu am incercat.

  10. Horatiu says:

    O sa incerc…oare backing-beanul trebuie sa aiba session scope (sau application scope daca ii un timer care ruleaza tot timpul)? La mine acum ii request? Poate fi asta problema.

    Mersi

  11. Simina Dorin says:

    Nu cred ca influenteaza cu nimic…poate sa ramana pe request daca e folosit one time sau pe session si va fi activ pe perioada intregii sesiuni.

  12. Horatiu says:

    Salut,

    Inca o intrebare ca nu reusesc sa folosesc prima ta varianta:
    1.BindingContext bc = (BindingContext)ADFContext.getCurrent().getSessionScope().get(“data”);
    DCDataControl dc = bc.findDataControl(“XYZAMDataControl”);
    XYZServiceAMImpl am =
    (XYZServiceAMImpl )dc.getDataProvider();

    Trebuie pe backing-bean sa setez un managed-property:

    data
    ????
    #{data}

    Si daca da…ce clasa tre sa pun la #{data}..aici ???
    Si trebuie in backingBean sa am private ??? data cu getters+setter si mai tre sa setez cumva in setter data…sau se face automat pe baza property-ului setat anterior?

    Mersi de suport.

  13. Simina Dorin says:

    Incearca si cu:
    BindingContext bindingContext = BindingContextFactory.create( “DataBindings.cpx” ) ;
    DCDataControl dataControl = bindingContext.findDataControl( “GRIPServiceDataControl” ) ;
    ApplicationModule appModule = dataControl.getApplicationModule() ;

  14. Horatiu says:

    Si ultima varianta ii clever…;)
    La mine problema era ca atat Thread cat si Timer nu aveau acces la BindingContext!!!…si de aia nu mergea prima varianta. A trebuit sa il setez prin intermediul beanului…pentru ca aveam nevoie sa fie updatate si tabelele automatic pe GUI atunci cand threadul modifica informatia in spate.

    10x a lot.

    Sa ai un weekend fain!

Leave a Reply