org.apache.cayenne.modeler.util.LongRunningTask Maven / Gradle / Ivy
/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package org.apache.cayenne.modeler.util;
import javax.swing.*;
import org.apache.cayenne.CayenneRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A base class for monitoring progress of long running tasks. It can runshowing the exact
* percentage of the task progress or in "indeterminate" mode.
*
* Warning: If the task started via "startAndWait()", caller must ensure that she is
* not running in the Swing EventDispatchThread, otherwise an exception is thrown, as the
* EvenDispatchThread will be blocked, preventing LongRunningTask from showing progress
* dialog.
*
*
*/
public abstract class LongRunningTask {
private static final Logger LOGGER = LoggerFactory.getLogger(LongRunningTask.class);
protected static final int DEFAULT_MS_TO_DECIDE_TO_POPUP = 500;
protected ProgressDialog dialog;
protected JFrame frame;
protected String title;
protected Timer taskPollingTimer;
protected boolean canceled;
protected int minValue;
protected int maxValue;
protected boolean finished;
protected T result;
public LongRunningTask(JFrame frame, String title) {
this.frame = frame;
this.title = title;
}
/**
* Starts current task, and blocks current thread until the task is done.
*/
public synchronized T startAndWait() {
// running from Event Dispatch Thread is bad, as this will block the timers...
if (SwingUtilities.isEventDispatchThread()) {
throw new CayenneRuntimeException(
"Can't block EventDispatchThread. Call 'startAndWait' from another thread.");
}
start();
if (finished) {
return result;
}
try {
wait();
}
catch (InterruptedException e) {
setCanceled(true);
}
notifyAll();
return result;
}
/**
* Configures the task to run in a separate thread, and immediately exits the method.
* This method is allowed to be invoked from EventDispatchThread.
*/
public void start() {
// prepare...
setCanceled(false);
this.finished = false;
Thread task = new Thread(this::internalExecute);
Timer progressDisplayTimer = new Timer(
DEFAULT_MS_TO_DECIDE_TO_POPUP,
e -> showProgress());
progressDisplayTimer.setRepeats(false);
// start
progressDisplayTimer.start();
task.start();
}
/**
* Starts progress dialog if the task is not finished yet.
*/
protected synchronized void showProgress() {
LOGGER.debug("will show progress...");
if (finished) {
return;
}
int currentValue = getCurrentValue();
if (!isCanceled() && currentValue < getMaxValue()) {
LOGGER.debug("task still in progress, will show progress dialog...");
this.dialog = new ProgressDialog(frame, "Progress...", title);
this.dialog.getCancelButton().addActionListener(e -> setCanceled(true));
dialog.getProgressBar().setMinimum(getMinValue());
dialog.getProgressBar().setMaximum(getMaxValue());
updateProgress();
this.taskPollingTimer = new Timer(500, e -> updateProgress());
this.taskPollingTimer.start();
this.dialog.setVisible(true);
}
}
/**
* Updates current state of the progress dialog.
*/
protected void updateProgress() {
if (isCanceled()) {
stop();
return;
}
dialog.getStatusLabel().setText(getCurrentNote());
JProgressBar progressBar = dialog.getProgressBar();
if (!isIndeterminate()) {
progressBar.setValue(getCurrentValue());
progressBar.setIndeterminate(false);
}
else {
progressBar.setIndeterminate(true);
}
}
protected synchronized void stop() {
if (taskPollingTimer != null) {
taskPollingTimer.stop();
}
if (dialog != null) {
dialog.dispose();
}
// there maybe other threads waiting on this task to finish...
finished = true;
notifyAll();
}
public boolean isFinished() {
return finished;
}
public boolean isCanceled() {
return canceled;
}
public void setCanceled(boolean b) {
if (b) {
LOGGER.debug("task canceled");
}
this.canceled = b;
}
protected void internalExecute() {
try {
execute();
}
catch (Throwable th) {
setCanceled(true);
LOGGER.warn("task error", th);
}
finally {
stop();
}
}
/**
* Runs the actual task, consulting "isCanceled()" state to make sure that the task
* wasn't canceled.
*/
protected abstract void execute();
protected abstract String getCurrentNote();
protected abstract int getCurrentValue();
protected abstract boolean isIndeterminate();
public int getMaxValue() {
return maxValue;
}
public void setMaxValue(int maxValue) {
this.maxValue = maxValue;
}
public int getMinValue() {
return minValue;
}
public void setMinValue(int minValue) {
this.minValue = minValue;
}
}