1 2 Previous Next 23 Replies Latest reply on Oct 23, 2007 7:38 AM by damatrix

    Starting up Asynchronous Method at startup

    damatrix

      Hi,

      I've been trying to get an asynchronous process to run immediately after startup of the application to automatically send email notifications. On a previous post someone recommended annotating an APPLICATION scope component method with @Observer("org.jboss.seam.postInitialize").

      However i find that the method is still not called after startup. Am i missing something. Below is the Applicaition Scope component and the component with the asynchronous method.

      @Scope(ScopeType.APPLICATION)
      @Name("tradewindsMonitor")
      public class TimerMonitor implements java.io.Serializable{
      
       @In(create=true)
       TradewindsMonitorBean timerHandler;
      
       @Observer("org.jboss.seam.postInitialize")
       public void startTimer(){
       Calendar calendar = Calendar.getInstance();
       calendar.add(Calendar.MINUTE,2);
       timerHandler.sendNotifications(calendar.getTime(),new Long(86400000));
       }
      


      @Name("timerHandler")
      public class TradewindsMonitorBean implements java.io.Serializable{
      
       @In(create=true)
       private Timer timer;
      
       @In(create = true)
       private MailRenderer mailRenderer;
       @In
       private EntityManager entityManager;
      
       @Asynchronous
       public Timer sendNotifications(@Expiration java.util.Date date, @IntervalDuration Long duration) {
       ...
       return timer;
      }
      


        • 1. Re: Starting up Asynchronous Method at startup
          pmuir

          Which version of Seam?

          • 2. Re: Starting up Asynchronous Method at startup
            modoc

            You can always use @Startup on the class and @Create on your method. I know this works.

            Is there an advantage to using the postInitialize observer annotation instead?

            • 3. Re: Starting up Asynchronous Method at startup
              damatrix

              I'm using Seam 2.0.Beta and JBoss 4.2.1.GA.

              I tried using @Startup and @Create but i realised that the contexts were not yet up and active at that stage of the application's startup. Putting my message in any context to be rendered by my email template throws an NPE at these stages.

              • 4. Re: Starting up Asynchronous Method at startup
                gbc1

                Works like this:

                In components.xml:

                <?xml version="1.0" encoding="UTF-8"?>
                <components xmlns="http://jboss.com/products/seam/components"
                 ...
                 xmlns:async="http://jboss.com/products/seam/async"
                 ...>
                
                 <async:quartz-dispatcher/>
                ...
                


                Startup class:
                @Startup
                @Stateful
                @Scope(ScopeType.APPLICATION)
                @Name("startupManager")
                public class StartupManager implements StartupManagerLocal{ // create Interface
                
                 @Logger
                 private Log log;
                
                 @In(create = true)
                 private ProcessingActionLocal processingAction;
                
                 private QuartzTriggerHandle handle = null;
                
                 @Create
                 public void create() {
                 log.info("create(): called");
                 // init
                 int delay = 20; // in seconds
                 long interval = 1000 * delay;
                 Calendar cal = Calendar.getInstance();
                 cal.setTime(new Date());
                 cal.add(Calendar.SECOND, delay);
                 // create Processing
                 long processingId = processingAction.createProcessing("serverJob");
                 // schedule job and save handler
                 this.handle = processingAction.doServerJob(processingId, cal.getTime(), interval);
                 log.info("create(): id: #0", processingId);
                 }
                
                 public QuartzTriggerHandle getHandle() {
                 return this.handle ;
                 }
                
                 @Remove
                 public void destroy() {
                 }
                }
                


                The ProcessingAction (The ProcessingBean is not shown here):
                @Stateful
                @Name("processingAction")
                public class ProcessingAction implements ProcessingActionLocal { // create Interface
                
                 @Logger
                 private Log log;
                
                 @In
                 private EntityManager entityManager;
                
                 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
                 public long createProcessing(String string) {
                 // create Processing
                 ProcessingBean processing = new ProcessingBean();
                 // managed context
                 entityManager.persist(processing);
                 processing.setName("serverJob");
                 // return id
                 return processing.getId();
                 }
                
                 @Asynchronous
                 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
                 public QuartzTriggerHandle doServerJob(long processingId, @Expiration Date date, @IntervalDuration Long interval) {
                 log.info("doServerJob(): called");
                 // get bean
                 ProcessingBean processing = entityManager.find(ProcessingBean.class, processingId);
                 // if exists remove
                 if (processing != null) {
                 entityManager.remove(processing);
                 log.info("doServerJob(): removed: id: #0", processingId);
                 } else {
                 // else stop server job
                 try {
                 startupManager.getHandle().cancel();
                 } catch (Exception e) {
                 log.error("", e);
                 }
                 log.info("doServerJob(): stopped: id: #0", processingId);
                 }
                 // return new handle
                 return new QuartzTriggerHandle("serverJob");
                 }
                
                 @Remove
                 public void destroy() {
                 }
                }
                


                Let me know if it worked out...

                Greetz GHad

                • 5. Re: Starting up Asynchronous Method at startup
                  damatrix

                  Hi,
                  I tried your approach but had a problem which i expected will occur. Whiles seam is starting up, the contexts are not yet active. Since my email is made up of facelets templates, i could only send data to them when contexts are alive.

                  However i finally sorted it out.

                  
                  @Stateful
                  @Scope(ScopeType.APPLICATION)
                  @Name("startupManager")
                  public class StartupManager implements StartupManagerLocal{ // create Interface
                  
                  
                  
                   @Create
                   public void create() {
                   TimerHandlerLocal timerHandler = (TimerHandlerLocal)Component.getInstance(TimerHandler.Class);
                   timerHandler.sendNotifications;
                   }
                  
                  
                   @Remove
                   public void destroy() {
                   }
                  }
                  



                  The TimerHandler class:

                  @Stateful
                  @Name("timerhandler")
                  public class TimerHandler implements TimerHandlerLocal { // create Interface
                  
                  
                   @In
                   private EntityManager entityManager;
                  
                   @In
                   private MailRender mailRenderer;
                  
                   @Asynchronous
                   @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
                   public QuartzTriggerHandle createProcessing(String string) {
                   List<Customer> customers = entityManager.createQuery("Select c
                   from Customer where c.notification=true").getResultList();
                   Context.getSessionContext().set("customers",customers);
                   mailRenderer.sendNotification();
                   Contexts.getSessionContxt().set("customers",null);
                  
                   return new QuartzTriggerHandle("notifications");
                   }
                  


                  Lastly, find somewhere to inject the StartupMonitor so that it starts up on its own. I think your login/authentication class is the best place.


                  @Scope(ScopeType.SESSION)
                  @Name("authenticator")
                  public class Authenticator implements java.io.Serializable {
                  
                  @In(create=true)
                  StartupMonitor startupMonitor
                  ...
                  }
                  


                  Since the StartupMonitor is APPLICATION scoped, it's @Create method will be called when the first person logs into the application and will run until applications is redeployed.



                  • 6. Re: Starting up Asynchronous Method at startup
                    wiberto

                    I tried this approach and it works great. the only problem I'm seeing is that if I redeploy the EAR where the application is at after the AS is running I get a SAX error:

                    Caused by: org.jboss.xb.binding.JBossXBException: Failed to create a new SAX parser
                     at org.jboss.xb.binding.parser.sax.SaxJBossXBParser.<init>(SaxJBossXBParser.java:96)
                     at org.jboss.xb.binding.UnmarshallerImpl.<init>(UnmarshallerImpl.java:55)
                     at org.jboss.xb.binding.UnmarshallerFactory$UnmarshallerFactoryImpl.newUnmarshaller(UnmarshallerFactory.java:96)
                     ... 72 more
                    Caused by: javax.xml.parsers.ParserConfigurationException: Feature 'http://apache.org/xml/features/xinclude' is not recognized.
                     at org.apache.xerces.jaxp.SAXParserFactoryImpl.newSAXParser(Unknown Source)
                     at org.jboss.xb.binding.parser.sax.SaxJBossXBParser.<init>(SaxJBossXBParser.java:92)
                     ... 74 more
                    


                    If I do a clean load of the application by starting the AS then it works fine.

                    Any idea why? Is it trying to use a different SAX parser after it has started?

                    Thanks,

                    Jose


                    • 7. Re: Starting up Asynchronous Method at startup
                      damatrix

                      I'm no expert at this but i dont think that the problem is related to the startup of asynchronous methods.

                      SAX exception should be related to some xml file parsing exception, shouldn't it?

                      • 8. Re: Starting up Asynchronous Method at startup
                        nickarls

                        I have a similar problem, I need a thread to run with a certain interval, regardless of if someone is logged in or not.

                        I can't get a reaction to postInitialize, @Startup works but when I try to reference the asynchronous bean I get a "Could not instantiate Seam component: org.jboss.seam.async.dispatcher" because "javax.naming.NameNotFoundException: X not bound" (X is the root of the ejb jndi-context?)

                        Tried various combinations of pojo and ejb for the asynchronous bean and I have <async:timer-service-dispatcher/> in my components.xml. Also tried various dependecies on the @Startup. Tried injecting the asynchronous bean, tried getting it from Components.

                        With and without chanting and waving a dead chicken across the keybord.

                        • 9. Re: Starting up Asynchronous Method at startup
                          nickarls

                          Now accepting applicants for saving my day ;-)

                          Who can e.g paste in an example that starts up automagically and writes "ping" to System.out every second?

                          • 10. Re: Starting up Asynchronous Method at startup

                            Here's what works for me (no EJB3 - plain POJO, Quartz and Tomcat, all deployed as WAR):

                            @Name ("scheduler")
                            @Scope (ScopeType.APPLICATION)
                            @Startup
                            public class Scheduler {
                             @In
                             private IService service;
                             @Logger
                             private Log log;
                             // set in seam.properties via scheduler.reminderCron
                             private String reminderCron;
                            
                             public void setReminderCron(String reminderCron) {
                             this.reminderCron = reminderCron;
                             }
                             @Create
                             public void startJobs() {
                             log.info("Reminder task started on #0, cron schedule is #1",
                             new Date(), reminderCron);
                             service.sendReminders(reminderCron);
                             }
                            }


                            public interface IService extends Serializable {
                             void sendReminders(String cron); // other methods are omitted
                            }


                            @Name ("service")
                            @Scope (ScopeType.CONVERSATION)
                            @AutoCreate
                            @Transactional
                            public class Service implements IService {
                             @Asynchronous
                             public void sendReminders(@IntervalCron String cron) {
                             log.info("Reminder task activated on #0", new Date());
                             // plus whatever other functionality you need here...
                             }
                            }
                            


                            The scheduler starts with the app, and runs every morning (as per cron expression, which you can change as needed)

                            • 11. Re: Starting up Asynchronous Method at startup
                              nickarls

                              Thanks, I'll declare you the winner. Adding Quartz as a dependency isn't that bad considering it is working ootb...

                              • 12. Re: Starting up Asynchronous Method at startup
                                damatrix

                                I find your solution interesting.

                                However i need the contexts (mostly session and/or conversation) to be fully loaded so that i can pass data to them to be rendered by a facelet during email sending. Does your solution assume that the contexts will have been loaded by the time your method gets called?

                                • 13. Re: Starting up Asynchronous Method at startup

                                  What do you mean by "fully loaded"? Since the startup class in ASavitsky example is managed by Seam, I would assume that by the time execution begins all your contexts will be available to you.

                                  • 14. Re: Starting up Asynchronous Method at startup

                                    Application context would be initialized, but since this is an async invocation, there'd be no session or conversation context available (and, most likely, no HttpSession active, either), thus no Seam-email rendering. I believe there's a JIRA issue addressing just that (making FacesContext available for async calls) - until then, I switched temporarily to regular JavaMail

                                    1 2 Previous Next