如何使用停止按钮停止运行Java代码

时间:2014-03-04 08:41:52

标签: java java-ee primefaces cancellation

我有一个调用来自支持Bean的方法的按钮。此方法允许从解析html代码中提取数据。当该方法运行时,我有一个对话框,显示进度条和命令按钮Cancel。我需要当用户单击取消按钮时,提取按钮调用的方法停止。

这是我的HTML代码:

<p:commandButton
    value="Start" style="width: 12%;height: 100%"
    update=":confirmPurchase, :confirmPurchaseTest, :mainform" id="extractbutton"
    ajax="true" widgetVar="ButtonExtract"
    actionListener="#{mailMB.searchEmails()}" 
    icon="ui-icon-disk" styleClass="ui-priority-primary"
    onstart="blockUIWidget1.show();" oncomplete=" blockUIWidget1.hide();" />

<p:dialog  widgetVar="blockUIWidget1" header="Hitonclick" modal="true"
    resizable="false" closable="false">
    <table border="0" style="width: 500px">
        <tbody > 
            <tr>  
                <td>
                    <p:graphicImage url="pictures/loading81.gif" width="200" height="200" alt="animated-loading-bar"/>
                </td>
                <td>
                    <h:outputLabel value="Extracting is in progress. Please wait..."/>
                    <div align="center">
                        <p:commandButton value="Cancel" title="Cancel" />
                    </div>
                </td>
            </tr>
            <div align="right">

            </div>
        </tbody>
    </table>
</p:dialog>

这是我在sessionScoped Bean中的searchEmails方法

 public void searchEmails() throws Exception {
        idCustomer = (String) session.getAttribute("idCustomer");
        System.out.println(idCustomer + " this is it");
        Customer customer = customerBusinessLocal.findById(idCustomer);
        data = dataBusinessLocal.createData(new Date(), number, keyword, moteur, customer, State.REJECTED);
        mails = mailBusinessLocal.createEmails(keyword, number, moteur, data);
        System.out.println("Method was invoked");    
 }

如何通过取消命令按钮停止searchEmails方法的运行?

2 个答案:

答案 0 :(得分:12)

具有中断友好任务的ExecutorService

ExecutorService documentation


  • 不是直接调用方法,而是将方法转换为ExecutorService的任务。

    public class SearchEmailsTask implements Runnable {
    
        private EmailSearcher emailSearcher;
    
        public SearchEmailsTask(EmailSearcher emailSearcher) {
            this.emailSearcher = emailSearcher;
        }
    
        @Override
        public void run() {
            emailSearcher.searchEmails();
        }
    }
    

    如果您想要退货,可以使用Callable<Object>


  • 如果要调用该方法,请将该任务提交给ExecutorService

    ExecutorService executorService = Executors.newFixedThreadPool(4);
    
    SearchEmailsTask searchEmailsTask = new SearchEmailsTask(new EmailSearcher());
    
    Future<?> future = executorService.submit(searchEmailsTask);
    

  • 保留对任务的引用。

    private static Map <String, Future<Object>> results = new HashMap <String, Future<Object>>();
    

    存储多个Future对象的地图应该是个好主意。如果你愿意,你当然可以选择更好的东西。


  • 在需要时调用取消任务。

    future.cancel(true);
    

注意:
任务应该有合适的线程中断检查,以便正确取消 要实现此目的,请参阅

  1. Future task of ExecutorService not truly cancelling
  2. how to suspend thread using thread's id?
  3. 祝你好运。

答案 1 :(得分:3)

使用您当前的架构并不容易。通过查看您的代码,您似乎在调用两个不能在中间中止的大型操作。你必须将工作分解成小单位并使处理流程不可用。

使用当前代码的最简单的解决方案可能看起来像这样。

 public class SessionScopedMailBean {
     volatile boolean searchAborted;

     public void searchEmails() throws Exception {
         searchAborted = false;
         while(!searchAborted) {
             // Process a single unit of work
         }
     }

     public void abortSearch() {
         searchAborted = true;
     }
}

在JSF中:

<p:commandButton value="Cancel" title="Cancel" actionListener="#{mailMB.abortSearch()}" />

那就是说我建议使用执行器和基于期货的方法来实现更清洁的架构。然后你也可以实现一个真正的进度条,而不仅仅是一个动画GIF微调器。

在命令处理程序中执行长操作的另一个问题是最终HTTP请求将超时。

更清洁的解决方案如下所示:

public class SessionScopedBean {
    ExecutorService pool;
    List<Future<Void>> pendingWork;

    @PostConstruct
    public void startPool() {
        pool = Executors.newFixedThreadPool(1);
    }

    @PreDestroy
    public void stopPool() {
        if(executor != null)
            pool.shutdownNow();
    }

    public void startSearch() {
        pendingWork = new ArrayList<Future<Void>>();

        // Prepare units of work (assuming it can be done quickly)
        while(/* no more work found */) {
            final MyType unit = ...; // Determine next unit of work
            Callable<Void> worker = new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    // Do whatever is needed to complete unit
                }
            };
            pendingWork.add(pool.submit(worker));
        }
    }

    public int getPending() {
        int nPending = 0;

        for(Future<MyResultType> i: pendingWork)
            if(!i.isDone()) nPending++;

        return nPending;
    }

    public void stopSearch() {
        for(Future<Void> i: pendingWork)
            if(!i.isDone()) i.cancel(true);
    }
}

然后使用AJAX轮询组件轮询getPending以确定它何时完成并隐藏窗口。

另见: