我需要在我的网络应用程序中安排任务。该任务需要使用在部署期间初始化的Servlet
的成员字段。我使用了EJB @Schedule
。但是,当触发任务时,成员字段为空。我想原因在于我必须将@Stateless
注释添加到servlet以使@Schedule
工作,而我的Servlet确实需要保留其状态?
如果是,我怎样才能以简单有效的方式解雇我的任务?使用GlassFish 3
以下是我的代码的快照
@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
}
修改
Java EE教程说:
企业bean容器的计时器服务使您可以为除有状态会话bean之外的所有类型的企业bean计划定时通知
所以我的结论是这种方式不适合用于Servlet
。
编辑2
启动CometD Bayeux服务需要servlet:请参阅here原因。 MyMemberField表示一个包装类的唯一实例,它关注与代理的API的交互(这是一个交易应用程序)。此包装类实例在所有会话和用户中必须是唯一的。我在Servlet的init()
上初始化它。
此包装类将请求发送到代理并接收异步答案。也许最好在Bayeux配置器之外定义这个类,但我不知道如何定义它。作为servlet?作为托管Bean?
最重要的是,我需要使用调度程序将调度消息发送给代理。因此,计划任务应该知道代理的包装器类实例。
答案 0 :(得分:4)
当您注释servlet @Stateless时,您已经创建了两个JavaEE组件:
当servlet处理初始请求时,servlet中的实例字段被初始化。当@Schedule运行时,EJB的实例字段被初始化为不同的东西。
我对如何解决问题的建议取决于实例字段中存储的数据。它是应用程序范围的初始化数据吗?如果是这样,那么我将使用@PostConstruct创建一个单独的@Singleton类来初始化实例数据,然后将@Schedule移动到该类。它是依赖于请求的数据吗?如果是这样,那么我将使用TimerService.createCalendarTimer并通过TimerConfig的info参数将数据传递给timer方法。
顺便说一句,如果您不需要保证计时器在应用程序停止时“赶上”或者JVM崩溃,那么您可能需要考虑使用非持久性计时器。
答案 1 :(得分:1)
@Singleton
public class FooTask {
private Foo foo;
@PostConstruct
public void init() {
foo = new Foo();
foo.initialize();
}
@Schedule(dayOfWeek = "Mon-Fri", hour = "21", minute = "59", second = "55")
public void run() {
System.out.println(foo);
// ...
}
public Foo getFoo() {
return foo;
}
}
如果由于某种原因希望能够在每个servlet请求上访问foo
,那么您应该将其作为@EJB
注入到servlet中。
@WebServlet("/foo/*")
public class FooServlet extends HttpServlet {
@EJB
private FooTask fooTask;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(fooTask.getFoo());
// ...
}
}