Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "JFace Data Binding/JSR303BeanJFaceDatabindingValidation"

(Eclipse RCP (OSGi context) sample - org.eclipse.core.databinding.validation.jsr303.samples.rcp)
(Eclipse RCP (OSGi context) sample - org.eclipse.core.databinding.validation.jsr303.samples.rcp)
Line 478: Line 478:
  
 
<ul>
 
<ul>
   <li>OSGi Fragment linked to javax.validation bundle which set the implementation to use.  
+
   <li>OSGi Fragment linked to javax.validation bundle which set the JSR-303 implementation to use.  
 
   </li>
 
   </li>
 
   <li>OSGi bundles which register the well JSR-303 ValidationFactory implementation in the OSGi registry services.
 
   <li>OSGi bundles which register the well JSR-303 ValidationFactory implementation in the OSGi registry services.
 
   </li>
 
   </li>
 
</ul>
 
</ul>

Revision as of 10:11, 28 October 2011

Target

Work is underway to support JSR-303 Bean Validation with JFace Databinding Validators. This support was created after reading the Validating JFace Databinding with JSR-303 article.

You can find several plug-in projects from JSR-303 XDocReport Git which provides JSR-303 support for JFace Databinding Validators:

  • org.eclipse.core.databinding.validation.jsr303 : JSR-303 support for JFace Databinding Validators source.
  • org.eclipse.core.databinding.validation.jsr303.samples : JSR-303 support for JFace Databinding Validators sample with Java main.
  • org.eclipse.core.databinding.validation.jsr303.samples.rcp : JSR-303 support for JFace Databinding Validators with Eclipse RCP (in an OSGi context).

JSR-303 support for JFace Databinding Validators

JSR-303 Overview

@Annotations for JSR-303

JSR-303 Bean Validation gives you the capability to declare with annotation your validation constraints in your Java Pojo model. Here an example with Person class to set validation constraints:

  • "name" property as required.
  • "email" property as email pattern
package org.eclipse.core.databinding.validation.jsr303.samples.model;
 
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
 
public class Person {
 
	@Size(min = 1)
	private String name;
 
	@Size(min = 1)
	@Pattern(regexp = ".+@.+\\.[a-z]+")
	private String email;
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public String getEmail() {
		return email;
	}
 
	public void setEmail(String email) {
		this.email = email;
	}
}

Those validation constraints can be used in any context :

  • UI form could display errors when constraints are not respected. Ex : UI Text field email doesn't contains a well formatted email like this : JSR303JFaceDatabindingSamples Jsr303PersonSnippetUI.png
  • ORM like Hibernate can use thoses constraints validation when Pojo model must be saved in the Database.

So JSR-303 gives a commons means to declare your constraint validation that you can used in several context of your application (in your UI form, in your DAO when Pojo must be saved, etc....)

Validator API for JSR-303

JSR-303 Bean Validation provides an API to validate property, value by using JSR-303 constraints validation declared with annotations. Here a sample code to validate the value "XXX" by using the annotations declared in the "email" property of the Person class :

...
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
...
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Set<ConstraintViolation<Person>> violations = factory.getValidator().validateValue(Person.class, "email", "XXX");
for (ConstraintViolation<Person> violation : violations) {
  System.err.println(violation.getMessage());
}

NOTE: this code works only if there is in the ClassPath an implementation of JSR-303 Bean Validation. If you have not an implementation, you will have this error :

Exception in thread "main" javax.validation.ValidationException: Unable to find a default provider
	at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:264)
	at javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:111)

Otherwise, with Apache implementation, teh console will display:

must match the following regular expression: .+@.+\.[a-z]+

Validator Implementation for JSR-303

JSR-303 Bean Validation provides an API but not an implementation. To execute validation with JSR-303 you need JSR-303 implementation. It exists several implementation like:

Provided samples with JFace Databinding support for JSR-303 use Hibernate Validator and Apache Bean Validation.

JSR-303 JFace Databinding Validator Overview

JFace Databinding Validator support for JSR-303 provides the org.eclipse.core.databinding.validation.jsr303.Jsr303BeanValidator an implementation of org.eclipse.core.databinding.validation.IValidator which uses Validator API for JSR-303.

Create Jsr303BeanValidator

If you wish create a JFace Databinding Validator to validate "email" property of the Person class :

public class Person {
 
  ...
  @Size(min = 1)
  @Pattern(regexp = ".+@.+\\.[a-z]+")
  private String email;
  ...
}

You can do that :

...
import org.eclipse.core.databinding.validation.jsr303;
import org.eclipse.core.databinding.validation.IValidator;
...
 
IValidator validator = new Jsr303BeanValidator(Person.class, "email");

Use Jsr303BeanValidator

At this step you can use the validator in your DatabindingContext by creating an instanceof of UpdateValueStrategy  :

IValidator validator = new Jsr303BeanValidator(Person.class, "email");
UpdateValueStrategy updateValueStrategy = new UpdateValueStrategy().setAfterConvertValidator(validator);

Here the full code to bind SWT Text with the "email" property of Person instance :

Text emailText = ....
Person model = ...
IObservableValue swtEmailTextObservableValue = SWTObservables.observeText(emailText, SWT.Modify);
IObservableValue modelEmailObserveValue = PojoObservables.observeValue(model, "email");
dataBindingContext.bindValue(
		swtEmailTextObservableValue,
		modelEmailObserveValue,
		new UpdateValueStrategy()
				.setAfterConvertValidator(new Jsr303BeanValidator(
						Person.class, "email")), null);

Use Jsr303UpdateValueStrategyFactory

You can simplify the creation of the JSR-303 UpdateValueStrategy. Instead of doing that:

UpdateValueStrategy updateValueStrategy = new UpdateValueStrategy().setAfterConvertValidator(new Jsr303BeanValidator(Person.class, "email"));

You can write :

UpdateValueStrategy updateValueStrategy = Jsr303UpdateValueStrategyFactory.create(Person.class, "email");

Here the full code to bind SWT Text with the "email" property of Person instance :

Text emailText = ....
Person model = ...
IObservableValue swtEmailTextObservableValue = SWTObservables.observeText(emailText, SWT.Modify);
IObservableValue modelEmailObserveValue = PojoObservables.observeValue(model, "email");
dataBindingContext.bindValue(
		swtEmailTextObservableValue,
		modelEmailObserveValue,
		Jsr303UpdateValueStrategyFactory.create(Person.class, "email"), null);

Use Jsr303BeansUpdateValueStrategyFactory

If you are using PojoObservables or BeansObservable, you can simplify more code, where you can avoid setting the Person.class and "email"to create teh well UpdateValueStrategy. When you do :

IObservableValue modelEmailObserveValue = PojoObservables.observeValue(model, "email")

You can know the Class type and the property name by using the IObservableValue. That's why you can simplify your code if you are using PojoObservables or BeansObservables by creating an instanceof of UpdateValueStrategy configured with JSR-303 by using information of IObservableValue with Jsr303BeansUpdateValueStrategyFactory like this :

IObservableValue modelEmailObserveValue = PojoObservables.observeValue(model, "email");
UpdateValueStrategy updateValueStrategy = Jsr303BeansUpdateValueStrategyFactory .create(modelEmailObserveValue);

Here the full code to bind SWT Text with the "email" property of Person instance :

Text emailText = ....
Person model = ...
IObservableValue swtEmailTextObservableValue = SWTObservables.observeText(emailText, SWT.Modify);
IObservableValue modelEmailObserveValue = PojoObservables.observeValue(model, "email");
dataBindingContext.bindValue(
		swtEmailTextObservableValue,
		modelEmailObserveValue,
		Jsr303BeansUpdateValueStrategyFactory .create(modelEmailObserveValue), null);

Java main sample - org.eclipse.core.databinding.validation.jsr303.samples

The Jsr303PersonSnippet from the org.eclipse.core.databinding.validation.jsr303.samples project bind Person model :

public class Person {
 
  Size(min = 1)
  private String name;
  ...
  @Size(min = 1)
  @Pattern(regexp = ".+@.+\\.[a-z]+")
  private String email;
  ...
}

with UI SWT Text and manage validation with JSR-303 support for JFace Databinding Validator.

JSR303JFaceDatabindingSamples Jsr303PersonSnippetUI.png

Workspace

Here a screen of required projects to launch the Jsr303PersonSnippet :

JSR303JFaceDatabindingSamples Workspace.png

The org.eclipse.core.databinding.validation.jsr303.samples project provides several launch in the /launch folder :

  • Jsr303PersonSnippet_ApacheBvalValidator.launch: launch Jsr303PersonSnippet by using Apache Bean Validator. This launch add the org.apache.bval.org.apache.bval.bundle project in the classpath to use the Apache Bean Validator.
  • Jsr303PersonSnippet_HibernateValidator.launch: launch Jsr303PersonSnippet by using Hibernate Validator. This launch add the org.hibernate.validator (and sfl4j) project in the classpath to use the Hibernate Validator.
  • Jsr303PersonSnippet_WithoutImplementationOfValidator.launch: launch Jsr303PersonSnippet without JSR-303 Bean Validation implementation to see the problem (validation can be occurred).

Run Jsr303PersonSnippet

Here a screen with Apache Bean Validator implementation (Jsr303PersonSnippet_ApacheBvalValidator.launch launch) which display error when "email" field is not valid :

JSR303JFaceDatabindingSamples Jsr303PersonSnippetUI.png

You will notice that in your console some trace are displayed :

JSR-303 Bean Support available?: true
OSGi context?: false
JSR-303 Bean Support ValidatorFactory Implementation: org.apache.bval.jsr303.ApacheValidatorFactory
JSR-303 Bean Support strategy resolver?: NoOSgi

Those trace use Jsr303BeanValidationSupport :

System.out.println("JSR-303 Bean Support available?: " + Jsr303BeanValidationSupport.isAvailable());
System.out.println("OSGi context?: " + Jsr303BeanValidationSupport.isOSGi());
System.out.println("JSR-303 Bean Support ValidatorFactory Implementation: " + Jsr303BeanValidationSupport.getValidatorFactoryClassName());
System.out.println("JSR-303 Bean Support strategy resolver?: " + Jsr303BeanValidationSupport.getStrategy());

Here a screen when there is no JSR-303 implementation (Jsr303PersonSnippet_WithoutImplementationOfValidator.launch launch) which display "Unable to find a default provider" because there none implementation :

JSR303JFaceDatabindingSamples Jsr303PersonSnippetUIWithoutImpl.png


You will notice that in your console some trace are displayed :

JSR-303 Bean Support available?: false
OSGi context?: false
JSR-303 Bean Support ValidatorFactory Implementation: null
JSR-303 Bean Support strategy resolver?: Unavailable

Java code Jsr303PersonSnippet

Here the full Java code of Jsr303PersonSnippet :

package org.eclipse.core.databinding.validation.jsr303.samples;
 
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.validation.jsr303.Jsr303BeanValidationSupport;
import org.eclipse.core.databinding.validation.jsr303.Jsr303BeansUpdateValueStrategyFactory;
import org.eclipse.core.databinding.validation.jsr303.samples.model.Person;
import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
 
public class Jsr303PersonSnippet {
 
	public static void main(String[] args) {
		// Display state of JSR-303 Bean Support
		System.out.println("JSR-303 Bean Support available?: "
				+ Jsr303BeanValidationSupport.isAvailable());
		System.out.println("OSGi context?: "
				+ Jsr303BeanValidationSupport.isOSGi());
		System.out
				.println("JSR-303 Bean Support ValidatorFactory Implementation: "
						+ Jsr303BeanValidationSupport
								.getValidatorFactoryClassName());
		System.out.println("JSR-303 Bean Support strategy resolver?: "
				+ Jsr303BeanValidationSupport.getStrategy());
 
		// Create UI+Binding
		final Display display = new Display();
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
			public void run() {
				final Shell shell = new Shell(display);
 
				shell.setLayout(new FillLayout());
 
				Composite parent = new Composite(shell, SWT.NONE);
				parent.setLayout(new GridLayout(2, false));
				parent.setLayoutData(new GridData(GridData.FILL_BOTH));
				Person model = new Person();
 
				// UI Name
				Label nameLabel = new Label(parent, SWT.NONE);
				nameLabel.setText("Name:");
				Text nameText = new Text(parent, SWT.BORDER);
				nameText.setLayoutData(new GridData(GridData.FILL_BOTH));
 
				// UI Email
				Label emailLabel = new Label(parent, SWT.NONE);
				emailLabel.setText("Email:");
				Text emailText = new Text(parent, SWT.BORDER);
				emailText.setLayoutData(new GridData(GridData.FILL_BOTH));
 
				DataBindingContext dataBindingContext = new DataBindingContext(
						SWTObservables.getRealm(display));
 
				// Binding Name
				IObservableValue nameTextObserveTextObserveWidget = SWTObservables
						.observeText(nameText, SWT.Modify);
				IObservableValue modelNameObserveValue = PojoObservables
						.observeValue(model, "name");
 
				Binding binding = dataBindingContext.bindValue(
						nameTextObserveTextObserveWidget,
						modelNameObserveValue,
						Jsr303BeansUpdateValueStrategyFactory
								.create(modelNameObserveValue), null);
				ControlDecorationSupport.create(binding, SWT.LEFT, parent);
 
				// Binding Email
				IObservableValue emailTextObserveTextObserveWidget = SWTObservables
						.observeText(emailText, SWT.Modify);
				IObservableValue modelEmailObserveValue = PojoObservables
						.observeValue(model, "email");
 
				binding = dataBindingContext.bindValue(
						emailTextObserveTextObserveWidget,
						modelEmailObserveValue,
						Jsr303BeansUpdateValueStrategyFactory
								.create(modelEmailObserveValue), null);
				ControlDecorationSupport.create(binding, SWT.LEFT, parent);
 
				shell.setSize(200, 100);
				shell.open();
 
				// The SWT event loop
				Display display = Display.getCurrent();
				while (!shell.isDisposed()) {
					if (!display.readAndDispatch()) {
						display.sleep();
					}
				}
			}
		});
	}
}

Here the full Java code of Person :

package org.eclipse.core.databinding.validation.jsr303.samples.model;
 
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
 
public class Person {
 
	@Size(min = 1)
	private String name;
 
	@Size(min = 1)
	@Pattern(regexp = ".+@.+\\.[a-z]+")
	private String email;
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public String getEmail() {
		return email;
	}
 
	public void setEmail(String email) {
		this.email = email;
	}
}

Eclipse RCP (OSGi context) sample - org.eclipse.core.databinding.validation.jsr303.samples.rcp

The org.eclipse.core.databinding.validation.jsr303.samples.rcp project is an Eclipse RCP which uses JSR-303 JFace Databinding Validators support in an OSGi context to bind Person model :

public class Person {
 
  Size(min = 1)
  private String name;
  ...
  @Size(min = 1)
  @Pattern(regexp = ".+@.+\\.[a-z]+")
  private String email;
  ...
}

with UI SWT Text from a FormEditor and manage validation with JSR-303 support for JFace Databinding Validator :

JSR303JFaceDatabindingSamples EclipseRCPFragmentConfigUI.png

This application is sample which shows you how to manage JSR-303 in an OSGi context. There are 2 means to do that:

  • OSGi Fragment linked to javax.validation bundle which set the JSR-303 implementation to use.
  • OSGi bundles which register the well JSR-303 ValidationFactory implementation in the OSGi registry services.

Back to the top