Как остановить запуск Java-кода с помощью кнопки остановки

У меня есть кнопка, которая вызывает метод из резервной копии Bean. Этот метод позволяет извлекать данные из синтаксического анализа html-кода. Пока этот метод работает, у меня есть диалог, показывающий индикатор выполнения и командную кнопку Cancel. Мне нужно, когда пользователь нажимает кнопку отмены, метод, вызываемый кнопкой extract, останавливается.

Это мой 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>

И вот мой метод searchEmails в моей сессииScoped Bean

 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 с помощью кнопки отмены?

Ответ 1

ExecutorService с дружественными прерываниями задачами

ExecutorService документация


  • Вместо прямого вызова метода преобразуйте метод в задачу 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);
    

Примечание:
Задача должна иметь подходящие проверки для прерывания потока для правильной отмены.
Для этого обратитесь к

Удачи.

Ответ 2

С вашей текущей архитектурой это будет нелегко. Посмотрев на свой код, кажется, что вы вызываете две большие операции, которые не могут быть прерваны посередине. Вы должны разбить работу на небольшие единицы и сделать обработку невозможной.

Самое простое решение, использующее ваш текущий код, может выглядеть примерно так.

 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, чтобы определить, когда это будет сделано, и скрыть окно.

См. также: