1

I'm honestly missing something here. I have no idea how to make an instance of TimerService object. It is always null. I don't have a constructor because it's an Interface. I can't use the create methods. @Resource doesn't seem to allocate anything to it.

I'm trying to setup a simple programmatic timer that does a task every X minutes. The timeout duration can vary based on configuration which can change throughout runtime. I am using a WebLogic 12 web application.

What I have so far:

import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;

@Singleton
public class TimerBean {
    @Resource
    protected TimerService timerService;

    public TimerBean(){
        System.out.println("TimerBean constructor " + timerService);
    }

    @Timeout
    public void timeoutHandler(Timer timer){
        String name = timer.getInfo().toString();
        System.out.println("Timer ticked. Name=" + name);
    }


    public void startOrModifyTimer(long initialExpiration, long interval, String name) {
        System.out.println("Start or modify " + timerService);
    }
}

This outputs:

TimerBean constructor null

& then after the server is running if I call start or modify:

Start or modify null


edit: I got it to work by making the TimerBean @Singleton & @Startup & replacing constructor with @PostConstruct method.

however while it has an object for TimerService instantiated whenever I try to use its methods it gives me java.lang.IllegalArgumentException: Unknown bean state 0 for which there is no information...

2778
  • 814
  • 1
  • 11
  • 32

3 Answers3

2

If you're trying to use field injection, you're relying on the framework to come along after the object has already been instantiated and set the field, so it will always be null in the constructor. You can either do whatever logic you need in an @PostConstruct method or, my strong preference, inject the TimerService as a constructor argument instead of directly into the field.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • `@PostConstruct` is never called. `@Inject` is not either (and Im unsure if I did it right). Furthermore when I do a requets to the server it calls startOrModify and startOrModify ALSO sees timerService as null. – 2778 Mar 05 '15 at 14:53
  • Are you calling `new TimerBean()`? – chrylis -cautiouslyoptimistic- Mar 05 '15 at 14:55
  • See my discussion at http://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null, which is for Spring, but the principles apply to any container injection. Note that constructor injection avoids this problem entirely because you're explicitly passing in the dependency (and this makes unit testing much simpler). – chrylis -cautiouslyoptimistic- Mar 05 '15 at 14:56
  • You should be using injection all the way up. The container itself should be scanning and instantiating your objects (based on either something like Spring's component-scanning or on definitions in XML files). – chrylis -cautiouslyoptimistic- Mar 05 '15 at 15:00
  • Using `@Resource` to instantiate TimerBean in the service class... does not instantiate TimerBean (it was null). I later tried with `@Autowired` instead of `@Resource` for everything; same thing. – 2778 Mar 05 '15 at 15:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/72349/discussion-between-user997739-and-chrylis). – 2778 Mar 05 '15 at 15:20
  • @chrylis I'm struggling to figure out how to inject the TimerService as a constructor argument. I can't inject an `@Resource` as an argument. The only way I have managed to do this is via field injection. Do you know how to inject TimerService as a constructor argument? – Eric B. Oct 11 '17 at 03:11
  • @EricB. I'm only familiar with Spring, not WebLogic, and in the Spring container it Just Works. – chrylis -cautiouslyoptimistic- Oct 11 '17 at 06:15
2

@PostConstruct is never called. @Inject is not either (and Im unsure if I did it right)

I got it to work by making the TimerBean @Singleton & @Startup & replacing constructor with @PostConstruct method.

chrylis is right. From your description it looks like you instantiating TimerBean via constructor.

Result is that you manage life-cycle by yourself and container is not able to take care of this instance and inject anymore.

Inject your TimerBean into the class where you want to use it (Session for example), or use it as you did:

@Singleton
@Startup
public class TimerBean { .. } 

Combination of these annotations basically create one instance of TimerBean during app server start sequence.

Btw. Constructor with @PostConstruct is wrong idea and it may behave really unpredictable during run-time (not sure if it is possible, but you creating circular instantiation with this combo).

Community
  • 1
  • 1
Petr Hunka
  • 312
  • 5
  • 11
1

I ended up using Timer & TimerTask for this. Couldn't figure TimerService out. Oh well. Seems to work fine.

For anyone curious:

    long interval = minutes*60*1000;
    long delay = interval;

    if(prevTask != null){
        delay = System.currentTimeMillis() - prevTask.scheduledExecutionTime(); //time left of previous setting
        prevTask.cancel();
        delay = interval - delay; //difference in time left & new interval
        if(delay <=0) //if by new setting should've already ran, so run it ASAP...
            delay = 2000;

        logger.info(String.format("DB dump was already scheduled before. Set dump delay to %s minutes & setting new schedule to every %s minutes.", delay/60/1000, minutes));
    }

    TimerTask task = new TimerTask(){
        private SimpleDateFormat ft = new SimpleDateFormat("yyyy.MM.dd 'at' HH:mm:ss SSS");
        private int minutes;
        public TimerTask initialize(int minutes){
            this.minutes = minutes;
            return this;
        }
        public void run() {
            try {
                logger.info(String.format("Doing scheduled %s dump to DB. (Configured to occur every %s minutes.)", ft.format(new Date(this.scheduledExecutionTime())), minutes));
                dumpToDB();
            } catch (NamingException | SQLException e) {
                e.printStackTrace();
            }
        }
    }.initialize(minutes);

    timer.schedule(task, delay, interval);
    prevTask = task;
2778
  • 814
  • 1
  • 11
  • 32
  • Bad answer, as the real problem was the OP didn't understand how "beans" work in Java EE. So, whenever you use an annotation such as `@Resource`, `@Inject`, `@PersistenceContext`, etc. it's supposed to be done in a *managed* bean, meaning that instances of the bean class can *only* be instantiated by the Java EE container, *not* by application code; ie, you can't do `new MyBean()` and expect its `@Resource` fields to be set. – Rogério Oct 20 '16 at 16:18