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

时间:2010-07-28 16:56:29

标签: java java-ee-6 ejb-3.1 future

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

异步发送邮件工作正常,但我无法弄清楚如何从外部访问进度。似乎我的做法是非常错误的,但不知何故它必须是可能的(也许是另一种方法)。这就是我的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();

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

3 个答案:

答案 0 :(得分:3)

没人?好的,这是我的解决方案。我不确定这是一个很大的解决方法,还是只是一种完成这项工作的方法。

由于@Asynchronous方法无法访问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(String)映射到Integer值(进度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来自我的Managed(Weld)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 Polling从我的JSF视图访问EmailManager的进度,告诉用户已经发送了多少封邮件。刚刚用2个用户进行测试,似乎可行。

答案 1 :(得分:0)

我在这里也只看到了@Singleton解决方案。 但这意味着需要管理进步EJB。例如。需要做一些努力来从Hashmap中删除旧会话。

答案 2 :(得分:0)

另一种解决方案描述于 Is there any way to know the progress of a EJB Asynchronous process?

此解决方案不需要有状态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();
  }
  ....
}