update for loading

This commit is contained in:
Greg DiCristofaro 2025-01-03 14:35:29 -05:00
parent c82bb93ce7
commit 14cb87e4fb
No known key found for this signature in database
3 changed files with 116 additions and 29 deletions

View File

@ -49,6 +49,8 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
private JPanel currentPanel;
private final Map<String, DataSourceProcessor> datasourceProcessorsMap = new HashMap<>();
private final PanelUpdateListener panelUpdateListener;
private String currentDsp;
@ -60,6 +62,7 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
AddImageWizardDataSourceSettingsVisual(AddImageWizardDataSourceSettingsPanel wizPanel) {
initComponents();
this.wizPanel = wizPanel;
this.panelUpdateListener = new PanelUpdateListener();
typePanel.setLayout(new BorderLayout());
discoverDataSourceProcessors();
currentDsp = ImageDSProcessor.getType(); //default value to the ImageDSProcessor
@ -107,24 +110,37 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
*/
@SuppressWarnings("deprecation")
private void updateCurrentPanel(JPanel panel) {
cleanupUpdateListener(currentPanel);
currentPanel = panel;
typePanel.removeAll();
typePanel.add(currentPanel, BorderLayout.CENTER);
typePanel.validate();
typePanel.repaint();
currentPanel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) {
wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid());
}
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) {
wizPanel.moveFocusToNext();
}
}
});
cleanupUpdateListener(currentPanel);
currentPanel.addPropertyChangeListener(panelUpdateListener);
this.wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid());
}
/**
* Removes PanelUpdateListener from panel if found.
* @param panel The panel from which to remove the listener.
*/
private void cleanupUpdateListener(JPanel panel) {
if (panel == null) {
return;
}
PropertyChangeListener[] listeners = panel.getPropertyChangeListeners();
if (listeners == null) {
return;
}
for (PropertyChangeListener listener: listeners) {
if (listener instanceof PanelUpdateListener) {
panel.removePropertyChangeListener(listener);
}
}
}
/**
* Returns the currently selected DS Processor
@ -221,4 +237,20 @@ final class AddImageWizardDataSourceSettingsVisual extends JPanel {
protected abstract boolean addSeparatorAfter(JList list, Object value, int index);
}
/**
* A property change listener that responds to update events.
*/
private class PanelUpdateListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) {
wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid());
}
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) {
wizPanel.moveFocusToNext();
}
}
}
}

View File

@ -163,7 +163,9 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
*/
@Override
public boolean isPanelValid() {
return configPanel.validatePanel();
// before attempting to validate the panel (a potentially long running operation),
// check if the validation is loading or on delay.
return !configPanel.isValidationLoading() && configPanel.validatePanel();
}
/**

View File

@ -67,7 +67,9 @@ public class ImageFilePanel extends JPanel {
private static int VALIDATE_TIMEOUT_MILLIS = 1200;
static ScheduledThreadPoolExecutor delayedValidationService = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("ImageFilePanel delayed validation").build());
private Future<Void> validateAction = null;
private final Object validateActionLock = new Object();
private Runnable validateAction = null;
private Future<?> validateFuture = null;
/**
* Creates new form ImageFilePanel
@ -632,10 +634,6 @@ public class ImageFilePanel extends JPanel {
showError(Bundle.ImageFilePanel_validatePanel_invalidSHA256());
return false;
}
if (!PathValidator.isValidForCaseType(path, Case.getCurrentCase().getCaseType())) {
showError(Bundle.ImageFilePanel_validatePanel_dataSourceOnCDriveError());
}
try {
String password = this.getPassword();
@ -652,8 +650,12 @@ public class ImageFilePanel extends JPanel {
showError(Bundle.ImageFilePanel_validatePanel_unknownError());
return false;
}
showError(null);
if (!PathValidator.isValidForCaseType(path, Case.getCurrentCase().getCaseType())) {
showError(Bundle.ImageFilePanel_validatePanel_dataSourceOnCDriveError());
} else {
showError(null);
}
return true;
}
@ -683,6 +685,16 @@ public class ImageFilePanel extends JPanel {
return true;
}
/**
* @return True if the panel is on a delay for validating (i.e. typing a
* password for bitlocker).
*/
public boolean isValidationLoading() {
synchronized (this.validateActionLock) {
return this.validateAction != null;
}
}
public void storeSettings() {
String imagePathName = getContentPaths();
@ -746,23 +758,64 @@ public class ImageFilePanel extends JPanel {
delayValidate();
}
private synchronized void delayValidate() {
if (ImageFilePanel.this.validateAction != null) {
ImageFilePanel.this.validateAction.cancel(true);
/**
* Run validation on a delay to avoid password checking too many times
* while typing.
*/
private void delayValidate() {
boolean triggerUpdate = false;
synchronized (ImageFilePanel.this.validateActionLock) {
if (ImageFilePanel.this.validateFuture != null &&
!ImageFilePanel.this.validateFuture.isCancelled() &&
!ImageFilePanel.this.validateFuture.isDone()) {
ImageFilePanel.this.validateFuture.cancel(true);
triggerUpdate = true;
}
ImageFilePanel.this.validateAction = new ValidationRunnable();
ImageFilePanel.this.validateFuture = ImageFilePanel.this.delayedValidationService.schedule(
ImageFilePanel.this.validateAction,
VALIDATE_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
}
// trigger invalidation after setting up new runnable if not already triggered
if (triggerUpdate) {
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
}
errorLabel.setVisible(false);
if (!ImageFilePanel.this.loadingLabel.isVisible()) {
ImageFilePanel.this.loadingLabel.setVisible(true);
}
}
/**
* Runnable to run the updateHelper if the validation action remains
* this runnable.
*/
private class ValidationRunnable implements Runnable {
ImageFilePanel.this.validateAction = ImageFilePanel.this.delayedValidationService.schedule(
() -> {
ImageFilePanel.this.updateHelper();
return null;
},
VALIDATE_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
@Override
public void run() {
if (Thread.interrupted()) {
return;
}
synchronized (ImageFilePanel.this.validateActionLock) {
if (ImageFilePanel.this.validateAction != this) {
return;
}
// set the validation action to null to indicate that this is done running and can be validated.
ImageFilePanel.this.validateAction = null;
ImageFilePanel.this.validateFuture = null;
}
ImageFilePanel.this.updateHelper();
}
}
}
}