package com.revolsys.swing.parallel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.function.Supplier; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import com.revolsys.collection.list.Lists; import com.revolsys.collection.map.Maps; import com.revolsys.parallel.ThreadInterruptedException; import com.revolsys.util.Exceptions; import com.revolsys.util.Property; public class Invoke { private static PropertyChangeListener PROPERTY_CHANGE_LISTENER = new PropertyChangeListener() { @Override public synchronized void propertyChange(final PropertyChangeEvent event) { final SwingWorker<?, ?> worker = (SwingWorker<?, ?>)event.getSource(); if (worker.isCancelled() || worker.isDone()) { try { final List<SwingWorker<?, ?>> oldWorkers; List<SwingWorker<?, ?>> newWorkers; synchronized (WORKERS) { oldWorkers = Lists.toArray(WORKERS); WORKERS.remove(worker); if (worker instanceof MaxThreadsSwingWorker) { final MaxThreadsSwingWorker maxThreadsWorker = (MaxThreadsSwingWorker)worker; final String workerKey = maxThreadsWorker.getWorkerKey(); final int maxThreads = maxThreadsWorker.getMaxThreads(); int threads = Maps.decrementCount(WORKER_COUNTS, workerKey); final List<SwingWorker<?, ?>> waitingWorkers = WAITING_WORKERS.get(workerKey); while (Property.hasValue(waitingWorkers) && threads < maxThreads) { final SwingWorker<?, ?> nextWorker = waitingWorkers.remove(0); Maps.addCount(WORKER_COUNTS, workerKey); nextWorker.execute(); threads++; } } for (final Iterator<SwingWorker<?, ?>> iterator = WORKERS.iterator(); iterator .hasNext();) { final SwingWorker<?, ?> swingWorker = iterator.next(); if (swingWorker.isDone()) { iterator.remove(); } } newWorkers = getWorkers(); } PROPERTY_CHANGE_SUPPORT.firePropertyChange("workers", oldWorkers, newWorkers); } finally { worker.removePropertyChangeListener(this); } } } }; private static final PropertyChangeSupport PROPERTY_CHANGE_SUPPORT = new PropertyChangeSupport( Invoke.class); private static final List<SwingWorker<?, ?>> WORKERS = new LinkedList<>(); private static final Map<String, List<SwingWorker<?, ?>>> WAITING_WORKERS = new HashMap<>(); private static final Map<String, Integer> WORKER_COUNTS = new HashMap<>(); public static <V> V andWait(final Callable<V> callable) { try { if (SwingUtilities.isEventDispatchThread()) { return callable.call(); } else { final RunnableCallable<V> runnable = new RunnableCallable<>(callable); SwingUtilities.invokeAndWait(runnable); return runnable.getResult(); } } catch (final Throwable e) { return Exceptions.throwUncheckedException(e); } } public static void andWait(final Runnable runnable) { if (SwingUtilities.isEventDispatchThread()) { runnable.run(); } else { try { SwingUtilities.invokeAndWait(runnable); } catch (final InterruptedException e) { throw new ThreadInterruptedException(e); } catch (final InvocationTargetException e) { Exceptions.throwCauseException(e); } } } public static <V> SwingWorker<V, Void> background(final String key, final int maxThreads, final String description, final Supplier<V> backgroundTask, final Consumer<V> doneTask) { if (backgroundTask != null) { if (SwingUtilities.isEventDispatchThread()) { final SwingWorker<V, Void> worker = new SupplierConsumerMaxThreadsSwingWorker<>(key, maxThreads, description, backgroundTask, doneTask); worker(worker); return worker; } else { try { final V result = backgroundTask.get(); later(() -> { doneTask.accept(result); }); } catch (final Exception e) { Exceptions.throwUncheckedException(e); } } } return null; } public static SwingWorker<?, ?> background(final String description, final Runnable backgroundTask) { if (backgroundTask != null) { if (SwingUtilities.isEventDispatchThread()) { final SwingWorker<?, ?> worker = new SupplierConsumerSwingWorker<>(description, () -> { backgroundTask.run(); return null; }); worker(worker); return worker; } else { backgroundTask.run(); } } return null; } public static SwingWorker<?, ?> background(final String description, final Supplier<?> backgroundTask) { if (backgroundTask != null) { if (SwingUtilities.isEventDispatchThread()) { final SwingWorker<?, ?> worker = new SupplierConsumerSwingWorker<>(description, backgroundTask); worker(worker); return worker; } else { backgroundTask.get(); } } return null; } public static <V> SwingWorker<V, Void> background(final String description, final Supplier<V> backgroundTask, final Consumer<V> doneTask) { if (backgroundTask != null) { if (SwingUtilities.isEventDispatchThread()) { final SwingWorker<V, Void> worker = new SupplierConsumerSwingWorker<>(description, backgroundTask, doneTask); worker(worker); return worker; } else { final V result = backgroundTask.get(); later(() -> { doneTask.accept(result); }); } } return null; } public static PropertyChangeSupport getPropertyChangeSupport() { return PROPERTY_CHANGE_SUPPORT; } public static List<SwingWorker<?, ?>> getWorkers() { synchronized (WORKERS) { final List<SwingWorker<?, ?>> workers = new ArrayList<>(); for (final SwingWorker<?, ?> worker : WORKERS) { if (!worker.isDone()) { workers.add(worker); } } return workers; } } public static void later(final Runnable runnable) { if (SwingUtilities.isEventDispatchThread()) { runnable.run(); } else { SwingUtilities.invokeLater(runnable); } } public static void worker(final SwingWorker<? extends Object, ? extends Object> worker) { boolean execute = true; final List<SwingWorker<?, ?>> oldWorkers; final List<SwingWorker<?, ?>> newWorkers; synchronized (WORKERS) { if (WORKERS.contains(worker)) { return; } oldWorkers = Lists.toArray(WORKERS); WORKERS.add(worker); if (worker instanceof MaxThreadsSwingWorker) { final MaxThreadsSwingWorker maxThreadsWorker = (MaxThreadsSwingWorker)worker; final String workerKey = maxThreadsWorker.getWorkerKey(); final int maxThreads = maxThreadsWorker.getMaxThreads(); final int threads = Maps.getCount(WORKER_COUNTS, workerKey); if (threads >= maxThreads) { execute = false; Maps.addToList(WAITING_WORKERS, workerKey, worker); } else { Maps.addCount(WORKER_COUNTS, workerKey); } } newWorkers = getWorkers(); } worker.addPropertyChangeListener(PROPERTY_CHANGE_LISTENER); PROPERTY_CHANGE_SUPPORT.firePropertyChange("workers", oldWorkers, newWorkers); if (execute) { worker.execute(); } } /** * Use a swing worker to make sure the task is done later in the UI thread. * * @param description * @param doneTask */ public static void workerDone(final String description, final Runnable doneTask) { if (doneTask != null) { final SwingWorker<Void, Void> worker = new SupplierConsumerSwingWorker<>(description, null, (result) -> doneTask.run()); worker(worker); } } private Invoke() { } }