公开 @Asynchronous 函数的当前进度以在视图中使用

发布于 2024-09-12 12:21:50 字数 824 浏览 2 评论 0原文

在我的 JEE6-App(在 Glassfish 3.0.1 上运行)中,我有一个 EmailEJB,它必须发送大量邮件。邮件是异步发送的,因此它使用新的 EJB3.1 @Asynchronous 进行注释,让它在单独的线程中运行。现在我希望用户了解该方法的当前状态:已经发送了多少封邮件?

异步发送邮件工作正常,但我不知道如何让外部可以访问进度。看来我这样做的方法是完全错误的,但不知何故它必须是可能的(也许是另一种方法)。这就是我的 EmailEJB 目前的样子(它是一种伪代码,但解释了我想要的内容):

@Stateful
public class EmailEJB {

  @Asynchronous
  public Future<Integer> sendMails() {
    for (int i=0; i<mails.size; i++) {
      sendMail(mails[i])
      // i want to return the progress without returning ;)
      return new AsyncResult<Integer>(i)
    }
  }
}

//Just for the completeness... from outside, i'm accessing the progress like this:
Future<Integer> progress = emailEJB.sendEmails();
Integer currentvalue = progress.get();

如何在异步函数中返回当前进度,而不用返回取消它?如何向用户显示函数内循环的进度?我需要另一个异步方法吗?有什么提示吗?

In my JEE6-App (running on Glassfish 3.0.1) I have an EmailEJB which has to send lots of mails. The mails are sent asynchronously, so its annotated with the new EJB3.1 @Asynchronous, letting it be run in a separate Thread. Now i want the user to be informed about the current status of the method: How many mails have already been sent?

Sending the mails asynchronously works fine, but i can't figure out how to let the progress be accessible from outside. Seems like my approach to do that is quite wrong, but somehow it has to be possible (maybe another approach). This is how my EmailEJB currently looks like (its kind of pseudo code, but explains what i want):

@Stateful
public class EmailEJB {

  @Asynchronous
  public Future<Integer> sendMails() {
    for (int i=0; i<mails.size; i++) {
      sendMail(mails[i])
      // i want to return the progress without returning ;)
      return new AsyncResult<Integer>(i)
    }
  }
}

//Just for the completeness... from outside, i'm accessing the progress like this:
Future<Integer> progress = emailEJB.sendEmails();
Integer currentvalue = progress.get();

How can i return the current progress inside my asynchronous function, without cancelling it with a return? How can i show the user the progress of a loop inside a function? Do i need another asynchronous method? Any hints?

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

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

发布评论

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

评论(3

多彩岁月 2024-09-19 12:21:50

没有人吗?好的,这是我的解决方案。我不确定这是否是一个很大的解决方法,或者只是完成此任务的一种方法。

由于 @Asynchronous 方法无法访问 Session 上下文,因此也没有 Session Bean(至少我不知道如何,我总是遇到 ConcurrentModificationErrors 或类似的错误),我创建了一个 Singleton ProgressEJB,其中包含一个 HashMap:

@Singleton @LocalBean @Startup
public class ProgressEJB {
  private HashMap<String, Integer> progressMap = new HashMap<String, Integer>
  // getters and setters
}

此 hashmap 应该映射 SessionId (字符串)到整数值(进度 0->100)。因此,用户会话与进度相关联。
在我的 EmailEJB 中,我注入了这个 ProgressEJB,并且在我的 @Asynchronous 方法中,每次发送电子邮件时我都会增加该值:

@Stateful @LocalBean
public class EmailEJB {
@Inject
private ProgressEJB progress;
// Mail-Settings
...
@Asynchronous
public void sendEmails(user:User, message:Message, sessionId:String) {
  progress.progressMap.put(sessionId, 0);
  for (int i=0; i<mails.size; i++) {
    sendMail(mails[i])
    progress.getProgressMap().put(sessionId, (i / mails.size) * 100)
  }
  progress.getProgressMap().remove(sessionId);
}

sessionId 来自我的托管(焊接)Bean,在调用该函数时:

@SessionScoped
@Named
public class EmailManager {
  @Inject 
  private ProgressEJB progress;
  @Inject
  private FacesContext facesContext;

  private String sessionId;

  @PostConstruct
  private void setSessionId() {
    this.sessionId = ((HttpSession)facesContext.getExternalContext().getSession(false)).getId();
  }

  public Integer getProgress() {
    if (progress.getProgressMap().get(sessionId) == null)
      return 100;
    else 
      return progress.getProgressMap().get(sessionId);
  }
}

现在我可以使用 Ajax 轮询从我的 JSF 视图中访问 EmailManager 的进度,告诉用户已经发送了多少邮件。刚刚与 2 个用户进行了测试,似乎有效。

Nobody? Ok so this is my solution. Im not sure if this is a big fat workaround or just a way to get this done.

Since an @Asynchronous method cannot access the Session context, and therefore also no Session Beans (at least i dont know how, i always got ConcurrentModificationErrors or similar ones) i created a Singleton ProgressEJB, which contains a HashMap:

@Singleton @LocalBean @Startup
public class ProgressEJB {
  private HashMap<String, Integer> progressMap = new HashMap<String, Integer>
  // getters and setters
}

This hashmap should map the SessionId (a String) to an Integer value (the progress 0->100). So a user session is associated with a progress.
In my EmailEJB, i'm injecting this ProgressEJB, and in my @Asynchronous method, i'm increasing the value everytime an email has been sent:

@Stateful @LocalBean
public class EmailEJB {
@Inject
private ProgressEJB progress;
// Mail-Settings
...
@Asynchronous
public void sendEmails(user:User, message:Message, sessionId:String) {
  progress.progressMap.put(sessionId, 0);
  for (int i=0; i<mails.size; i++) {
    sendMail(mails[i])
    progress.getProgressMap().put(sessionId, (i / mails.size) * 100)
  }
  progress.getProgressMap().remove(sessionId);
}

The sessionId comes from my Managed (Weld) Bean, when calling the function:

@SessionScoped
@Named
public class EmailManager {
  @Inject 
  private ProgressEJB progress;
  @Inject
  private FacesContext facesContext;

  private String sessionId;

  @PostConstruct
  private void setSessionId() {
    this.sessionId = ((HttpSession)facesContext.getExternalContext().getSession(false)).getId();
  }

  public Integer getProgress() {
    if (progress.getProgressMap().get(sessionId) == null)
      return 100;
    else 
      return progress.getProgressMap().get(sessionId);
  }
}

Now i can access progress from EmailManager from my JSF view with Ajax Polling, telling the user how many mails already have been sent. Just tested it with 2 users, seems to work.

听闻余生 2024-09-19 12:21:50

我在这里也只看到一个@Singleton 解决方案。
但这意味着需要 Housekeeping in ProgressEJB。例如,需要付出一些努力来从哈希图中删除旧会话。

I also see only a @Singleton solution here.
But this imply the need of Housekeeping in ProgressEJB. E.g. some effort is needed to prune old session from Hashmap.

筱武穆 2024-09-19 12:21:50

另一种解决方案描述于
有什么方法可以知道EJB 异步进程的进度?

此解决方案不需要 Stateful Bean。

@Stateless
public class EmailEJB {
    // Mail-Settings
    ...
    @Asynchronous
    public void sendEmails(User user, Message message, WorkContext context) {
      progress.progressMap.put(sessionId, 0);
      for (int i=0; i<mails.size; i++) {
        sendMail(mails[i])
        context.setProgress((i / mails.size) * 100)
      }
      context.setRunning(false);
    }
}

保存进度的 Context-Object。

public class WorkContext {
    //volatile is important!
    private volatile Integer progress = 0;
    private volatile boolean running = false;    
    // getters & setters
}

使用方法非常简单。

@SessionScoped
@Named
public class EmailManager {
  @Inject 
  private EmailEJB emailEJB;
  private WorkContext workContext;

  public void doStuff() {
        workContext = new WorkContext();
        emailEJB.sendEmails(user, message, workContext)
  }

  public Integer getProgress() {
        return workContext.getProgress();
  }
  ....
}

Another solution is described in
Is there any way to know the progress of a EJB Asynchronous process?

This solution does not need a Stateful Bean.

@Stateless
public class EmailEJB {
    // Mail-Settings
    ...
    @Asynchronous
    public void sendEmails(User user, Message message, WorkContext context) {
      progress.progressMap.put(sessionId, 0);
      for (int i=0; i<mails.size; i++) {
        sendMail(mails[i])
        context.setProgress((i / mails.size) * 100)
      }
      context.setRunning(false);
    }
}

The Context-Object, which holds the progress.

public class WorkContext {
    //volatile is important!
    private volatile Integer progress = 0;
    private volatile boolean running = false;    
    // getters & setters
}

The usage is very easy.

@SessionScoped
@Named
public class EmailManager {
  @Inject 
  private EmailEJB emailEJB;
  private WorkContext workContext;

  public void doStuff() {
        workContext = new WorkContext();
        emailEJB.sendEmails(user, message, workContext)
  }

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