2

How can I stop the SwingWorker doing his work? I know there's the cancel() method for that, but the most I could do is to anonymously create a new SwingWorker that is doing the job.

Here is the code for reference:

public void mostrarResultado(final ResultSet resultado) {

    new SwingWorker<Void, Object[]>() {

        @Override
        public Void doInBackground() {

            // TODO: Process ResultSet and create Rows.  Call publish() for every N rows created.+
            DefaultTableModel modelo = new DefaultTableModel();
            jTableResultados.removeAll();
            int columnas;
            int res = 0;
            try {
                ResultSetMetaData metadata = resultado.getMetaData();
                columnas = metadata.getColumnCount();
                Object[] etiquetas = new Object[columnas];
                jProgressBar.setIndeterminate(true);
                for (int i = 0; i < columnas; i++) {
                    etiquetas[i] =
                            metadata.getColumnName(i + 1);
                }
                modelo.setColumnIdentifiers(etiquetas);
                jTableResultados.setModel(modelo);

                while (resultado.next() && !this.isCancelled()) {
                    res++;
                    Object fila[] = new Object[columnas];
                    for (int i = 0; i < columnas; i++) {
                        fila[i] = resultado.getObject(i + 1);
                    }
                    modelo.addRow(fila);
                    publish(fila);
                }
                jProgressBar.setIndeterminate(false);
                if (res == 0) {
                    JOptionPane.showMessageDialog(jPanelClientes.getParent(), "No se encontraron resultados con los criterios de búsqueda definidos", "Sin resultados", JOptionPane.INFORMATION_MESSAGE);
                }
            } catch (SQLException ex) {
                LOG.log(Level.SEVERE, "Excepcion: ", ex);
                JOptionPane.showMessageDialog(jPanelClientes.getParent(), "Error en al ejecutar la busqueda seleccionada", "Error", JOptionPane.ERROR_MESSAGE);
            }
            return null;
        }
    }.execute();
}

Actually the job is well done and published with no issues, but given that I work with a DB, it can happen that the query has too many results and the time taken for the full publish takes a time so the user has to be able to cancel the task and run a new one.

Obviously the worker is a different method than the cancel button event so I won't be able to invoke Worker cancel() method.

Tried making the worker an attribute for the class with no luck this way:

    public class myClass extends javax.swing.JFrame {
      private SwingWorker<Void, Object[]> tarea;

but then when I go for:

tarea = new SwingWorker<Void,Object[]>(){...} 

I get Incompatible types, found void, required SwingWorker, and I'm totally out of ideas.

Any tips?

Thanks

Alex K
  • 22,315
  • 19
  • 108
  • 236
Lowb
  • 162
  • 1
  • 13
  • See also http://stackoverflow.com/questions/6113944/how-cancel-the-execution-of-a-swingworker – DNA Mar 05 '12 at 09:31
  • possible duplicate of [Stop/cancel SwingWorker thread?](http://stackoverflow.com/questions/8083768/stop-cancel-swingworker-thread) – DNA Mar 05 '12 at 09:31
  • Indeed possible duplicate, too bad the search didnt pop out that answer :/ – Lowb Mar 05 '12 at 09:40
  • @Lowb possible duplicate, yes, we aren't mind readers, no one knows what's happens in rest of your code, because your SwingWorkers code isn't correct, please see my answer here, for better help sooner please edit your post with [SSCCE](http://example.com) – mKorbel Mar 05 '12 at 09:53
  • I meant, that I didnt see that answers on the search and yes, those solved my problem, that's why I was saying it was a duplicate. Sorry if I wasn't clear enought. – Lowb Mar 05 '12 at 10:02
  • So yes, fixed the problem with the link @DNA 2nd comment Thanks!! – Lowb Mar 05 '12 at 10:04
  • your snippet looks wrong (assuming jtableResultados is-a JTable): you _must not_ access any view component nor its model nor any of its other properties in doInBackground. Access to those is allowed only in a custom process method or in done. – kleopatra Mar 05 '12 at 11:26
  • @kleopatra yes, it is a JTable, but how else would I post the rows from the DB query to the JTable if not on the doInBackground method? – Lowb Mar 05 '12 at 15:39
  • in process (if you have want intermediate updates) and/or done (if you are interested in the final result only) – kleopatra Mar 05 '12 at 15:42

2 Answers2

3

This is not a answer to the question (which is how to cancel, I think that's solved) but how to separate the background handling from the view/model updates. A pseudo-code outline:

public static class MyDBWorker extends SwingWorker<Void, Object[]> {

    private JTable table;
    private DefaultTableModel model;
    private ResultSet resultado;

    public MyDBWorker(JTable table, ResultSet resultado) {
        this.table = table;
        this.resultado = resultado;
    }

    @Override
    protected Void doInBackground() throws Exception {
        ResultSetMetaData metadata = resultado.getMetaData();
        int columnas = metadata.getColumnCount();
        Object[] etiquetas = new Object[columnas];
        for (int i = 0; i < columnas; i++) {
            etiquetas[i] = metadata.getColumnName(i + 1);
        }
        publish(etiquetas);
        while (resultado.next() && !this.isCancelled()) {
            Object fila[] = new Object[columnas];
            for (int i = 0; i < columnas; i++) {
                fila[i] = resultado.getObject(i + 1);
            }
            publish(fila);
        }
        return null;
    }


    @Override
    protected void process(List<Object[]> chunks) {
        int startIndex = 0;
        // first chunk, set up a new model
        if (model == null) {
            model = new DefaultTableModel();
            model.setColumnIdentifiers(chunks.get(0));
            table.setModel(model);
            startIndex = 1;
        }
        for (int i = startIndex; i < chunks.size(); i++) {
            model.addRow(chunks.get(i));
        }
    }

    @Override
    protected void done() {
        // nothing to do, we were cancelled
        if (isCancelled()) return;
        // check for errors thrown in doInBackground
        try {
            get();
            // all was well in the background thread
            // check if there are any results
            if (table.getModel().getRowCount() == 0) {
                // show message
            }
        } catch (ExecutionException e) {
            // we get here if f.i. an SQLException is thrown
            // handle it as appropriate, f.i. inform user/logging system
        } catch (InterruptedException e) {
            // 
        }
    }

}

// use it
SwingWorker worker = new MyDBWorker(table, resultado);
PropertyChangeListener stateListener = new PropertyChangeListener() {

    @Override
    public void propertyChange(PropertyChangeEvent e) {
        if (e.getNewValue() == SwingWorker.StateValue.STARTED) {
            progressBar.setIndeterminate(true);
        }
        if (e.getNewValue() == SwingWorker.StateValue.DONE) {
            progressBar.setIndeterminate(false);
        }

    }

};
worker.addPropertyChangeListener(doneListener);
worker.execute();

Edit

fixed a bug in process: adding rows to the model must start on first index of the batch always, except when processing the header.

kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • thanks for the tip @kleopatra. I guess instead of publish(fila) I should call for process(fila) for every row that I want to post? – Lowb Mar 05 '12 at 16:17
  • @Lowb no - the one thing to really burn into one's brain is that clear separation of threads: some methods are designed to be called on the background thread (f.i. doInBackground, publish) while others are guaranteed to be executed on the EDT (f.i. process, done). Never mix methods of the one group with those of the other group – kleopatra Mar 05 '12 at 16:28
  • hmmm I don't get it @kleopatra, when is process() invoked then? what's the content of chunks? how can I decide when the rows are shown in the UI? too many questions, sorry :( – Lowb Mar 05 '12 at 16:39
  • @Lowb simply call publish in doInBackground whenever you want to update the ui. The actual update happens in process, whatever happens between publish --> process is completely internal to the worker and not under your control (and you don't want to know it :-) – kleopatra Mar 05 '12 at 16:46
  • ok ok I get it now, when I call publish() then the program does some stuff and calls process() and that's why right? thanks btw!!! – Lowb Mar 05 '12 at 16:55
  • @kleopatra I always noticed there (I saw a few times) that after get(); next code line is related catch blok – mKorbel Mar 05 '12 at 17:49
  • @kleopatra it is the same as it was before, i started at 1 always, didn't it? – Lowb Mar 09 '12 at 11:24
  • @Lowb sorry for the confusion: in the process method, the row data filling (in the model) has to start at 1 _only if_ 0 corresponds to the header, otherwise must start at 0. Everything in doInBackground is just fine and unchanged – kleopatra Mar 09 '12 at 11:29
1

1) never create / re_create

DefaultTableModel modelo = new DefaultTableModel();

2) please what's

jTableResultados.removeAll();

JTable, AbstractTableModel, DefaultTableModel

3) your JProgressBar couldn't be works

jProgressBar.setIndeterminate(false);

4) both linked question make me sence in connections with canceling SwingWorker's instance

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 1) how should I define the tablemodel then? 2) that's deleting the previously shown results in the jTable that shows the results for the BD query 3) the progressbar works just fine. 4) that solved it, yes. – Lowb Mar 05 '12 at 10:00