Bug 475465 - NPE when image contains in tag in the Search dialog
Fixing the case where no value was put in the queue if a server
error occurred, which resulted in UI blocked for 10s (until
timeout) and then an NPE.
Added databinding validation support to validate the given
search term against a regex (as found in the CLI help)
Change-Id: Ia3eb1836904ce036ca3368043582b8979d3640d1
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Reviewed-on: https://git.eclipse.org/r/54227
Tested-by: Hudson CI
Reviewed-by: Roland Grunberg <rgrunber@redhat.com>
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageSearchPage.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageSearchPage.java
index 98cf5e5..52a5e18 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageSearchPage.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ImageSearchPage.java
@@ -12,22 +12,32 @@
package org.eclipse.linuxtools.internal.docker.ui.wizards;
import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.beans.PojoProperties;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.databinding.wizard.WizardPageSupport;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
@@ -78,7 +88,6 @@
super("ImageSearchPage", //$NON-NLS-1$
WizardMessages.getString("ImageSearchPage.title"), //$NON-NLS-1$
SWTImagesFactory.DESC_BANNER_REPOSITORY);
- setMessage(WizardMessages.getString("ImageSearchPage.title")); //$NON-NLS-1$
this.model = model;
}
@@ -170,23 +179,16 @@
final IObservableValue observableTermModel = BeanProperties
.value(ImageSearchModel.class, ImageSearchModel.TERM)
.observe(model);
- ctx.bindValue(
- WidgetProperties.text(SWT.Modify).observe(searchImageText),
- observableTermModel);
- // enable/disable the search button
- observableTermModel.addValueChangeListener(new IValueChangeListener() {
+ final UpdateValueStrategy strategy = new UpdateValueStrategy();
+ strategy.setBeforeSetValidator(new SearchTermValidator());
- @Override
- public void handleValueChange(final ValueChangeEvent event) {
- final String term = (String) event.getObservableValue()
- .getValue();
- if (term.isEmpty()) {
- searchImageButton.setEnabled(false);
- } else {
- searchImageButton.setEnabled(true);
- }
- }
- });
+ final ISWTObservableValue imageSearchTextObservable = WidgetProperties
+ .text(SWT.Modify).observe(searchImageText);
+ ctx.bindValue(imageSearchTextObservable, observableTermModel, strategy,
+ null);
+ // enable/disable the search button
+ imageSearchTextObservable
+ .addValueChangeListener(onTermValueChanged(searchImageButton));
// observe the viewer content
searchResultTableViewer
.setContentProvider(new ObservableListContentProvider());
@@ -211,6 +213,8 @@
ctx.bindValue(WidgetProperties.text().observe(selectedImageDescription),
observableSelectedImageDescription);
searchImageText.setFocus();
+ // attach the Databinding context status to this wizard page.
+ WizardPageSupport.create(this, this.ctx);
setControl(container);
}
@@ -229,6 +233,26 @@
return viewerColumn;
}
+ private IValueChangeListener onTermValueChanged(
+ final Button searchImageButton) {
+ return new IValueChangeListener() {
+
+ @Override
+ public void handleValueChange(final ValueChangeEvent event) {
+ final String searchTerm = (String) event.getObservableValue()
+ .getValue();
+ final IStatus status = AggregateValidationStatus
+ .getStatusMaxSeverity(
+ ctx.getValidationStatusProviders());
+ if (searchTerm.isEmpty() || !status.isOK()) {
+ searchImageButton.setEnabled(false);
+ } else {
+ searchImageButton.setEnabled(true);
+ }
+ }
+ };
+ }
+
private TraverseListener onSearchImageTextTraverse() {
return new TraverseListener() {
@@ -246,8 +270,12 @@
@Override
public void keyReleased(final KeyEvent event) {
- if (event.character == SWT.CR
- && !ImageSearchPage.this.model.getTerm().isEmpty()) {
+ final IStatus status = AggregateValidationStatus
+ .getStatusMaxSeverity(
+ ctx.getValidationStatusProviders());
+ final String searchTerm = ImageSearchPage.this.model.getTerm();
+ if (event.character == SWT.CR && !searchTerm.isEmpty()
+ && status.isOK()) {
searchImages();
}
}
@@ -285,6 +313,8 @@
searchResultQueue.offer(searchResults);
} catch (DockerException e) {
Activator.log(e);
+ searchResultQueue.offer(
+ new ArrayList<IDockerImageSearchResult>());
}
monitor.done();
}
@@ -328,6 +358,28 @@
return this.model.getSelectedImage() != null;
}
+ static class SearchTermValidator implements IValidator {
+
+ private static final Pattern termPattern = Pattern
+ .compile("[a-z0-9]+([._-][a-z0-9]+)*"); //$NON-NLS-1$
+
+ @Override
+ public IStatus validate(final Object value) {
+ final String term = (String) value;
+ if (term == null || term.isEmpty()) {
+ return ValidationStatus.info(WizardMessages
+ .getString("ImageSearchPage.description")); //$NON-NLS-1$
+ } else if (termPattern.matcher(term).matches()) {
+ return Status.OK_STATUS;
+ } else {
+ return ValidationStatus.error(WizardMessages.getFormattedString(
+ "ImageSearchPage.term.invalidformat", //$NON-NLS-1$
+ termPattern.pattern()));
+ }
+ }
+
+ }
+
static class ImageNameColumnLabelProvider extends ColumnLabelProvider {
@Override
@@ -401,7 +453,6 @@
}
abstract boolean doPaint(final Object element);
-
}
}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
index cc4f63d..5a5733c 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
@@ -170,6 +170,8 @@
ImageSearch.title=Search and pull a Docker image
ImageSearchPage.title=Search the Docker Registry for images
+ImageSearchPage.description=Input an image name as the search term
+ImageSearchPage.term.invalidformat=Search term must match {0}
ImageSearchPage.imageLabel=Image:
ImageSearchPage.search=Search
ImageSearchPage.searchResultLabel=Matching images