3 Replies Latest reply on Nov 11, 2008 11:45 PM by Sunil Subrahmanyam

    EJB Timer persistence across restarts

    Cloves Almeida Newbie

      I'm using Seam 2.0.0.GA and Jboss 4.2.2.GA.


      Here's the scenario. Whenever certain business operation is complete (ie: a sale is confirmed by the customer) I need my Seam application to warn a remote application about that operation (ie: message the warehouse to ship the product). The remote application is a web-enabled legacy application - the integration done through HTTPS POST.


      In order to ensure reliability, I used an @Asynchronous method that loops every 10 seconds until the POST is sucessfully complete and return HTTP 200 OK. It work as a charm as long as i don't try to restart the server or application. The persisted timers can't be restored, it blows giving this exception:


      18:49:56,388 ERROR [TimerImpl] Error invoking ejbTimeout: javax.ejb.EJBException: java.lang.NullPointerException
      



      I'm using EJB Timers, not Quartz - since they're easier to configure for persistence. I haven't seen a Seam application that depends on persistent timers reloading across server restarts or documentation explaining on how to make this work.


      Below are the classes that can be used to reproduce this.



      @Name
      public class SalesAction {
           @In WarehouseMessenger warehouseMessenger;
           
           public void sendToWarehouse(Sale sale) {
               Long salesId = sale.getId();
               String sku = sale.getSKU();
               int qty = sale.getQuantity();
               String destination = sale.getDestination();
               long interval = 10000;
               
                warehouseMessenger.shipProductMessage(salesId, sku, qty, destination, interval);
           }
           
      }
      
      @Name
      public class WarehouseMessenger {
           @In Timer timer;
           @In EntityManager entityManager;
      
           @Logger Log log;
      
           @Asynchronous
           @Transaction
           public void shipProductMessage(Long salesId, String sku, int qty, String destination, @IntervalDuration interval); {
                String url = "https://remoteserver/warehouse/ship";
                
                /* Post method that uses apache httpclient */
                int status = post(url,salesId, sku, qty, destination);
               
               if (status = 200) {      // HTTP OK
                    Sale sale = entityManager.find(Sale.class,salesId);
                    sale.setWarehouseNotified(true);
                    timer.cancel();
                    log.debug("Warehouse notified of shipping order");
               } else {
                    log.debug("Could not notify warehouse");
               }
           }
           
      }
      



        • 1. Re: EJB Timer persistence across restarts
          Cloves Almeida Newbie

          Just an update, I dropped EJB Timer service and switched for Quartz - by far more reliable and stable. I'll post some of the issues that took me some time to figure.


          1) Do not forget to change your build.xml to: a) place the seam.quartz.properties file into your WEB-INF/classes directory, b) place the quartz-xxx.jar in EAR the lib/ folder,


          2) I needed to setup a JobStoreCMT, instead of the usual JobStoreTX. This enables Quartz to use Container Managed Transactions. Futhermore, since I wanted to use the same datasource for both my application and Quartz, I needed a xa-datasource AND a local-tx-datasource configured in my -ds.xml pointing both to my database.


          3) You cannot access the currently executing job by injecting it (the context variable is timer). To make it happen, I patched the QuartzDispatcher. I created a JIRA issue for the patch:


          So, you can access the QuartzTriggerHandle (in my case to stop it) by injecting the timer context variable:


          @In QuartzTriggerHandle timer;
          



          • 2. Re: EJB Timer persistence across restarts
            Cloves Almeida Newbie

            Oh, another thing.


            When canceling a job, in fact, you're updating your Quartz tables and, because of Container Managed Transactions, you need to encapsulate your method in a transaction. This is easily done in Seam with the @Transaction annotation.

            • 3. Re: EJB Timer persistence across restarts
              Sunil Subrahmanyam Newbie

              Hi,


              I'm trying to setup a JobStoreCMT configuration. Could you post a sample configuration file? I am already using xa-datasource as our application has to connect to 3 databases. I did not understand why you had to setup a local-tx-datasource pointing to the same database. Could you also explain this?


              Thanks for your help.