I need to schedule a task in my web application. The task needs to use a member field of the Servlet
that is initialized during deployment. I have used the EJB @Schedule
. However when the task is fired, the member field is null. I guess that the cause lies in the fact that I had to add the @Stateless
annotation to the servlet in order to make the @Schedule
work, and my Servlet needs to preserve its state indeed?
If yes, how can I fire my task in a simple and efficace way? Using GlassFish 3
Here is a snapshot of my code
@Stateless // <-- Wrong ??
public class myServlet extends GenericServlet {
private MemberField myMemberField = new MemberField();
@Override
public void init() throws ServletException {
myMemberField.initialize();
}
@Schedule(dayOfWeek = "Mon-Fri", hour = "21", minute = "59", second = "55")
public void myTask() {
System.out.println(myMemberField.toString());
}
// other stuff
}
EDIT
The Java EE Tutorial says:
The timer service of the enterprise bean container enables you to schedule timed notifications for all types of enterprise beans except for stateful session beans
So my conclusion is that this way is not suitable for being used in a Servlet
.
EDIT 2
The servlet is necessary for starting the CometD Bayeux service: see here why. MyMemberField represents the unique instance of a wrapper class that cares of the interaction with the API of the broker (this is a trading application). This wrapper class instance must be unique across all sessions and users. I initialize it on init()
of the Servlet.
This wrapper class sends requests to the broker and receives asynchronous answers. Maybe it's better to define this class outside the Bayeux configurator, but I don't know how to define it. As a servlet? As a Managed Bean?
On top on it, I need to work with a scheduler in order to send scheduled messages to the broker. So, the scheduled task should be aware of the broker's wrapper class instance.
@bkail hit the nail on the head. You're mixing the Servlet and EJB concepts. Split them into two separate classes.
If you want to be able to access
foo
on every servlet request for some reason, then you should inject it as@EJB
in your servlet.When you annotate a servlet @Stateless, you have created two JavaEE components:
When the servlet handles the initial request, the instance field in the servlet is initialized. When the @Schedule runs, the instance field of the EJB is initialized to something different.
My recommendation for how to resolve the problem depends on what data is stored in the instance field. Is it application-wide initialization data? If so, then I would create a separate @Singleton class with an @PostConstruct that initializes the instance data, and then move the @Schedule to that class. Is it request-dependent data? If so, then I would use TimerService.createCalendarTimer and pass the data to the timer method via the info parameter of the TimerConfig.
As an aside, if you don't need a guarantee that the timer will "catch up" while the application is stopped or if the JVM crashes, then you might want to consider using a non-persistent timer.