Java EE WebApp + icefaces中的长时间运行任务

我对Java EE知之甚少,但目前正在学习它。

我想出了一个涉及用户调用的长时间运行任务(最多几分钟)的项目。 该任务包括几个步骤。 当然我想向用户展示进度。

该项目使用Java EE与JPA,JSF和Icefaces。 它运行在Glassfish上。

一位经验丰富的同事向我提出了以下模式:

  1. 创建一个无状态的异步EJB,它创建一个响应对象并处理请求
  2. 在每个步骤后保留响应对象
  3. 在辅助bean中,查询并显示响应对象

这很好用。 我唯一的问题是更新状态站点以镜像进度。 目前,我每隔x秒就会重新加载一个简单的JavaScript页面。

你知道一种方式/模式来反映从无状态ejb到jsf支持bean的当前步骤吗? 或者,我更喜欢这样,你知道一种每隔x秒查询一次支持bean值的方法吗?

编辑:

我知道Icefaces推送机制,但我希望状态更新站点与计算EJB分离,原因如下:

  • 可能已经销毁了支持bean,因为用户离开了站点并稍后返回以获取结果
  • 一个用户可能存在多个会话,因此可能存在多个bean
  • 设计简洁

有几种选择可以传回这些信息。 如果EJB存在于同一个JVM中,您也可以使用某些单例映射并在某些键下存储进度(会话ID)

如果不是这种情况,您将需要一些共享状态或通信。 有几种选择

  • 将它存储在可从两个层访问的数据库中(sql,JNDI,LDAP – 更好的解决方案是键值存储,就像redis一样 – 如果你得到它)
  • 使用一些消息传递来存储Web层级的处理状态
  • 将状态存储在EJB层侧的哈希值中,并提供另一个SLSB方法来缓存此状态

你的选择并不容易 – 所有这些解决方案都以不同的方式吸引人。

当您使用icefaces时,您可以使用ICEpush机制来呈现更新。

我使用线程轮询模型和ProgressBar组件完成了这个。

public void init() { // This method is called by the constructor. // It doesn't matter where you define the PortableRenderer, as long as it's before it's used. PushRenderer.addCurrentSession("fullFormGroup"); portableRenderer = PushRenderer.getPortableRenderer(); } public void someBeanMethod(ActionEvent evt) { // This is a backing bean method called by some UI event (eg clicking a button) // Since it is part of a JSF/HTTP request, you cannot call portableRenderer.render copyExecuting = true; // Create a status thread and start it Thread statusThread = new Thread(new Runnable() { public void run() { try { // message and progress are both linked to components, which change on a portableRenderer.render("fullFormGroup") call message = "Copying..."; // initiates render. Note that this cannot be called from a thread which is already part of an HTTP request portableRenderer.render("fullFormGroup"); do { progress = getProgress(); portableRenderer.render("fullFormGroup"); // render the updated progress Thread.sleep(5000); // sleep for a while until it's time to poll again } while (copyExecuting); progress = getProgress(); message = "Finished!"; portableRenderer.render("fullFormGroup"); // push a render one last time } catch (InterruptedException e) { System.out.println("Child interrupted."); } }); statusThread.start(); // create a thread which initiates script and triggers the termination of statusThread Thread copyThread = new Thread(new Runnable() { public void run() { File someBigFile = new File("/tmp/foobar/large_file.tar.gz"); scriptResult = copyFile(someBigFile); // this will take a long time, which is why we spawn a new thread copyExecuting = false; // this will caue the statusThread's do..while loop to terminate } }); copyThread.start(); }