EJB @Schedule 问题

发布于 2024-12-08 12:31:12 字数 1285 浏览 1 评论 0原文

我需要在我的网络应用程序中安排一项任务。该任务需要使用在部署期间初始化的 Servlet 的成员字段。我使用了EJB @Schedule。然而,当任务被触发时,成员字段为空。我猜原因在于我必须向 servlet 添加 @Stateless 注释才能使 @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

servlet对于启动CometD Bayeux服务是必需的:请参阅此处为什么。 MyMemberField 代表包装类的唯一实例,该类负责与经纪商 API 的交互(这是一个交易应用程序)。此包装类实例在所有会话和用户中必须是唯一的。我在 Servlet 的 init() 上初始化它。 该包装类向代理发送请求并接收异步应答。也许最好在 Bayeux 配置器之外定义此类,但我不知道如何定义它。作为一个servlet?作为托管 Bean? 最重要的是,我需要使用调度程序才能将计划的消息发送到代理。因此,计划任务应该知道代理的包装类实例。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夏见 2024-12-15 12:31:12

当您为 Servlet 添加 @Stateless 注释时,您就创建了两个 JavaEE 组件:

  1. 由 Web 容器管理的 Servlet。 Web 容器很可能会创建该类的单个实例来服务所有请求。
  2. 由 EJB 容器管理的无状态会话 Bean。 EJB 容器很可能会创建该类的多个实例,并将它们集中起来处理 EJB 请求。

当servlet处理初始请求时,servlet中的实例字段被初始化。当@Schedule运行时,EJB的实例字段被初始化为不同的东西。

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

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

When you annotate a servlet @Stateless, you have created two JavaEE components:

  1. A servlet managed by the webcontainer. The webcontainer will most likely create a single instance of the class to service all requests.
  2. A stateless session bean managed by the EJB container. The EJB container will most likely create several instances of the class and pool them to handle EJB requests.

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.

半衬遮猫 2024-12-15 12:31:12

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

}

@bkail hit the nail on the head. You're mixing the Servlet and EJB concepts. Split them into two separate classes.

@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;
    }

}

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.

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

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文