EJB @Schedule问题

时间:2011-10-07 21:45:04

标签: java-ee servlets glassfish ejb scheduled-tasks

我需要在我的网络应用程序中安排任务。该任务需要使用在部署期间初始化的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? 最重要的是,我需要使用调度程序将调度消息发送给代理。因此,计划任务应该知道代理的包装器类实例。

2 个答案:

答案 0 :(得分:4)

当您注释servlet @Stateless时,您已经创建了两个JavaEE组件:

  1. 由webcontainer管理的servlet。 webcontainer很可能会创建一个类的实例来为所有请求提供服务。
  2. 由EJB容器管理的无状态会话bean。 EJB容器很可能会创建该类的多个实例,并将它们集中在一起以处理EJB请求。
  3. 当servlet处理初始请求时,servlet中的实例字段被初始化。当@Schedule运行时,EJB的实例字段被初始化为不同的东西。

    我对如何解决问题的建议取决于实例字段中存储的数据。它是应用程序范围的初始化数据吗?如果是这样,那么我将使用@PostConstruct创建一个单独的@Singleton类来初始化实例数据,然后将@Schedule移动到该类。它是依赖于请求的数据吗?如果是这样,那么我将使用TimerService.createCalendarTimer并通过TimerConfig的info参数将数据传递给timer方法。

    顺便说一句,如果您不需要保证计时器在应用程序停止时“赶上”或者JVM崩溃,那么您可能需要考虑使用非持久性计时器。

答案 1 :(得分:1)

@bkail击中了头部的钉子。您正在混合Servlet和EJB概念。将它们分成两个单独的类。

@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());
        // ...
    }

}