diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java index 8a0ff7e52..e74206b01 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java @@ -15,9 +15,6 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,49 +27,49 @@ import jadx.gui.ui.panel.ProgressPanel; import jadx.gui.utils.NLS; import jadx.gui.utils.UiUtils; -import static jadx.gui.utils.UiUtils.calcProgress; - /** - * Class for run tasks in background with progress bar indication. + * Run tasks in the background with progress bar indication. * Use instance created in {@link MainWindow}. */ public class BackgroundExecutor { private static final Logger LOG = LoggerFactory.getLogger(BackgroundExecutor.class); private final JadxSettings settings; - private final ProgressPanel progressPane; + private final ProgressUpdater progressUpdater; private ThreadPoolExecutor taskQueueExecutor; - private final Map taskRunning = new ConcurrentHashMap<>(); + private final Map taskRunning = new ConcurrentHashMap<>(); private final AtomicLong idSupplier = new AtomicLong(0); public BackgroundExecutor(JadxSettings settings, ProgressPanel progressPane) { this.settings = Objects.requireNonNull(settings); - this.progressPane = Objects.requireNonNull(progressPane); + this.progressUpdater = new ProgressUpdater(progressPane, this::taskCanceled); reset(); } - public synchronized Future execute(IBackgroundTask task) { - long id = idSupplier.incrementAndGet(); - TaskWorker taskWorker = new TaskWorker(id, task); - taskRunning.put(id, task); - taskQueueExecutor.execute(() -> { - taskWorker.init(); - taskWorker.run(); + public synchronized void execute(IBackgroundTask task) { + InternalTask internalTask = buildTask(task); + taskQueueExecutor.execute(() -> runTask(internalTask)); + } + + public synchronized Future executeWithFuture(IBackgroundTask task) { + InternalTask internalTask = buildTask(task); + return taskQueueExecutor.submit(() -> { + runTask(internalTask); + return internalTask.getStatus(); }); - return taskWorker; } public synchronized void cancelAll() { try { - taskRunning.values().forEach(Cancelable::cancel); + taskRunning.values().forEach(this::cancelTask); taskQueueExecutor.shutdownNow(); boolean complete = taskQueueExecutor.awaitTermination(3, TimeUnit.SECONDS); if (complete) { LOG.debug("Background task executor canceled successfully"); } else { String taskNames = taskRunning.values().stream() - .map(IBackgroundTask::getTitle) + .map(t -> t.getBgTask().getTitle()) .collect(Collectors.joining(", ")); LOG.debug("Background task executor cancel failed. Running tasks: {}", taskNames); } @@ -100,8 +97,8 @@ public class BackgroundExecutor { execute(new SimpleTask(title, Collections.singletonList(backgroundRunnable), onFinishUiRunnable)); } - public Future execute(String title, Runnable backgroundRunnable) { - return execute(new SimpleTask(title, Collections.singletonList(backgroundRunnable))); + public void execute(String title, Runnable backgroundRunnable) { + execute(new SimpleTask(title, Collections.singletonList(backgroundRunnable))); } public void startLoading(Runnable backgroundRunnable, Runnable onFinishUiRunnable) { @@ -118,152 +115,87 @@ public class BackgroundExecutor { idSupplier.set(0); } - private void taskComplete(long id) { - taskRunning.remove(id); + private InternalTask buildTask(IBackgroundTask task) { + long id = idSupplier.incrementAndGet(); + InternalTask internalTask = new InternalTask(id, task); + taskRunning.put(id, internalTask); + return internalTask; } - private final class TaskWorker extends SwingWorker implements ITaskInfo { - private final long id; - private final IBackgroundTask task; - private ITaskExecutor taskExecutor; - private TaskStatus status = TaskStatus.WAIT; - private long jobsCount; - private long jobsComplete; - private long time; - - public TaskWorker(long id, IBackgroundTask task) { - this.id = id; - this.task = task; + private void runTask(InternalTask internalTask) { + try { + IBackgroundTask task = internalTask.getBgTask(); + ITaskExecutor taskExecutor = task.scheduleTasks(); + taskExecutor.setThreadsCount(settings.getThreadsCount()); + int tasksCount = taskExecutor.getTasksCount(); + internalTask.setTaskExecutor(taskExecutor); + internalTask.setJobsCount(tasksCount); + if (UiUtils.JADX_GUI_DEBUG) { + LOG.debug("Starting background task '{}', jobs count: {}, time limit: {} ms, memory check: {}", + task.getTitle(), tasksCount, task.timeLimit(), task.checkMemoryUsage()); + } + long startTime = System.currentTimeMillis(); + Supplier cancelCheck = buildCancelCheck(internalTask, startTime); + internalTask.taskStart(startTime, cancelCheck); + progressUpdater.addTask(internalTask); + taskExecutor.execute(); + taskExecutor.awaitTermination(); + } catch (Exception e) { + LOG.error("Task failed", e); + internalTask.setStatus(TaskStatus.ERROR); + } finally { + taskComplete(internalTask); } + } - public void init() { - addPropertyChangeListener(progressPane); - SwingUtilities.invokeLater(() -> { - progressPane.reset(); - if (task.getTaskProgress() != null) { - progressPane.setIndeterminate(false); + private void taskComplete(InternalTask internalTask) { + try { + IBackgroundTask task = internalTask.getBgTask(); + internalTask.setJobsComplete(internalTask.getTaskExecutor().getProgress()); + internalTask.setStatus(TaskStatus.COMPLETE); + internalTask.updateExecTime(); + task.onDone(internalTask); + // treat UI task operations as part of the task to not mix with others + UiUtils.uiRunAndWait(() -> { + try { + task.onFinish(internalTask); + } catch (Exception e) { + LOG.error("Task onFinish failed", e); + internalTask.setStatus(TaskStatus.ERROR); } }); + } catch (Exception e) { + LOG.error("Task complete failed", e); + internalTask.setStatus(TaskStatus.ERROR); + } finally { + internalTask.taskComplete(); + progressUpdater.taskComplete(internalTask); + removeTask(internalTask); } + } - @Override - protected TaskStatus doInBackground() throws Exception { - progressPane.changeLabel(this, task.getTitle() + "… "); - progressPane.changeCancelBtnVisible(this, task.canBeCanceled()); - try { - runJobs(); - } finally { - try { - task.onDone(this); - // treat UI task operations as part of the task to not mix with others - UiUtils.uiRunAndWait(() -> { - try { - progressPane.setVisible(false); - task.onFinish(this); - } catch (Throwable e) { - LOG.error("Task onFinish failed", e); - status = TaskStatus.ERROR; - } - }); - } catch (Throwable e) { - LOG.error("Task onDone failed", e); - status = TaskStatus.ERROR; - } finally { - taskComplete(id); - progressPane.changeVisibility(this, false); - removePropertyChangeListener(progressPane); - } + private void removeTask(InternalTask internalTask) { + taskRunning.remove(internalTask.getId()); + } + + private void cancelTask(InternalTask internalTask) { + try { + IBackgroundTask task = internalTask.getBgTask(); + if (!internalTask.isRunning()) { + // task complete or not yet started + task.cancel(); + removeTask(internalTask); + return; } - return status; - } - - private void runJobs() throws InterruptedException { - taskExecutor = task.scheduleTasks(); - jobsCount = taskExecutor.getTasksCount(); - LOG.debug("Starting background task '{}', jobs count: {}, time limit: {} ms, memory check: {}", - task.getTitle(), jobsCount, task.timeLimit(), task.checkMemoryUsage()); - if (jobsCount != 1 && !task.isSilent()) { - progressPane.changeVisibility(this, true); - } - status = TaskStatus.STARTED; - taskExecutor.setThreadsCount(settings.getThreadsCount()); - taskExecutor.execute(); - long startTime = System.currentTimeMillis(); - status = waitTermination(buildCancelCheck(startTime)); - time = System.currentTimeMillis() - startTime; - jobsComplete = taskExecutor.getProgress(); - } - - @SuppressWarnings("BusyWait") - private TaskStatus waitTermination(Supplier cancelCheck) throws InterruptedException { - try { - int k = 0; - while (true) { - if (!taskExecutor.isRunning()) { - return TaskStatus.COMPLETE; - } - if (task.isSilent()) { - Thread.sleep(50); - continue; - } - TaskStatus cancelStatus = cancelCheck.get(); - if (cancelStatus != null) { - performCancel(); - return cancelStatus; - } - if (k < 10) { - // faster update for short tasks - Thread.sleep(200); - if (k == 5) { - updateProgress(); - } - } else { - updateProgress(); - Thread.sleep(1000); - } - if (jobsCount == 1 && k == 5) { - // small delay before show progress to reduce blinking on short tasks - progressPane.changeVisibility(this, true); - } - k++; - } - } catch (InterruptedException e) { - LOG.debug("Task wait interrupted"); - performCancel(); - return TaskStatus.CANCEL_BY_USER; - } catch (Exception e) { - LOG.error("Task wait aborted by exception", e); - performCancel(); - return TaskStatus.ERROR; - } - } - - private void updateProgress() { - Consumer onProgressListener = task.getProgressListener(); - ITaskProgress taskProgress = task.getTaskProgress(); - if (taskProgress == null) { - setProgress(calcProgress(taskExecutor.getProgress(), jobsCount)); - if (onProgressListener != null) { - onProgressListener.accept(new TaskProgress(taskExecutor.getProgress(), jobsCount)); - } - } else { - setProgress(calcProgress(taskProgress)); - if (onProgressListener != null) { - onProgressListener.accept(taskProgress); - } - } - } - - private void performCancel() throws InterruptedException { - progressPane.changeLabel(this, task.getTitle() + " (" + NLS.str("progress.canceling") + ")… "); - progressPane.changeIndeterminate(this, true); + ITaskExecutor taskExecutor = internalTask.getTaskExecutor(); // force termination + task.cancel(); + taskExecutor.terminate(); + ExecutorService executor = taskExecutor.getInternalExecutor(); if (executor == null) { return; } - taskExecutor.terminate(); - task.cancel(); int cancelTimeout = task.getCancelTimeoutMS(); if (cancelTimeout != 0) { if (executor.awaitTermination(cancelTimeout, TimeUnit.MILLISECONDS)) { @@ -277,74 +209,48 @@ public class BackgroundExecutor { LOG.debug("Forced task cancel status: {}", complete ? "success" : "fail, still active: " + (taskExecutor.getTasksCount() - taskExecutor.getProgress())); - } - - private Supplier buildCancelCheck(long startTime) { - long waitUntilTime = task.timeLimit() == 0 ? 0 : startTime + task.timeLimit(); - boolean checkMemoryUsage = task.checkMemoryUsage(); - return () -> { - if (task.isCanceled()) { - return TaskStatus.CANCEL_BY_USER; - } - if (waitUntilTime != 0 && waitUntilTime < System.currentTimeMillis()) { - LOG.error("Task '{}' execution timeout, force cancel", task.getTitle()); - return TaskStatus.CANCEL_BY_TIMEOUT; - } - if (isCancelled() || Thread.currentThread().isInterrupted()) { - LOG.warn("Task '{}' canceled", task.getTitle()); - return TaskStatus.CANCEL_BY_USER; - } - if (checkMemoryUsage && !UiUtils.isFreeMemoryAvailable()) { - LOG.info("Memory usage: {}", UiUtils.memoryInfo()); - if (taskExecutor.getThreadsCount() == 1) { - LOG.error("Task '{}' memory limit reached, force cancel", task.getTitle()); - return TaskStatus.CANCEL_BY_MEMORY; - } - LOG.warn("Low memory, reduce processing threads count to 1"); - // reduce threads count and continue - taskExecutor.setThreadsCount(1); - System.gc(); - UiUtils.sleep(1000); // wait GC - if (!UiUtils.isFreeMemoryAvailable()) { - LOG.error("Task '{}' memory limit reached (after GC), force cancel", task.getTitle()); - return TaskStatus.CANCEL_BY_MEMORY; - } - } - return null; - }; - } - - @Override - public TaskStatus getStatus() { - return status; - } - - @Override - public long getJobsCount() { - return jobsCount; - } - - @Override - public long getJobsComplete() { - return jobsComplete; - } - - @Override - public long getJobsSkipped() { - return jobsCount - jobsComplete; - } - - @Override - public long getTime() { - return time; - } - - @Override - public String toString() { - return "TaskWorker{status=" + status - + ", jobsCount=" + jobsCount - + ", jobsComplete=" + jobsComplete - + ", time=" + time + "ms}"; + } catch (Exception e) { + LOG.error("Failed to cancel task: {}", internalTask, e); } } + + /** + * Task cancel notification from progress updater + */ + private void taskCanceled(InternalTask task) { + cancelTask(task); + } + + private Supplier buildCancelCheck(InternalTask internalTask, long startTime) { + IBackgroundTask task = internalTask.getBgTask(); + int timeLimit = task.timeLimit(); + long waitUntilTime = timeLimit == 0 ? 0 : startTime + timeLimit; + boolean checkMemoryUsage = task.checkMemoryUsage(); + return () -> { + if (task.isCanceled() || Thread.currentThread().isInterrupted()) { + return TaskStatus.CANCEL_BY_USER; + } + if (waitUntilTime != 0 && waitUntilTime < System.currentTimeMillis()) { + LOG.warn("Task '{}' execution timeout, force cancel", task.getTitle()); + return TaskStatus.CANCEL_BY_TIMEOUT; + } + if (checkMemoryUsage && !UiUtils.isFreeMemoryAvailable()) { + LOG.warn("High memory usage: {}", UiUtils.memoryInfo()); + if (internalTask.getTaskExecutor().getThreadsCount() == 1) { + LOG.warn("Task '{}' memory limit reached, force cancel", task.getTitle()); + return TaskStatus.CANCEL_BY_MEMORY; + } + LOG.warn("Low free memory, reduce processing threads count to 1"); + // reduce threads count and continue + internalTask.getTaskExecutor().setThreadsCount(1); + System.gc(); + UiUtils.sleep(1000); // wait GC + if (!UiUtils.isFreeMemoryAvailable()) { + LOG.error("Task '{}' memory limit reached (after GC), force cancel", task.getTitle()); + return TaskStatus.CANCEL_BY_MEMORY; + } + } + return null; + }; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/InternalTask.java b/jadx-gui/src/main/java/jadx/gui/jobs/InternalTask.java new file mode 100644 index 000000000..0f716fd05 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/jobs/InternalTask.java @@ -0,0 +1,141 @@ +package jadx.gui.jobs; + +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +import org.jetbrains.annotations.NotNull; + +import jadx.api.utils.tasks.ITaskExecutor; + +public class InternalTask implements Delayed, ITaskInfo { + private final long id; + private final IBackgroundTask bgTask; + private final AtomicBoolean running = new AtomicBoolean(false); + private final AtomicLong nextUpdate = new AtomicLong(0); + private final AtomicBoolean firstUpdate = new AtomicBoolean(true); + + private long startTime; + private long execTime; + private Supplier cancelCheck; + private TaskStatus status = TaskStatus.WAIT; + private ITaskExecutor taskExecutor; + private long jobsCount; + private long jobsComplete; + + public InternalTask(long id, IBackgroundTask task) { + this.id = id; + this.bgTask = task; + } + + public void taskStart(long startTime, Supplier cancelCheck) { + this.startTime = startTime; + this.cancelCheck = cancelCheck; + this.status = TaskStatus.STARTED; + this.running.set(true); + } + + public void taskComplete() { + this.running.set(false); + if (status == TaskStatus.STARTED) { + // might be already set to error or cancel + this.status = TaskStatus.COMPLETE; + } + updateExecTime(); + } + + public long getId() { + return id; + } + + public IBackgroundTask getBgTask() { + return bgTask; + } + + public void setNextUpdate(long nextUpdate) { + this.nextUpdate.set(nextUpdate); + } + + public boolean isRunning() { + return running.get(); + } + + public boolean checkForFirstUpdate() { + return firstUpdate.compareAndExchange(true, false); + } + + public Supplier getCancelCheck() { + return cancelCheck; + } + + public long getStartTime() { + return startTime; + } + + @Override + public TaskStatus getStatus() { + return status; + } + + public void setStatus(TaskStatus taskStatus) { + this.status = taskStatus; + } + + public ITaskExecutor getTaskExecutor() { + return taskExecutor; + } + + public void setTaskExecutor(ITaskExecutor taskExecutor) { + this.taskExecutor = taskExecutor; + } + + @Override + public long getJobsComplete() { + return jobsComplete; + } + + public void setJobsComplete(long jobsComplete) { + this.jobsComplete = jobsComplete; + } + + @Override + public long getJobsCount() { + return jobsCount; + } + + public void setJobsCount(long jobsCount) { + this.jobsCount = jobsCount; + } + + @Override + public long getJobsSkipped() { + return jobsCount - jobsComplete; + } + + @Override + public long getTime() { + return execTime; + } + + public void updateExecTime() { + this.execTime = System.currentTimeMillis() - startTime; + } + + @Override + public long getDelay(@NotNull TimeUnit unit) { + return unit.convert(nextUpdate.get() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + + @Override + public int compareTo(@NotNull Delayed o) { + return Long.compare(nextUpdate.get(), ((InternalTask) o).nextUpdate.get()); + } + + @Override + public String toString() { + return "InternalTask{" + bgTask.getTitle() + ", status=" + status + + ", progress=" + jobsComplete + " of " + jobsCount + '}'; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/ProgressUpdater.java b/jadx-gui/src/main/java/jadx/gui/jobs/ProgressUpdater.java new file mode 100644 index 000000000..c94c96301 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/jobs/ProgressUpdater.java @@ -0,0 +1,106 @@ +package jadx.gui.jobs; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.core.utils.Utils; +import jadx.gui.ui.panel.ProgressPanel; +import jadx.gui.utils.NLS; +import jadx.gui.utils.UiUtils; + +@SuppressWarnings({ "FieldCanBeLocal", "InfiniteLoopStatement" }) +public class ProgressUpdater { + private static final Logger LOG = LoggerFactory.getLogger(ProgressUpdater.class); + + private static final int UPDATE_INTERVAL_MS = 1000; + + private final ProgressPanel progressPane; + private final Consumer cancelCallback; + private final ExecutorService bgExecutor = Executors.newSingleThreadExecutor(Utils.simpleThreadFactory("jadx-progress")); + private final BlockingQueue tasks = new DelayQueue<>(); + + public ProgressUpdater(ProgressPanel progressPane, Consumer cancelCallback) { + this.progressPane = progressPane; + this.cancelCallback = cancelCallback; + this.bgExecutor.execute(this::updateLoop); + } + + public void addTask(InternalTask task) { + if (task.getBgTask().isSilent()) { + return; + } + scheduleNextUpdate(task); + } + + public void taskComplete(InternalTask task) { + task.setNextUpdate(0); + updateProgress(task); + } + + private void scheduleNextUpdate(InternalTask task) { + task.setNextUpdate(System.currentTimeMillis() + UPDATE_INTERVAL_MS); + tasks.add(task); + } + + private void updateLoop() { + while (true) { + try { + InternalTask task = tasks.take(); + if (task.isRunning()) { + updateProgress(task); + cancelCheck(task); + scheduleNextUpdate(task); + } + } catch (Exception e) { + LOG.warn("Error in ProgressUpdater loop", e); + } + } + } + + private void updateProgress(InternalTask internalTask) { + UiUtils.uiRun(() -> { + IBackgroundTask bgTask = internalTask.getBgTask(); + if (internalTask.isRunning()) { + if (internalTask.checkForFirstUpdate()) { + progressPane.setLabel(bgTask.getTitle() + "… "); + progressPane.setCancelButtonVisible(bgTask.canBeCanceled()); + progressPane.setVisible(true); + } + ITaskProgress taskProgress = bgTask.getTaskProgress(); + if (taskProgress == null) { + int progress = internalTask.getTaskExecutor().getProgress(); + taskProgress = new TaskProgress(progress, internalTask.getJobsCount()); + } + progressPane.setProgress(taskProgress); + Consumer onProgressListener = bgTask.getProgressListener(); + if (onProgressListener != null) { + onProgressListener.accept(taskProgress); + } + } else { + progressPane.reset(); + progressPane.setVisible(false); + } + }); + } + + private void cancelCheck(InternalTask task) { + TaskStatus taskStatus = task.getCancelCheck().get(); + if (taskStatus == null) { + return; + } + task.setStatus(taskStatus); + UiUtils.uiRun(() -> { + IBackgroundTask bgTask = task.getBgTask(); + progressPane.setLabel(bgTask.getTitle() + " (" + NLS.str("progress.canceling") + ")… "); + progressPane.setCancelButtonVisible(false); + progressPane.setIndeterminate(true); + }); + cancelCallback.accept(task); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/TaskProgress.java b/jadx-gui/src/main/java/jadx/gui/jobs/TaskProgress.java index a2bf0f11b..aa5a3eb48 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/TaskProgress.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/TaskProgress.java @@ -36,4 +36,9 @@ public class TaskProgress implements ITaskProgress { public void updateTotal(int total) { this.total = total; } + + @Override + public String toString() { + return "TaskProgress{" + progress + " of " + total + '}'; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/search/SearchJob.java b/jadx-gui/src/main/java/jadx/gui/search/SearchJob.java index 381e77fe6..33f253660 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/SearchJob.java +++ b/jadx-gui/src/main/java/jadx/gui/search/SearchJob.java @@ -6,8 +6,8 @@ import org.slf4j.LoggerFactory; import jadx.gui.treemodel.JNode; public class SearchJob implements Runnable { - private static final Logger LOG = LoggerFactory.getLogger(SearchJob.class); + private final SearchTask searchTask; private final ISearchProvider provider; diff --git a/jadx-gui/src/main/java/jadx/gui/search/SearchTask.java b/jadx-gui/src/main/java/jadx/gui/search/SearchTask.java index 572189f89..a545c7b86 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/SearchTask.java +++ b/jadx-gui/src/main/java/jadx/gui/search/SearchTask.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -62,7 +61,7 @@ public class SearchTask extends CancelableBackgroundTask { resetCancel(); resultsCount.set(0); taskProgress.updateTotal(jobs.stream().mapToInt(s -> s.getProvider().total()).sum()); - future = backgroundExecutor.execute(this); + future = backgroundExecutor.executeWithFuture(this); } public synchronized boolean addResult(JNode resultNode) { @@ -79,19 +78,17 @@ public class SearchTask extends CancelableBackgroundTask { } public synchronized void waitTask() { - if (future != null) { - try { - future.get(200, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - LOG.debug("Search task wait timeout"); - } catch (Exception e) { - LOG.warn("Search task wait error", e); - } finally { - future.cancel(true); - future = null; - } + if (future == null) { + return; + } + try { + future.get(200, TimeUnit.MILLISECONDS); + } catch (Exception e) { + LOG.warn("Search task wait error", e); + future.cancel(true); + } finally { + future = null; } - } @Override diff --git a/jadx-gui/src/main/java/jadx/gui/ui/panel/ProgressPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/panel/ProgressPanel.java index 4e721a7a6..65d9d4c30 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/panel/ProgressPanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/panel/ProgressPanel.java @@ -1,8 +1,6 @@ package jadx.gui.ui.panel; import java.awt.Dimension; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -11,18 +9,15 @@ import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; -import javax.swing.SwingWorker; import jadx.gui.jobs.ITaskProgress; import jadx.gui.ui.MainWindow; +import jadx.gui.utils.Icons; import jadx.gui.utils.UiUtils; -public class ProgressPanel extends JPanel implements PropertyChangeListener { - +public class ProgressPanel extends JPanel { private static final long serialVersionUID = -3238438119672015733L; - private static final Icon ICON_CANCEL = UiUtils.openSvgIcon("ui/close"); - private final JProgressBar progressBar; private final JLabel progressLabel; private final JButton cancelButton; @@ -43,8 +38,9 @@ public class ProgressPanel extends JPanel implements PropertyChangeListener { add(progressLabel); add(progressBar); - cancelButton = new JButton(ICON_CANCEL); - cancelButton.setPreferredSize(new Dimension(ICON_CANCEL.getIconWidth(), ICON_CANCEL.getIconHeight())); + Icon cancelIcon = Icons.ICON_CLOSE; + cancelButton = new JButton(cancelIcon); + cancelButton.setPreferredSize(new Dimension(cancelIcon.getIconWidth(), cancelIcon.getIconHeight())); cancelButton.setToolTipText("Cancel background jobs"); cancelButton.setBorderPainted(false); cancelButton.setFocusPainted(false); @@ -82,31 +78,6 @@ public class ProgressPanel extends JPanel implements PropertyChangeListener { progressBar.setStringPainted(true); } - @Override - public void propertyChange(PropertyChangeEvent evt) { - switch (evt.getPropertyName()) { - case "progress": - setProgress((Integer) evt.getNewValue()); - break; - - case "label": - setLabel((String) evt.getNewValue()); - break; - - case "visible": - setVisible(((Boolean) evt.getNewValue())); - break; - - case "indeterminate": - setIndeterminate(((Boolean) evt.getNewValue())); - break; - - case "cancel-visible": - cancelButton.setVisible(((Boolean) evt.getNewValue())); - break; - } - } - public void setLabel(String label) { progressLabel.setText(label); } @@ -115,19 +86,7 @@ public class ProgressPanel extends JPanel implements PropertyChangeListener { progressBar.setIndeterminate(newValue); } - public void changeLabel(SwingWorker task, String label) { - task.firePropertyChange("label", null, label); - } - - public void changeIndeterminate(SwingWorker task, boolean indeterminate) { - task.firePropertyChange("indeterminate", null, indeterminate); - } - - public void changeVisibility(SwingWorker task, boolean visible) { - task.firePropertyChange("visible", null, visible); - } - - public void changeCancelBtnVisible(SwingWorker task, boolean visible) { - task.firePropertyChange("cancel-visible", null, visible); + public void setCancelButtonVisible(boolean visible) { + cancelButton.setVisible(visible); } }