1

I'm getting a NullPointerException when trying to create a Timer. Obviously the TimerService is not getting set, but my question is, why? As far as I can tell I'm following the exact pattern used by another class in our application that does work.

Note: I'm new to EJB so explanation is helpful

@Stateless
public class MyClass implements SomeInterface
{
   private static final Logger ourLogger = new Logger(MyClass.class);

   private volatile Timer timer = null;
   private volatile static MyClass instance = null;

   @javax.annotation.Resource
   TimerService timerService;


   @Override
   public synchronized void myMethod()
   {
      resetTimer();
      //do other stuff
   }


   /**
    * Creates the timer.
    */
   @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
   private synchronized void resetTimer()
   {
      if (timer != null)
      {
         timer.cancel();
         timer = null;
      }
      //NullPointerException on next line:
      timer = timerService.createTimer(30000, "My Note");
   }

   public static MyClass getInstance()
   {//MDB calls this, then runs instance.myMethod
      if (instance == null)
      {
         instance = new MyClass ();
      }

      return instance;
   }


   @Timeout
   public synchronized void timeout()
   {
       //Do some stuff
       instance = null;
   }

}
StormeHawke
  • 5,987
  • 5
  • 45
  • 73
  • Do you have an ejb declaration for `TimerService`? – Sotirios Delimanolis Sep 18 '13 at 19:22
  • 1
    Despite the whole problem with injection - 1. why do you hold a state (store a timer) in stateless EJB? 2. Why do you synchronize a thread-safe EJB? And finally - why do you treat private method as a business method? How do you even invoke this bean and know the `TimerService` is not set? – Piotr Nowicki Sep 18 '13 at 19:24
  • @SotiriosDelimanolis we have another class that follows this pattern that injects a TimerService in the same manner – StormeHawke Sep 18 '13 at 19:27
  • @PiotrNowicki I'm calling this from a MessageDrivenBean. This is only part of the class. I store the timer so that it can be canceled and reset. the resetTimer() method is run from another method that is not shown for brevity's sake. – StormeHawke Sep 18 '13 at 19:28
  • The reason why I'm asking how do you invoke this bean is that you might use it not as an EJB but as a regular class so the `TimerService` is not injected. That's first part. Secondly - if you store anything in a SLSB you cannot be sure that the next time it will be in the same state you left it - therefore you really shouldn't do it. – Piotr Nowicki Sep 18 '13 at 19:30
  • @PiotrNowicki Suggested alternatives to get the same end result are welcome. I need a resettable timer that, when it runs out, triggers a method – StormeHawke Sep 18 '13 at 19:32
  • Show us more code - e.g. what business methods do you have in your EJB (you didn't show even one in your example.) Secondly, show us how do you invoke business method of this EJB from MDB. Thirdly - if you want to hold a state - use stateful EJB for this. – Piotr Nowicki Sep 18 '13 at 19:34
  • @PiotrNowicki - using a stateful EJB necessarily prevents the injection of a TimerSession (https://community.jboss.org/thread/155940?start=0&tstart=0) – StormeHawke Sep 18 '13 at 19:38
  • Yup, sorry - was too focused on the whole idea of storing a state in stateless EJB that I forgot we're talking about Timers. – Piotr Nowicki Sep 18 '13 at 19:40
  • @PiotrNowicki just posted some additional code, hope that gives you some more context – StormeHawke Sep 18 '13 at 19:43

2 Answers2

3

Additional code you posted adds one more thing to the list of wrong things with this code (which I started in first comment to your question.)

You're trying to implement a Singleton design pattern in SLSB. This is wrong on many levels.

If you need singleton - just use @Singleton EJB. If you're doing it like you posted - you're basically not instantiating an EJB but some regular Java class. No wonder why injection doesn't occur - container doesn't know anything about the instance you're creating in your method.

If you want to have an EJB - just give it a default constructor and use it from your MDB with @EJB. Then the container will instantiate it and give it the EJB nature (with all those management lifecycle, pooling, dependency injection, etc.)

To sum up you can find the list of wrong things in the code you've posted:

  • you're holding a state in a stateless EJB,
  • you're implementing a singleton pattern into pooled-by-specification EJB,
  • you're synchronizing already thread-safe (also by-specification) EJB,
  • you're adding @TransactionAttribute to private method; I'm not even sure if this have any sense. @TransactionAttribute is a sign that you're defining a business method. At the same time this method is private which means it cannot be a part of the business interface so it is not a business method.

Hope this will help you!

Piotr Nowicki
  • 17,914
  • 8
  • 63
  • 82
  • What you're saying makes sense, but now that I've changed it to `@Singleton` and put an `@EJB` field in my MDB, my MDB is no longer receiving messages. No exceptions, just nothing happens when I send my JMS message. What "gotchas" might I be running into? – StormeHawke Sep 18 '13 at 21:25
  • Uhm, it's hard to say from such short description. You can show some more code (especially of the MDB and interaction with your new Singleton bean) or test if it works with `@EJB` and its usage commented out of the MDB and with it enabled. Is it the only thing that changed? – Piotr Nowicki Sep 18 '13 at 21:29
  • See the code under the EDIT note. Probably should have made it a new question, but since you're already familiar with it... – StormeHawke Sep 18 '13 at 21:43
  • On second thought, this should be a new question. I moved the edit to this link: http://stackoverflow.com/questions/18883021/messagedriven-bean-not-receiving-messages – StormeHawke Sep 18 '13 at 21:55
0

In Oracle's Java EE 6 Tutorial, Canceling and Saving Timers:

To save a Timer object for future reference, invoke its getHandle method and store the TimerHandle object in a database

If you want to spare yourself this unpleasant job, another way is to inject into the @Stateless bean a Singleton bean, store the timer instance into a property of the @Singleton bean, and run / cancel the Timer into the Singleton bean.

perissf
  • 15,979
  • 14
  • 80
  • 117