[JBoss Tools] - Eclipse wizards with jface databinding in Deltacloud Tools (1)
by Andre Dietisheim
Andre Dietisheim [http://community.jboss.org/people/adietish] modified the document:
"Eclipse wizards with jface databinding in Deltacloud Tools (1)"
To view the document, visit: http://community.jboss.org/docs/DOC-15964
--------------------------------------------------------------
h1. Less code
If you use jface databinding to code your swt views in Eclipse you'll get spared from writing listeners and updating code by hand. JFace databinding offers very nice abstractions and automatisms that offer funcitonalities for the vast majority of the tasks where you have to listen for user input and update a view accordingly.
h1. Premise
If you implement a complex and highly dynamic UI in Eclipse you'll have to code many listener that wait for user actions. Those listeners mostly do nothing spectacular but update widgets and models in reaction to user inputs. You end up with a lot of repetitive boilerplate code. UI frameworks in the non-java land (ex. http://qt.nokia.com/products/ Trolltechs QT) always had approaches that were far more elegant than what we usually did in Swing and SWT. Those so called binding frameworks slowly made it into http://jgoodies.com/ Swing and http://wiki.eclipse.org/index.php/JFace_Data_Binding Eclipse. Nowadays Eclipse offers a very mature (developed since 2006) framework that spares you from boilerplate and leads to far more concise and a less verbose implementations: Jface Databinding!
h1. Usecase
In Deltacloud tools, we had to implement a wizard page that allows a user to create a connection to a Deltacloud server:
The user has to supply a name, an url and the credentials for it.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65...
Sounds pretty simple at first sight. Looking closer at it you'll discover few buttons and labels, that shall get updated upon user inputs. We need to inform the user in a intuitive way and mark form fields by error decorations. The wizard page shall also show a global error message that reflects the errors in a human readable manner.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65...
h1.
We choose to use jface databinding and not to stick to the traditional, tedious approach. I'll discuss our implementation in a simplified way. You may have a look at the complete implementation at http://anonsvn.jboss.org/repos/jbosstools/trunk/deltacloud/plugins/org.jb... CloudConnectionPage.java
h1.
h1. Let us get to the (little) code
h2. The user must provide a name for the connection
We stick to a fairly simple use case and discuss all steps that we have to take: The use musn't leave the name blank. He has name the connection.
The picture above shows the various ways we implement to inform the user. As long as no name is provided:
* We decorate the name filed with a error icon
* We provide a meaningful error message in the title area of our page
* We disable the finish-button
h1. **
h2.
h2. We need a text widget for the name
To start, we need a text field to hold the user inputs. The classic approach would be to create a SWT text wiget and attach a ModifyLlistener to it. We would implement this listener by validating the user input. Jface databinding spares us from writing this listener:
Text nameText = new Text(parent, SWT.BORDER | SWT.SINGLE);
IObservableValue observableText = WidgetProperties.text(SWT.Modify).observe(nameText);
JFace databinding operates on *Observables*. Observables provide a standardized observer pattern used across jface databinding. No matter what you observe, a widget, a bean, etc. you always listen to changes that occour in a jface observable.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65...
The code shown above attaches a ModifyListener to the text field and wraps it in an *Observable*. We still have no validation. We'll get to this in a few steps.
h2.
h2. We need a model to store the name
Jface databinding is a framework that is built upon the principle of model-view-controller (*MVC*). It connects a view to a model and transfers the user inputs to the model. It of course also operates the other way round and updates the view if new values are set to the model.
We'll now create a simple bean that holds the name of our connection:
public class CloudConnectionModel extends ObservablePojo {
public static final String PROPERTY_NAME = "name";
private String name;
public CloudConnectionModel(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
getPropertyChangeSupport().
firePropertyChange(PROPERTY_NAME, this.name, this.name = name);
}
}
Our model is a simple bean and provides getters and setter for the name property. It uses PropertyChangeSupport to inform observers about changes to the name of our connection. PropertyChangeSupport, an observer pattern shipped with the jdk, allows us to get informed of changes. Since jface uses *Observables*, we have to wrap our name property to conform to the *Observable* interface:
IObservableValue observableName =
BeanProperties.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel),
h2.
h2. Databinding, transfer the user input to our model!
Now that we have observables on both ends (model and widget), we can tell databinding to connect both ends. JFace databinding will then propagate user inputs to our model. It will also transfer changes in the model to the widget. A so called *Binding* may be created on behalf of the *DataBindingContext:*
DataBindingContext dbc = new DataBindingContext();
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel));
h2. But hey, refuse invalid names
We now told databinding to transfer the user input to our model. What we still miss is the ability to constrain user inputs.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65...
Jface databinding offers the ability to convert and validate values while they are transferred from one end to the other. We can tell databinding to validate the name before it sets the user input to the model. Databinding uses *IValidator* instances for that sake:
public class MandatoryStringValidator implements IValidator {
private String errorMessage;
public MandatoryStringValidator(String errorMessage) {
this.errorMessage = errorMessage;
}
/**
*
* validates the given string. Validation passes only if the given value is
* not <tt>null</tt> and it's length's larger than 0
*
*/
public IStatus validate(Object value) {
if (!((value instanceof String) && ((String) value).length() > 0)) {
return ValidationStatus.error(errorMessage);
}
return ValidationStatus.ok();
}
}
The validator shown above validates ok if the user provides any name. It fails if no name is set to the text widget.
To instruct jface databinding to use our validator, we have to wire it into a so called *UpdateValueStrategy*. As we saw above, databinding transfers values both ways: from the view to the model and from the model to the view. A binding therefore has 2 update strategies. A first one that gets applied when values are transfered from the view to the model and another one that is applied when transferring from the model to the view. We only want to validate names that the user provides. We therefore keep the default (*null*) strategy for updates that occur in the model:
Binding nameTextBinding =
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties
.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel),
new UpdateValueStrategy().setBeforeSetValidator(
new MandatoryStringValidator("You must name the connection")),
null);
h2. Show me the errors
We now may display the result of the validation. We want the text field to be decorated:
h3.
Field decoration
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65...
ControlDecorationSupport.create(nameTextBinding, SWT.LEFT | SWT.TOP);
h3. Wizard page error
We also want to get informed in the title area of our wizard page.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65...
A single call to jface databinding achieves this for you:
WizardPageSupport.create(this, dbc);
h3. Finish button enablement
There's a single missing piece: We also want to disable the finish button as soon as validation fails:
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-105... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65...
In the classic approach, one would have to implement *WizardPage#isPageComplete*.
public abstract class WizardPage extends DialogPage implements IWizardPage {
/**
* The <code>WizardPage</code> implementation of this <code>IWizard</code> method
* returns the value of an internal state variable set by
* <code>setPageComplete</code>. Subclasses may extend.
*/
public boolean isPageComplete() {
And here's what you have to do using jface databinding:
h4. Nothing, not a single line of additional code.
*WizardPageSupport#create* displays the validation messages in the title area but also connects the validation state to the page completion state.
h1. All the code you need
To sum up, the whole databinding code looks like the following:
DataBindingContext dbc = new DataBindingContext();
WizardPageSupport.create(this, dbc);
Text nameText = new Text(parent, SWT.BORDER | SWT.SINGLE);
Binding nameTextBinding =
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties
.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel),
new UpdateValueStrategy().setBeforeSetValidator(
new MandatoryStringValidator("You must name the connection")),
null
);
ControlDecorationSupport.create(nameTextBinding, SWT.LEFT | SWT.TOP);
h1. Conclusion
JFace databinding brings a fresh breath into UI programming compared to what was state of the art when swing was introduced. It lessens boilerplate considerably and brings well adapted paradigms to what's needed when building UIs with java. Your code gets more concise, maintainable and your work gets more effective.
JFace databinding exists for 5 years now and a large userbase made it stable and mature over the years. There's really no reason not to use it. You wont regret it.
--------------------------------------------------------------
Comment by going to Community
[http://community.jboss.org/docs/DOC-15964]
Create a new document in JBoss Tools at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&co...]
14 years, 2 months
[EJB3] - Re: EJB 3.1 beans in jars in a war packaged app do not get deployed
by Marek Dec
Marek Dec [http://community.jboss.org/people/marekdec] created the discussion
"Re: EJB 3.1 beans in jars in a war packaged app do not get deployed"
To view the discussion, visit: http://community.jboss.org/message/572301#572301
--------------------------------------------------------------
Hi Jaikiran,
I found an answer to my problem (thanks to your suggestion). I skipped some files in the structure I posted before as I assumed they were insignificant. I was wrong again. It turns out I put a +jboss-scanning.xml+ to work around the JBAS-8361 issue (richfaces with guava). I must have been blind not to see it before.
Anyhow, thanks a lot for your help.
Just for reference, this was my +jboss-scanning.xml+ file:
<scanning xmlns="urn:jboss:scanning:1.0">
<!--
For JBoss AS 6 integration there is a conflict with guava, and
google-collections. JBAS-8361
-->
<path name="WEB-INF/classes"></path>
<path name="WEB-INF/lib/guava-r05.jar">
<exclude name="com.google.common.collect" />
</path>
</scanning>
{code}
This obviously defines the 'includes' and overrides the default scanning paths. I added the <path name="WEB-INF/lib"></path> tag at the end in order to get JBoss to scan the whole +lib+ directory.
--------------------------------------------------------------
Reply to this message by going to Community
[http://community.jboss.org/message/572301#572301]
Start a new discussion in EJB3 at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=1&cont...]
14 years, 2 months
[JBoss Tools] - Eclipse wizards with jface databinding in Deltacloud Tools (1)
by Andre Dietisheim
Andre Dietisheim [http://community.jboss.org/people/adietish] modified the document:
"Eclipse wizards with jface databinding in Deltacloud Tools (1)"
To view the document, visit: http://community.jboss.org/docs/DOC-15964
--------------------------------------------------------------
h1. Less code
If you use jface databinding to code your swt views in Eclipse you'll get spared from writing listeners and updating code by hand. JFace databinding offers very nice abstractions and automatisms that offer funcitonalities for the vast majority of the tasks where you have to listen for user input and update the view accordingly.
h1. Premise
If you implement a complex and highly dynamic UI in Eclipse you'll have to code many listener that wait for user actions. Those listeners mostly do nothing spectacular but update widgets and models in reaction to the user inputs. You end up with a lot of repetitive boilerplate code. UI frameworks in the non-java land (ex. http://qt.nokia.com/products/ Trolltechs QT) always had approaches that were far more elegant than what we usually do for Swing and SWT.
Eclipse offers a very mature (develped since 2006) framework that spares you from boilerplate and leads to far more concise and a less verbose implementations: Jface Databinding!
h1. Usecase
In deltacloud tools, we had to implement a wizard page that allows a user to create a connection to a deltacloud server:
The user has to supply a name, an url and the credentials for it.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-64-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-64...
Sounds pretty simple at first sight. Looking closer at it you'll discover few buttons and labels, that shall get updated upon user inputs. We need to inform the user in a intuitive way and mark form fields by error decorations. The wizard page shall also show a global error message that reflects the errors in a human readable manner.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-64-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-64...
h1.
You may have a look at the implementation we discuss here at http://anonsvn.jboss.org/repos/jbosstools/trunk/deltacloud/plugins/org.jb... CloudConnectionPage.java
h1.
h1. Let us get to the (little) code
h2. The user must provide a name for the connection
We stick to a fairly simple use case and discuss all steps that we have to take: The use musn't leave the name blank. He has name the connection.
The picture above shows the various ways we implement to inform the user. As long as no name is provided:
* We decorate the name filed with a error icon
* We provide a meaningful error message in the title area of our page
* We disable the finish-button
h1. **
h2.
h2. We need a text widget for the name
To start, we need a text field to hold the user inputs. The classic approach would be to create a SWT text wiget and attach a ModifyLlistener to it. We would implement this listener by validating the user input. Jface databinding spares us from writing this listener:
Text nameText = new Text(parent, SWT.BORDER | SWT.SINGLE);
IObservableValue observableText = WidgetProperties.text(SWT.Modify).observe(nameText);
JFace databinding operates on *Observables*. Observables provide a standardized observer pattern used across jface databinding. No matter what you observe, a widget, a bean, etc. you always listen to changes that occour in a jface observable.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-64-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-64...
The code shown above attaches a ModifyListener to the text field and wraps it in an *Observable*. We still have no validation. We'll get to this in a few steps.
h2.
h2. We need a model to store the name
Jface databinding is a framework that is built upon the principle of model-view-controller (*MVC*). It connects a view to a model and transfers the user inputs to the model. It of course also operates the other way round and updates the view if new values are set to the model.
We'll now create a simple bean that holds the name of our connection:
public class CloudConnectionModel extends ObservablePojo {
public static final String PROPERTY_NAME = "name";
private String name;
public CloudConnectionModel(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
getPropertyChangeSupport().
firePropertyChange(PROPERTY_NAME, this.name, this.name = name);
}
}
Our model is a simple bean and provides getters and setter for the name property. It uses PropertyChangeSupport to inform observers about changes to the name of our connection. PropertyChangeSupport, an observer pattern shipped with the jdk, allows us to get informed of changes. Since jface uses *Observables*, we have to wrap our name property to conform to the *Observable* interface:
IObservableValue observableName =
BeanProperties.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel),
h2.
h2. Databinding, transfer the user input to our model!
Now that we have observables on both ends (model and widget), we can tell databinding to connect both ends. JFace databinding will then propagate user inputs to our model. It will also transfer changes in the model to the widget. A so called *Binding* may be created on behalf of the *DataBindingContext:*
DataBindingContext dbc = new DataBindingContext();
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel));
h2. But hey, refuse invalid names
We now told databinding to transfer the user input to our model. What we still miss is the ability to constrain user inputs.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-64-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-64...
Jface databinding offers the ability to convert and validate values while they are transferred from one end to the other. We can tell databinding to validate the name before it sets the user input to the model. Databinding uses *IValidator* instances for that sake:
public class MandatoryStringValidator implements IValidator {
private String errorMessage;
public MandatoryStringValidator(String errorMessage) {
this.errorMessage = errorMessage;
}
/**
*
* validates the given string. Validation passes only if the given value is
* not <tt>null</tt> and it's length's larger than 0
*
*/
public IStatus validate(Object value) {
if (!((value instanceof String) && ((String) value).length() > 0)) {
return ValidationStatus.error(errorMessage);
}
return ValidationStatus.ok();
}
}
The validator shown above validates ok if the user provides any name. It fails if no name is set to the text widget.
To instruct jface databinding to use our validator, we have to wire it into a so called *UpdateValueStrategy*. As we saw above, databinding transfers values both ways: from the view to the model and from the model to the view. A binding therefore has 2 update strategies. A first one that gets applied when values are transfered from the view to the model and another one that is applied when transferring from the model to the view. We only want to validate names that the user provides. We therefore keep the default (*null*) strategy for updates that occur in the model:
Binding nameTextBinding =
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties
.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel),
new UpdateValueStrategy().setBeforeSetValidator(
new MandatoryStringValidator("You must name the connection")),
null);
h2. Show me the errors
We now may display the result of the validation. We want the text field to be decorated:
h3.
Field decoration
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-64-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-64...
ControlDecorationSupport.create(nameTextBinding, SWT.LEFT | SWT.TOP);
h3. Wizard page error
We also want to get informed in the title area of our wizard page.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-64-106... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-64...
A single call to jface databinding achieves this for you:
WizardPageSupport.create(this, dbc);
h3. Finish button enablement
There's a single missing piece: We also want to disable the finish button as soon as validation fails:
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-64-105... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-64...
In the classic approach, one would have to implement *WizardPage#isPageComplete*.
public abstract class WizardPage extends DialogPage implements IWizardPage {
/**
* The <code>WizardPage</code> implementation of this <code>IWizard</code> method
* returns the value of an internal state variable set by
* <code>setPageComplete</code>. Subclasses may extend.
*/
public boolean isPageComplete() {
And here's what you have to do using jface databinding:
h4. Nothing, not a single line of additional code.
*WizardPageSupport#create* displays the validation messages in the title area but also connects the validation state to the page completion state.
h1. All the code you need
To sum up, the whole databinding code looks like the following:
DataBindingContext dbc = new DataBindingContext();
WizardPageSupport.create(this, dbc);
Text nameText = new Text(parent, SWT.BORDER | SWT.SINGLE);
Binding nameTextBinding =
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties
.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel),
new UpdateValueStrategy().setBeforeSetValidator(
new MandatoryStringValidator("You must name the connection")),
null
);
ControlDecorationSupport.create(nameTextBinding, SWT.LEFT | SWT.TOP);
h1. Conclusion
JFace databinding brings a fresh breath into UI programming compared to what was state of the art when swing was introduced. It lessens boilerplate considerably and brings well adapted paradigms to what's needed when building UIs with java. Your code gets more concise, maintainable and your work gets more effective.
JFace databinding exists for 5 years now and a large userbase made it stable and mature over the years. There's really no reason not to use it. You wont regret it.
--------------------------------------------------------------
Comment by going to Community
[http://community.jboss.org/docs/DOC-15964]
Create a new document in JBoss Tools at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&co...]
14 years, 2 months
Which HA-JMS ConnectionFactory?
by Gerald Turner
How should I code transacted HA-JMS sender and receiver non-EJB clients
deployed within a cluster of JBoss containers?
The question is tricky because: we're using JBoss 4.2.3 in "all"
configuration, but would like to upgrade to 5.1 without code changes;
the clients are SAR deployments of custom MBeans; no EJB/MDB whatsoever.
Various informal documentation I've been able to scrape from the web
suggests looking up the ConnectionFactory from local (non-HA) JNDI with
name "java:/JmsXA" and looking up the Queue from HA-JNDI.
JmsXA is not working because evidently (and as it's name suggests) it is
an XAConnectionFactory, and the code:
QueueSession session =
connection.createQueueSession(true, Session.SESSION_TRANSACTED);
…is not actually honoring the transcted/mode arguments (javadoc: "usage
undefined"¹). This becomes a problem when the receiver code issues
session.commit() or session.rollback() which raise the exception
"TransactionInProgressException: Should not be call from a XASession".
I've searched for examples of using XASession from a non-EJB client and
haven't come up with anything. I suppose I could try experimenting with
session.getXAResource², but first problem faced would be how to allocate
Xid objects, and then I imagine there will be failures because the
underlying DataSource isn't XA (MySQL Cluster/NDB storage engine doesn't
support XA).
Avoiding the XA mess and trying ConnectionFactory lookup with JNDI name
"java:/ConnectionFactory" isn't working either: this object only exists
on the master nodes local JNDI, it's not exported to HA-JNDI. I suppose
I'll have to do remote JNDI connection to the master node? That sounds
awful since I'll also need to detect and reconnect on master node
failover.
Does JBoss 5.1 / JBoss Messaging "fix" any of this? I could change
targets but I'm having massive interoperability problems between 4.2 and
5.1 in other applications (several clusters with total of 300+ JBoss
application servers, "fork-lift" upgrade of our applications isn't
pretty).
Any advice would be much appreciated!
¹ http://download.oracle.com/javaee/5/api/javax/jms/XAConnection.html#creat...
² http://download.oracle.com/javaee/5/api/javax/jms/XASession.html#getXARes...
--
Gerald Turner Email: gturner(a)unzane.com JID: gturner(a)unzane.com
GPG: 0xFA8CD6D5 21D9 B2E8 7FE7 F19E 5F7D 4D0C 3FA0 810F FA8C D6D5
14 years, 2 months
[JBoss Tools] - How we use jface databinding in Deltacloud Tools
by Andre Dietisheim
Andre Dietisheim [http://community.jboss.org/people/adietish] modified the document:
"How we use jface databinding in Deltacloud Tools"
To view the document, visit: http://community.jboss.org/docs/DOC-15964
--------------------------------------------------------------
h1. Less code
If you use jface databinding to code your swt views in Eclipse you'll get spared from writing listeners and updating code by hand. JFace databinding offers very nice abstractions and automatisms that offer funcitonalities for the vast majority of the tasks where you have to listen for user input and update the view accordingly.
h1. *Premise*
If you implement a complex and highly dynamic UI in Eclipse you'll have to code many many many listener that wait for user actions. Those listeners mostly do nothing spectacular but update widgets and models in reaction to the user inputs. You end up with a lot of repetitive boilerplate code. UI frameworks in the non-java land (ex. http://qt.nokia.com/products/ Trolltechs QT) already have approaches that are far more elegant and slick than what we knew for Swing and SWT.
By luck Eclipse has progressed in this area (since 2005!) and offers neat abstractions that help a lot in this area and lead to far more concise and a less verbose implementations: Jface Databinding!
h1. Usecase
We had to implement a wizard page that allows a user to create a connection to a deltacloud server. The user has to supply a name, an url and the credentials for it.
http://community.jboss.org/servlet/JiveServlet/showImage/10530/cloud-conn... http://community.jboss.org/servlet/JiveServlet/downloadImage/10530/cloud-...
Sounds pretty simple at first sight. Looking closer at it you'll discover few buttons that shall be disabled if you enter inacurrate values. Furthermore we need to inform the user in a intuitive way and mark them by error decorations. The wizard page shall also show a global error message that reflects what's wrong in what the user entered.
http://community.jboss.org/servlet/JiveServlet/showImage/10531/invalid-ur... http://community.jboss.org/servlet/JiveServlet/downloadImage/10531/invali...
h1.
h1. Where's the source?
You may have a look at the implementation we discuss here at http://anonsvn.jboss.org/repos/jbosstools/trunk/deltacloud/plugins/org.jb... CloudConnectionPage.java
h1. *A first step: only use unique names*
Our GUI holds multiple connections to various deltacloud servers. The wizard allows you to edit existing connections or create new ones. The may of course not use names that are already picked for other connections.
http://community.jboss.org/servlet/JiveServlet/showImage/10533/duplicate-... http://community.jboss.org/servlet/JiveServlet/downloadImage/10533/duplic...
To be intuitive the wizard shall decorate the field that's invalid. The wizard shall also show you the full error text in the title area and disable the finish button if the user supplies invalid values.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-29-105... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-29...
h1. *How to decorate the name text field*
Let us take a first step and implement the code that will decorate our text field if we enter an name that's already used.
Jface databinding is a framework that is built upon the principle of model-view-controller (*MVC*). It connects a view to a model and transfers the values the user enters in the view to the model. It of course also operates the other way round and updates the view if new values are set to the model.
If a invalid value is detected while transfering it, an error is generated. If a user supplies a duplicate name to our connection, we'll be able show the error that occurs while transfering this name to the model.
h2. Let us create a model
As explained above, databinding operates on a model. We therefore need a model to store our values. We'll use a bean for that sake and provide notification capabilities with PropertyChangeSupport. PropertyChangeSupport, an implementation of the observer pattern, that's shipped with the jdk, allows jface databinding to observe value changes in our model. It will get notified as soon as we set new names to the model.
public class CloudConnectionModel extends ObservablePojo {
public static final String PROPERTY_NAME = "name";
private String name;
public CloudConnectionModel(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
getPropertyChangeSupport().
firePropertyChange(PROPERTY_NAME, this.name, this.name = name);
}
}
h2. Databinding, transfer the user input to our model!
We now have a model that holds our name. We now have to listen for user input on the text widget and set the model accordingly. We of course don't want to do that manually, we want databinding to provide this functionality.
h2. A bit of theory: what the hell are observables?
JFace databinding does not operate on the level of SWT listeners. It offers its own observer pattern. The main benefit here is that both ends of a *MVC* dont differ any longer, there are no SWT listeners to listen to changes in widgets nor PropertyChangeSupprt listeners to act upon changes in a model. Jface databinding observers *Observables*. Any participant to the jface databinding framework must implement the Observable interface. It of course provides adapters that adapt the various observer implementations that exist in the wild.
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-29-105... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-29...
h2. Databdining, adapt those beasts!
We need an *Observable* for our model since our model uses PropertyChangeSupport to notify observers. Jface databinding offers a factory that adapts the PropertyChangeSupport we used. To tell databindirng to observe changes in the name property we do the following:
IObservableValue beanPropertyObservable =
BeanProperties.value(CloudConnectionModel.class, "name").observe(connectionModel)
We also need to provide an observable for the text widget, that allows the user to enter the name of the connection. We tell the appropriate observable factory it to listen to text modifications on the text widget:
h2. Now connect both ends
Now that we have observables on both ends (model and widget), we can tell databinding to connect both ends of our *MVC* and transfer the values between them. Databinding does that if we instruct the *DataBindingContext* to create a bind them:
DataBindingContext dbc = new DataBindingContext();
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties.value(CloudConnectionModel.class, CloudConnectionModel.PROPERTY_NAME)
.observe(connectionModel));
h2. But hey, inputs are not always valid
We now told databinding to transfer the name the user enters in our text widget to our model and to update the widget if we set a new name to the model. What we still miss is the ability to constrain user inputs.
http://community.jboss.org/servlet/JiveServlet/showImage/10542/convert-va... http://community.jboss.org/servlet/JiveServlet/downloadImage/10542/conver...
Jface databinding offers the ability to convert and validate values while it transfers values from one end to the other. In other words we can tell it to validate the name before it sets the user input to the model.Databinding uses *IValidator* instances for that sake:
The binding we created now has to be told to use our *CloudNameValidator*. We do that in a custom *UpdateValueStrategy*. As we saw above, Databinding transfers values both ways: from the view to the model and the other way round. Databinding therefore offers strategies for both transfers. We only want to validate names that get transfered from the text widget, where the user may enter new names, to the model. We dont need validation where new names set in the model get propagated to the text widget. We therefore only set a single UpateValueStrategy and stick to the default (that does not validate) for the other strategy by indicating *null*.
h1. Disable finish button
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-29-105... http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-29...
In non-technical pure logical terms there's a single trigger to all these markers: You entered a bad value to a field.
The wizard reacts by decorating the text field that holds a the duplicate name, it displays the error in the header and it disabled the Finish button.
Jface databinding offers a context that holds a global validity state for a form. If a form gets invalid, the golbal state gets invalid, too. A jface wizards is built upon several pages. Such a page is a form that may be globally valid or invalid.
> DataBindingContext dbc = new DataBindingContext();
To enable/disable the Finish button you now need to connect the wizard page to the DataBindingContext. Jface databinding will then enable/disable the Finish (or the Next Page button for instance) automatically for you. To bind the validity of the databinding context to the page validity, you only need 1 line of code:
> WizardPageSupport.create(this, dbc);
Jface databinding will now enable the finish button (or the next-page button) for you as soon as the form aka the databinding context is valid.
h2. WORK IN PROGRESS
h1. Conclusion
--------------------------------------------------------------
Comment by going to Community
[http://community.jboss.org/docs/DOC-15964]
Create a new document in JBoss Tools at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&co...]
14 years, 2 months