Difference between revisions of "JFace Data Binding/PojoBindable"

From Eclipsepedia

Jump to: navigation, search
(Pojo Bindable in Action)
(@Bindable#fireEvents)
 
(29 intermediate revisions by one user not shown)
Line 3: Line 3:
 
== Target ==
 
== Target ==
  
Work is underway to support binding with Pojo by using BeansObservable WITHOUT coding firePropertyChange (into setter method)/addPropertyChangeListener/removePropertyChangeListener.  See (for the moment) [https://bugs.eclipse.org/bugs/show_bug.cgi?id=307417 Bug 307417]-
+
Work is underway to support binding with Pojo by using BeansObservable WITHOUT coding firePropertyChange (into setter method)/addPropertyChangeListener/removePropertyChangeListener.  For the last version of Pojo Bindable, plese see [[/PojoBindableSVN|Pojo Bindable SVN]] section otherwise, see (for the moment) [https://bugs.eclipse.org/bugs/show_bug.cgi?id=307417 Bug 307417]-
you can find 4 plug-in projects that provide Pojo Bindable :
+
you can find 7 plug-in projects that provide Pojo Bindable :
  
 
<ul>
 
<ul>
Line 208: Line 208:
 
  bleAgent</source>
 
  bleAgent</source>
  
To use Pojo Bindable Java Agent, '''you must launch your program with several JVM parameters''' :  
+
To use Pojo Bindable Java Agent (if you are not into OSGi context), '''you must launch your program with the JVM parameter''' :  
  
 
===== '''-javaagent:<your path>/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar''' =====  
 
===== '''-javaagent:<your path>/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar''' =====  
Line 214: Line 214:
 
This parameter means that you execute the BindableAgent before executing your program. The BindableAgent add a [http://java.sun.com/javase/6/docs/api/java/lang/instrument/ClassFileTransformer.html ClassFileTransformer] into [http://java.sun.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html Instrumentation] instance to update class bytecode.
 
This parameter means that you execute the BindableAgent before executing your program. The BindableAgent add a [http://java.sun.com/javase/6/docs/api/java/lang/instrument/ClassFileTransformer.html ClassFileTransformer] into [http://java.sun.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html Instrumentation] instance to update class bytecode.
  
===== '''-Dbindable.packages=<your model package>'''=====  
+
===== Configure Pojo Bindable Java Agent =====
 +
 
 +
Pojo Bindable Java Agent must be configured. The required configuration is to set the property '''bindable.packages''' which defines the packages where Pojo are stored in order to Bindable Java Agent change the bytecode only for the Pojo Class. You can configure Pojo Bindable with 3 means :
 +
 
 +
====== with JVM parameter -Dbindable.packages ======
 +
 
 +
You can configure Bindable Java Agent with '''JVM parameter -Dbindable.packages''' with value ''org.eclipse.core.examples.databinding.pojo.bindable.model'' like this :
 +
 
 +
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar
 +
-Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model</source>
 +
 
 +
====== with Java code by implementing BindableStrategyProvider ======
 +
 
 +
You can configure Bindable Java Agent with '''Java code BindableStrategyProvider''' with value ''org.eclipse.core.examples.databinding.pojo.bindable.model'' by implementing org.eclipse.core.databinding.pojo.bindable.'''BindableStrategyProvider''' interface like this :
 +
 
 +
<source lang="java" >package org.eclipse.core.examples.databinding.pojo.bindable.provider;
 +
 
 +
import org.eclipse.core.databinding.pojo.bindable.BindableStrategy;
 +
import org.eclipse.core.databinding.pojo.bindable.BindableStrategyProvider;
 +
import org.eclipse.core.databinding.pojo.bindable.DefaultBindableStrategy;
 +
 
 +
public class MyBindableStrategyProvider implements BindableStrategyProvider {
 +
 
 +
private DefaultBindableStrategy bindableStrategy = null;
 +
 
 +
public MyBindableStrategyProvider() {
 +
 
 +
bindableStrategy = new DefaultBindableStrategy(null);
 +
bindableStrategy
 +
.addPackageName("org.eclipse.core.examples.databinding.pojo.bindable.model");
 +
}
 +
 
 +
public BindableStrategy getBindableStrategy() {
 +
return bindableStrategy;
 +
}
 +
 
 +
}</source>
 +
 
 +
After you must launch your program with '''-Dbindable.strategy_provider''' like this :
 +
 
 +
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar
 +
-Dbindable.strategy_provider=org.eclipse.core.examples.databinding.pojo.bindable.provider.MyBindableStrategyProvider</source>
 +
 
 +
====== with bindable.properties file ======
 +
 
 +
You can configure Bindable Java Agent with '''bindable.properties file'''with value ''org.eclipse.core.examples.databinding.pojo.bindable.model'' by creating '''bindable.properties file'' into src folder with this content :
 +
 
 +
<source lang="text" >bindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model
 +
</source>
 +
 
 +
After you can launch your program only with javaagent JVM parameter :
 +
 
 +
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.ja</source>
 +
 
 +
===== Configuration properties of Pojo Bindable =====
 +
 
 +
====== '''bindable.packages=<your model package>'''======
  
 
BindableAgent requires the property "bindable.packages" to know which Class owned "bindable.packages", must be transformed to add PropertyChangeSupport. If you have several packages you can use ';' character as delimiter (ex : Dbindable.packages=com.example.package1;com.example.package2).
 
BindableAgent requires the property "bindable.packages" to know which Class owned "bindable.packages", must be transformed to add PropertyChangeSupport. If you have several packages you can use ';' character as delimiter (ex : Dbindable.packages=com.example.package1;com.example.package2).
  
===== '''-Dbindable.use_annotation=<true|false>''' =====  
+
====== '''bindable.use_annotation=<true|false>''' ======
  
 
BindableAgent use "bindable.use_annotation" property to see if [[#@Bindable annotation|@Bindable annotation]] must be used or not. This property is not required and by default the value is false.
 
BindableAgent use "bindable.use_annotation" property to see if [[#@Bindable annotation|@Bindable annotation]] must be used or not. This property is not required and by default the value is false.
  
===== '''-Dbindable.strategy_provider=<provider>''' =====  
+
====== '''bindable.strategy_provider=<provider>''' ======
 
Set an implementation of BindableStrategyProvider to configure BindableStrategy with Java code.
 
Set an implementation of BindableStrategyProvider to configure BindableStrategy with Java code.
  
===== '''-Dbindable.gen_basedir=<path>''' =====  
+
====== '''bindable.gen_basedir=<path>''' ======  
  
 
Set the path of base dir if you wish generate the result of transformed class into a file.
 
Set the path of base dir if you wish generate the result of transformed class into a file.
  
===== '''Dbindable.debug=<true|false>''' =====  
+
====== '''bindable.debug=<true|false>''' ======
  
 
If true, display information about BindableAgent and Class Transformation.
 
If true, display information about BindableAgent and Class Transformation.
  
==== Configure Pojo Bindable Java Agent====
+
==== Configure Pojo Bindable Java Agent with -Dbindable.packages ====
  
 
Create a lib folder and copy/paste Pojo Bindable Java Agent '''org.eclipse.core.databinding.pojo.bindable_1.0.0.jar''' (that you can find into org.eclipse.core.tests.databinding.pojo.bindable/lib folder).
 
Create a lib folder and copy/paste Pojo Bindable Java Agent '''org.eclipse.core.databinding.pojo.bindable_1.0.0.jar''' (that you can find into org.eclipse.core.tests.databinding.pojo.bindable/lib folder).
Line 245: Line 301:
 
To fill JVM Parameters that with Eclipse IDE, click on Run->Run Configurations. Select "PojoPersonBindableTest" launch (from Java Application node) and click on Arguments tabs. Fill VM Arguments textarea with this content :  
 
To fill JVM Parameters that with Eclipse IDE, click on Run->Run Configurations. Select "PojoPersonBindableTest" launch (from Java Application node) and click on Arguments tabs. Fill VM Arguments textarea with this content :  
  
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar -Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model</source>
+
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar  
 +
-Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model</source>
  
 
[[Image:PojoBindable_VMParameters.png]]
 
[[Image:PojoBindable_VMParameters.png]]
Line 253: Line 310:
 
Click on Run and you will have this error :  
 
Click on Run and you will have this error :  
  
<source lang="text" >Caused by: java.lang.NoClassDefFoundError: org/objectweb/asm/ClassVisitor
+
<source lang="text" >Caused by: java.lang.NoClassDefFoundError: org/objectweb/asm/Opcodes
at org.eclipse.core.databinding.pojo.bindable.BindableHelper.initialize(BindableHelper.java:47)FATAL ERROR in native method: processing of -javaagent failed
+
...
 +
at org.eclipse.core.databinding.pojo.bindable.agent.BindableAgent.premain(BindableAgent.java:67)FATAL ERROR in native method: processing of -javaagent failed
 
...</source>
 
...</source>
  
This error means that you must add [http://asm.ow2.org/ ObjectWeb ASM] in your ClassPath. Indead Pojo Bindable use ObjectWeb ASM 3.2.0 to change bytecode. You can download ASM [http://asm.ow2.org/download/index.html here] or use ASM bundle '''com.springsource.org.objectweb.asm''' (ASM OSGified) that you can find into the Pojo Bindable projects. For our example import '''com.springsource.org.objectweb.asm''' project in your workspace and add this Project into your Class Path.
+
This error means that you must add [http://asm.ow2.org/ ObjectWeb ASM] in your ClassPath. Indead Pojo Bindable use ObjectWeb ASM 3.2.0 to change bytecode. You can download ASM [http://asm.ow2.org/download/index.html here] or use ASM bundle '''com.springsource.org.objectweb.asm''' ASM bundle '''com.springsource.org.objectweb.asm.commons''' (ASM OSGified) that you can find into the Pojo Bindable projects. For our example you can copy/paste ASM Jars (that you can find on bindable-pojo-test) '''com.springsource.org.objectweb.asm-3.2.0.jar''' and '''com.springsource.org.objectweb.asm.commons-3.2.0.jar''' into lib folder and add it into Class Path project.
 +
 
 +
==== Add Eclipse/Core/Runtime in your ClassPath ====
 +
 
 +
Run it and error will display :
 +
 
 +
<source lang="text" >Caused by: java.lang.NoClassDefFoundError: org/eclipse/core/runtime/IStatus
 +
at org.eclipse.core.databinding.pojo.bindable.agent.BindableAgent.getBindableStrategyFromPackages(BindableAgent.java:160)
 +
...</source>
 +
 
 +
This erro means that you must add Eclipse/Core/Runtime into your ClassPath because Pojo Bindable use org.eclipse.core.runtime.IStatus to log messages (like ILogger from JFace Databinding). Add
 +
'''org.eclipse.equinox.common_*.jar''' into lib and add it into Class Path project (you can find this Jar '''org.eclipse.equinox.common_3.4.0.v20080421-2006.jar''' on bindable-pojo-test).
  
 
Run it and error must disappear.
 
Run it and error must disappear.
Line 270: Line 339:
 
import java.beans.PropertyChangeSupport;
 
import java.beans.PropertyChangeSupport;
  
public class PojoPerson implements org.eclipse.core.databinding.pojo.bindable.BindableAware {
+
import org.eclipse.core.databinding.pojo.bindable.BindableAware;
  
private transient PropertyChangeSupport _bindable_propertyChangeSupport = null;
+
public class PojoPerson implements BindableAware {
 
+
String name = "HelloWorld";
+
  
 +
private String name = "HelloWorld";
 +
private transient PropertyChangeSupport _bindable_propertyChangeSupport;
 +
 
public String getName() {
 
public String getName() {
 
return name;
 
return name;
Line 281: Line 351:
  
 
public void setName(String name) {
 
public void setName(String name) {
Object oldValue = this.name;
+
String s = getName();
Object newValue = name;
+
 
this.name = name;
 
this.name = name;
_bindable_getPropertyChangeSupport().firePropertyChange("name ",
+
_bindable_getPropertyChangeSupport().firePropertyChange("name", s,
oldValue, newValue);
+
getName());
 
+
 
}
 
}
  
 
private PropertyChangeSupport _bindable_getPropertyChangeSupport() {
 
private PropertyChangeSupport _bindable_getPropertyChangeSupport() {
if (_bindable_propertyChangeSupport == null) {
+
if (_bindable_propertyChangeSupport == null)
this._bindable_propertyChangeSupport = new PropertyChangeSupport(
+
_bindable_propertyChangeSupport = new PropertyChangeSupport(this);
this);
+
}
+
 
return _bindable_propertyChangeSupport;
 
return _bindable_propertyChangeSupport;
 
}
 
}
  
public void addPropertyChangeListener(String propertyName,
+
public void addPropertyChangeListener(String s,
PropertyChangeListener listener) {
+
PropertyChangeListener propertychangelistener) {
 +
_bindable_getPropertyChangeSupport().addPropertyChangeListener(s,
 +
propertychangelistener);
 +
}
 +
 
 +
public void addPropertyChangeListener(
 +
PropertyChangeListener propertychangelistener) {
 
_bindable_getPropertyChangeSupport().addPropertyChangeListener(
 
_bindable_getPropertyChangeSupport().addPropertyChangeListener(
propertyName, listener);
+
propertychangelistener);
 
}
 
}
  
public void removePropertyChangeListener(String propertyName,
+
public void removePropertyChangeListener(String s,
PropertyChangeListener listener) {
+
PropertyChangeListener propertychangelistener) {
 +
_bindable_getPropertyChangeSupport().removePropertyChangeListener(s,
 +
propertychangelistener);
 +
}
 +
 
 +
public void removePropertyChangeListener(
 +
PropertyChangeListener propertychangelistener) {
 
_bindable_getPropertyChangeSupport().removePropertyChangeListener(
 
_bindable_getPropertyChangeSupport().removePropertyChangeListener(
propertyName, listener);
+
propertychangelistener);
 
}
 
}
 +
 +
 
}</source>
 
}</source>
  
As you can see, Bindable Agent add PropertyChangeSupport capability and update bytecode of '''PojoPerson#setName(String name)''' to call [http://java.sun.com/j2se/1.4.2/docs/api/java/beans/PropertyChangeSupport.html#firePropertyChange%28java.lang.String,%20java.lang.Object,%20java.lang.Object%29 PropertyChangeSupport#firePropertyChange(String propertyName, Object oldValue, Object newValue)]. You can notice that PojoPerson implements org.eclipse.core.databinding.pojo.bindable.'''BindableAware''' interface. Here te content of this BindableAware interface :  
+
As you can see, Bindable Agent add PropertyChangeSupport capability and update bytecode of '''PojoPerson#setName(String name)''' to call [http://java.sun.com/j2se/1.4.2/docs/api/java/beans/PropertyChangeSupport.html#firePropertyChange%28java.lang.String,%20java.lang.Object,%20java.lang.Object%29 PropertyChangeSupport#firePropertyChange(String propertyName, Object oldValue, Object newValue)].  
 +
 
 +
===== getter getXXX()/isXXX() required =====
 +
 
 +
Here code of setName :
 +
 
 +
<source lang="java" >public void setName(String name) {
 +
  String s = getName();
 +
  this.name = name;
 +
  _bindable_getPropertyChangeSupport().firePropertyChange("name", s, getName());
 +
}</source>
 +
 
 +
As you can notice, getName() is used into setName to get the old value. It's a restriction to use Pojo Bindable. For boolean primtive type isXXX is required.
 +
 
 +
<source lang="java" >public void setBooleanValue(boolean value) {
 +
  Boolean oldValue = Boolean.valueOf(isBooleanValue());
 +
  this.value = value;
 +
  _bindable_getPropertyChangeSupport().firePropertyChange("booleanValue", oldValue , Boolean.valueOf(isBooleanValue()));
 +
}</source>
 +
 
 +
===== setter with primitive type =====
 +
 
 +
When primitive type is used into setter, The old/new value is converted to Java Object (boolean to java.lang.Boolean, short to java.lang.Short....). Here sampel with short value :
 +
 
 +
<source lang="java" >public void setShortValue(short value) {
 +
  Short oldValue = Short.valueOf(getShortValue());
 +
  this.value = value;
 +
  _bindable_getPropertyChangeSupport().firePropertyChange("shortValue", oldValue , Short.valueOf(getShortValue()));
 +
}</source>
 +
 
 +
 
 +
===== Implements BindableAware =====
 +
 
 +
 
 +
You can notice that PojoPerson implements org.eclipse.core.databinding.pojo.bindable.'''BindableAware''' interface. Here te content of this BindableAware interface :  
  
 
<source lang="java" >package org.eclipse.core.databinding.pojo.bindable;
 
<source lang="java" >package org.eclipse.core.databinding.pojo.bindable;
Line 320: Line 434:
 
void addPropertyChangeListener(String propertyName,
 
void addPropertyChangeListener(String propertyName,
 
PropertyChangeListener listener);
 
PropertyChangeListener listener);
 +
 +
void addPropertyChangeListener(PropertyChangeListener listener);
  
 
void removePropertyChangeListener(String propertyName,
 
void removePropertyChangeListener(String propertyName,
 
PropertyChangeListener listener);
 
PropertyChangeListener listener);
 +
 +
void removePropertyChangeListener(PropertyChangeListener listener);
 
}
 
}
 
</source>
 
</source>
Line 485: Line 603:
  
 
Run it to check that property "name" of pur Pojo PojoPerson can be tracked.
 
Run it to check that property "name" of pur Pojo PojoPerson can be tracked.
 +
 +
==== -Dbindable.debug=true ====
 +
 +
To debug BindableAgent and check your Pojo class is transformed, you can add the parameter into JVM parameters :
 +
 +
<source lang="text" >-Dbindable.debug=true</source>
 +
 +
If you lanch the previous example with this parameter, console will display :
 +
 +
<source lang="text" >Status INFO: NO Transformation for class: org/eclipse/core/internal/runtime/LocalizationUtils
 +
Status INFO: NO Transformation for class: org/eclipse/core/internal/runtime/CommonMessages
 +
Status INFO: Java Bindable Agent called, with configuration:
 +
bindable.packages=org/eclipse/core/examples/databinding/pojo/bindable/model
 +
bindable.use_annotation=false
 +
bindable.debug=true
 +
bindable.gen_basedir=D://tmp/
 +
bindable.strategy_provider=null
 +
Status INFO: NO Transformation for class: org/eclipse/core/examples/databinding/pojo/bindable/PojoPersonBindableTest
 +
Status INFO: NO Transformation for class: org/eclipse/core/examples/databinding/pojo/bindable/PojoPersonBindableTest$1
 +
Status INFO: Transform class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson
 +
Status INFO: NO Transformation for class: org/eclipse/core/databinding/pojo/bindable/BindableAware
 +
---------- Property PojoPerson changed --------
 +
  PropertyName=name
 +
  OldValue=HelloWorld
 +
  NewValue=New name
 +
------------------------------------------
 +
</source>
 +
 +
You can notice that console display each Class wich must be loaded. The message
 +
 +
<source lang="text" >Status INFO: NO Transformation for class: ...</source>
 +
 +
means that Class is not transformed.
 +
 +
<source lang="text" >Status INFO: Transform class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson</source>
 +
 +
means that our PojoPerson class is transformed.
 +
 +
You can notice on start of teh console teh configuration of Pojo Bindable :
 +
 +
<source lang="text" >Status INFO: Java Bindable Agent called, with configuration:
 +
bindable.packages=org/eclipse/core/examples/databinding/pojo/bindable/model
 +
bindable.use_annotation=false
 +
bindable.debug=true
 +
bindable.gen_basedir=D://tmp/
 +
bindable.strategy_provider=null</source>
 +
 +
==== -Dbindable.gen_basedir=D://tmp/ ====
 +
 +
You can if you wish, generate the result of class transformed into file. To do that you must add the parameter into JVM parameters :
 +
 +
<source lang="text" >-Dbindable.gen_basedir=D://tmp/</source>
 +
 +
Here, result of class transformed will be generate into folder D://tmp/. If you lanch the previous example with this parameter, console will display :
 +
 +
<source lang="text" >...
 +
Status INFO: Transform class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson
 +
Status INFO: Generate transformed class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson into file: D://tmp/org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson.class
 +
Status INFO: Transformed class output written to: D://tmp/org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson.class
 +
...</source>
 +
 +
You can check that you have D://tmp/org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson.class file generated. You can decompile it to check that this class implements BindableAware interface.
 +
 +
==== -Dbindable.strategy_provider=org...MyBindableStrategyProvider ====
 +
 +
We have configured BindableAgent with several JVM parameters (-Dbindable.packages, -Dbindable.debug...), but it's possible to configure it with Java code. To do that create a class wich implements
 +
org.eclipse.core.databinding.pojo.bindable.'''BindableStrategyProvider''' interface.  You can found sample org.eclipse.core.examples.databinding.pojo.bindable.provider.'''MyBindableStrategyProvider'''
 +
into '''org.eclipse.core.examples.databinding.pojo.bindable''' bundle.
 +
 +
<source lang="java" >package org.eclipse.core.examples.databinding.pojo.bindable.provider;
 +
 +
import org.eclipse.core.databinding.pojo.bindable.BindableStrategy;
 +
import org.eclipse.core.databinding.pojo.bindable.BindableStrategyProvider;
 +
import org.eclipse.core.databinding.pojo.bindable.DefaultBindableStrategy;
 +
 +
public class MyBindableStrategyProvider implements BindableStrategyProvider {
 +
 +
private DefaultBindableStrategy bindableStrategy = null;
 +
 +
public MyBindableStrategyProvider() {
 +
 +
bindableStrategy = new DefaultBindableStrategy(null);
 +
bindableStrategy
 +
.addPackageName("org.eclipse.core.examples.databinding.pojo.bindable.model");
 +
bindableStrategy.setDebugMode(true);
 +
// bindableStrategy.setGenBaseDir("D://tmp");
 +
bindableStrategy.setUseAnnotation(false);
 +
}
 +
 +
public BindableStrategy getBindableStrategy() {
 +
return bindableStrategy;
 +
}
 +
 +
}</source>
 +
 +
Launch Bindable Agent with -Dbindable.strategy_provider like this :
 +
 +
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar
 +
-Dbindable.strategy_provider=org.eclipse.core.examples.databinding.pojo.bindable.provider.MyBindableStrategyProvider</source>
 +
 +
You can find that into the launch/HelloWorldWithBeansObservablesWithBindableAgentProvider.launch
  
 
== Pojo Bindable & JFace Databinding ==
 
== Pojo Bindable & JFace Databinding ==
Line 728: Line 947:
 
Sometimes you have "set" method into your model Pojo and you will NOT transform setXXX method by calling PropertyChangeSupport. To do that you can use @Bindable annotation. For instance :  
 
Sometimes you have "set" method into your model Pojo and you will NOT transform setXXX method by calling PropertyChangeSupport. To do that you can use @Bindable annotation. For instance :  
  
At first you must available @Bindable annotation with JVM parameters '''bindable.use_annotation=true'''. After you can write this to transform setName method and avoid transforming setLogin method :  
+
At first you must available @Bindable annotation with JVM parameters '''bindable.use_annotation=true'''.  
 +
 
 +
=== @Bindable#value ===
 +
 
 +
After you can write this to transform setName method and avoid transforming setLogin method :  
  
 
<source lang="java" >package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations;
 
<source lang="java" >package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations;
Line 794: Line 1,017:
  
 
Project '''org.eclipse.core.tests.databinding.pojo.bindable''' show you usecase with org.eclipse.core.databinding.pojo.bindable.'''Bindable''' annotation.
 
Project '''org.eclipse.core.tests.databinding.pojo.bindable''' show you usecase with org.eclipse.core.databinding.pojo.bindable.'''Bindable''' annotation.
 +
 +
=== @Bindable#dependsOn ===
 +
 +
If you wish manage '''computed values''', you can use '''@Bindable#dependsOn'''. You can find TestCase into [http://dynaresume.googlecode.com/svn/trunk/JFace-Pojo-Bindable/tests/org.eclipse.core.tests.databinding.pojo.bindable/src/org/eclipse/core/tests/databinding/pojo/bindable/annotations/computed/ComputedValueTest.java ComputedValueTest] which use model [http://dynaresume.googlecode.com/svn/trunk/JFace-Pojo-Bindable/tests/org.eclipse.core.tests.databinding.pojo.bindable/src/org/eclipse/core/tests/databinding/pojo/bindable/domain/annotations/computed/ComputedValueTestEntity.java ComputedValueTestEntity]
 +
 +
Imagine you have this Pojo :
 +
 +
<source lang="java" >package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.computed;
 +
 +
import java.beans.PropertyChangeEvent;
 +
 +
import org.eclipse.core.databinding.pojo.bindable.annotation.Bindable;
 +
 +
public class ComputedValueTestEntity {
 +
 +
private double sellingPrice;
 +
private double buyingPrice;
 +
 +
public ComputedValueTestEntity(double sellingPrice, double buyingPrice) {
 +
this.sellingPrice = sellingPrice;
 +
this.buyingPrice = buyingPrice;
 +
}
 +
 +
public double getSellingPrice() {
 +
return sellingPrice;
 +
}
 +
 +
public void setSellingPrice(double sellingPrice) {
 +
this.sellingPrice = sellingPrice;
 +
}
 +
 +
public double getBuyingPrice() {
 +
return buyingPrice;
 +
}
 +
 +
public void setBuyingPrice(double buyingPrice) {
 +
this.buyingPrice = buyingPrice;
 +
}
 +
 +
public double getRatio() {
 +
return sellingPrice / buyingPrice;
 +
}
 +
 +
public boolean isCheapBuyingPrice() {
 +
return buyingPrice < 100;
 +
}
 +
 +
}</source>
 +
 +
Now you want observe changed of the "ratio" property (''ComputedValueTestEntity#getRatio()''). As you can notice, the method ''ComputedValueTestEntity#setRatio(double ratio)'' doesn't exist, because "ratio" property depends on "sellingPrice" and "buyingPrice" properties. So "ratio" change when "sellingPrice" and "buyingPrice" change, on other words when methods setSellingPrice/setBuyingPrice are called.
 +
 +
Imagine you want bind this "ratio" property with a SWT Label. An event PropertyChangeEvent with property name "ratio" must be fired when setSellingPrice/setBuyingPrice methods are called. It means that "ratio" property, depends on call of setSellingPrice and setBuyingPrice methods. You can manage that with Pojo Bindable with '''@Bindable#dependsOn''' declared into (''ComputedValueTestEntity#getRatio()'') getter method like this :
 +
 +
<source lang="java" >@Bindable(dependsOn = { "sellingPrice", "buyingPrice" })
 +
public double getRatio() {
 +
    return sellingPrice / buyingPrice;
 +
}</source>
 +
 +
 +
After you can observe "ratio" property. Here a sample code to observe it :
 +
 +
<source lang="java" >PropertyChangeListener listener = new PropertyChangeListener() {
 +
public void propertyChange(PropertyChangeEvent event) {
 +
System.out
 +
.println("---------- Property ComputedValueTestEntity changed --------");
 +
System.out.println("  PropertyName=" + event.getPropertyName());
 +
System.out.println("  OldValue=" + event.getOldValue());
 +
System.out.println("  NewValue=" + event.getNewValue());
 +
System.out
 +
.println("------------------------------------------");
 +
}
 +
};
 +
 +
// Create ComputedValueTestEntity model
 +
ComputedValueTestEntity p = new ComputedValueTestEntity(4, 2);
 +
 +
// addPropertyChangeListener to observe ratio (javagent must be used to transform bytecode!!!)
 +
((BindableAware)p).addPropertyChangeListener("ratio", listener);
 +
 +
// Change buyingPrice, event is fired with 'ratio' property
 +
p.setBuyingPrice(100);
 +
// Change sellingPrice, event is fired with 'ratio' property
 +
p.setSellingPrice(100);</source>
 +
 +
 +
If you launch this sample with Pojo Bindable Java Agent :
 +
 +
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar -Dbindable.packages=org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.computed -Dbindable.use_annotation=true</source>
 +
 +
You will see on console :
 +
 +
<source lang="text" >---------- Property ComputedValueTestEntity changed --------
 +
  PropertyName=ratio
 +
  OldValue=2.0
 +
  NewValue=0.04
 +
------------------------------------------
 +
---------- Property ComputedValueTestEntity changed --------
 +
  PropertyName=ratio
 +
  OldValue=0.04
 +
  NewValue=1.0
 +
------------------------------------------</source>
 +
 +
For information Pojo Bindable change the bytecode of the Pojo like this :
 +
 +
<source lang="java " >package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.computed;
 +
 +
import java.beans.PropertyChangeListener;
 +
import java.beans.PropertyChangeSupport;
 +
import org.eclipse.core.databinding.pojo.bindable.BindableAware;
 +
 +
public class ComputedValueTestEntity
 +
    implements BindableAware
 +
{
 +
 +
    private double sellingPrice;
 +
    private double buyingPrice;
 +
    private transient PropertyChangeSupport _bindable_propertyChangeSupport;
 +
    private transient double _bindable_setSellingPrice_ratio;
 +
    private transient double _bindable_setBuyingPrice_ratio;
 +
 +
    public ComputedValueTestEntity(double sellingPrice, double buyingPrice)
 +
    {
 +
        this.sellingPrice = sellingPrice;
 +
        this.buyingPrice = buyingPrice;
 +
    }
 +
 +
    public double getSellingPrice()
 +
    {
 +
        return sellingPrice;
 +
    }
 +
 +
    public void setSellingPrice(double sellingPrice)
 +
    {
 +
        _bindable_beforeDependsOn_setSellingPrice();
 +
        Double double1 = Double.valueOf(getSellingPrice());
 +
        this.sellingPrice = sellingPrice;
 +
        _bindable_getPropertyChangeSupport().firePropertyChange("sellingPrice", double1, Double.valueOf(getSellingPrice()));
 +
        _bindable_afterDependsOn_setSellingPrice();
 +
    }
 +
 +
    public double getBuyingPrice()
 +
    {
 +
        return buyingPrice;
 +
    }
 +
 +
    public void setBuyingPrice(double buyingPrice)
 +
    {
 +
        _bindable_beforeDependsOn_setBuyingPrice();
 +
        Double double1 = Double.valueOf(getBuyingPrice());
 +
        this.buyingPrice = buyingPrice;
 +
        _bindable_getPropertyChangeSupport().firePropertyChange("buyingPrice", double1, Double.valueOf(getBuyingPrice()));
 +
        _bindable_afterDependsOn_setBuyingPrice();
 +
    }
 +
 +
    public double getRatio()
 +
    {
 +
        return sellingPrice / buyingPrice;
 +
    }
 +
 +
    private PropertyChangeSupport _bindable_getPropertyChangeSupport()
 +
    {
 +
        if(_bindable_propertyChangeSupport == null)
 +
            _bindable_propertyChangeSupport = new PropertyChangeSupport(this);
 +
        return _bindable_propertyChangeSupport;
 +
    }
 +
 +
    public void addPropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(s, propertychangelistener);
 +
    }
 +
 +
    public void addPropertyChangeListener(PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(propertychangelistener);
 +
    }
 +
 +
    public void removePropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(s, propertychangelistener);
 +
    }
 +
 +
    public void removePropertyChangeListener(PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(propertychangelistener);
 +
    }
 +
 +
    private void _bindable_beforeDependsOn_setSellingPrice()
 +
    {
 +
        _bindable_setSellingPrice_ratio = getRatio();
 +
    }
 +
 +
    private void _bindable_afterDependsOn_setSellingPrice()
 +
    {
 +
        _bindable_getPropertyChangeSupport().firePropertyChange("ratio", Double.valueOf(_bindable_setSellingPrice_ratio), Double.valueOf(getRatio()));
 +
    }
 +
 +
    private void _bindable_beforeDependsOn_setBuyingPrice()
 +
    {
 +
        _bindable_setBuyingPrice_ratio = getRatio();
 +
    }
 +
 +
    private void _bindable_afterDependsOn_setBuyingPrice()
 +
    {
 +
        _bindable_getPropertyChangeSupport().firePropertyChange("ratio", Double.valueOf(_bindable_setBuyingPrice_ratio), Double.valueOf(getRatio()));
 +
    }
 +
 +
}</source>
 +
 +
=== @Bindable#fireEvents ===
 +
 +
If you wish force '''fire events''', when you call methods you can use '''@Bindable#fireEvents'''. You can find TestCase into [http://dynaresume.googlecode.com/svn/trunk/JFace-Pojo-Bindable/tests/org.eclipse.core.tests.databinding.pojo.bindable/src/org/eclipse/core/tests/databinding/pojo/bindable/annotations/fireEvents/PeopleTestCase.java PeopleTestCase] which use model [http://dynaresume.googlecode.com/svn/trunk/JFace-Pojo-Bindable/tests/org.eclipse.core.tests.databinding.pojo.bindable/src/org/eclipse/core/tests/databinding/pojo/bindable/domain/annotations/fireEvents/People.java People]
 +
 +
Imagine you have this Pojo :
 +
 +
<source lang="java" >package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents;
 +
 +
import java.util.ArrayList;
 +
import java.util.Collection;
 +
 +
public class People {
 +
 +
  private Collection<Person> people = null;
 +
 +
  public void addPerson(Person person) {
 +
    if (people == null) {
 +
      people = new ArrayList<Person>();
 +
    }
 +
    people.add(person);
 +
  }
 +
 +
  public Collection<Person> getPeople() {
 +
    return people;
 +
  }
 +
 +
  public static class Person {
 +
 +
  }
 +
}</source>
 +
 +
When People#addPerson(Person) is called you want fire events "people" with old/new list of Person to observe changed of  the "people" property.
 +
You can manage that with Pojo Bindable with '''@Bindable#fireEvents''' declared into (''People#addPerson(Person)'') method like this :
 +
 +
<source lang="java" >@Bindable(fireEvents = { "people" })
 +
public void addPerson(Person person) {
 +
if (people == null) {
 +
people = new ArrayList<Person>();
 +
}
 +
people.add(person);
 +
}</source>
 +
 +
People#getPeople() is required to get the old/new value by using the property "people". After you can observe "people" property. Here a sample code to observe it :
 +
 +
<source lang="java" >People people = new People();
 +
BindableAware peopleBindable = (BindableAware) people;
 +
 +
// Instrumentation was done with successfull, add Listener
 +
PropertyChangeListener listener = new PropertyChangeListener() {
 +
public void propertyChange(PropertyChangeEvent event) {
 +
System.out.println("---------- Property People changed --------");
 +
System.out.println("  PropertyName=" + event.getPropertyName());
 +
System.out.println("  OldValue=" + event.getOldValue());
 +
System.out.println("  NewValue=" + event.getNewValue());
 +
System.out.println("------------------------------------------");
 +
testEvent = event;
 +
}
 +
};
 +
 +
peopleBindable.addPropertyChangeListener("people", listener);</source>
 +
 +
If you launch this sample with Pojo Bindable Java Agent :
 +
 +
<source lang="text" >-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar -Dbindable.packages=org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents
 +
-Dbindable.use_annotation=true</source>
 +
 +
You will see on console :
 +
 +
<source lang="text" >---------- Property People changed --------
 +
  PropertyName=people
 +
  OldValue=null
 +
  NewValue=[org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents.People$Person@92e78c]
 +
------------------------------------------</source>
 +
 +
For information Pojo Bindable change the bytecode of the Pojo like this :
 +
 +
<source lang="java " >package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents;
 +
 +
import java.beans.PropertyChangeListener;
 +
import java.beans.PropertyChangeSupport;
 +
import java.util.ArrayList;
 +
import java.util.Collection;
 +
import org.eclipse.core.databinding.pojo.bindable.BindableAware;
 +
 +
public class People
 +
    implements BindableAware
 +
{
 +
 +
    private Collection people;
 +
    private transient PropertyChangeSupport _bindable_propertyChangeSupport;
 +
    private transient Collection _bindableFireEvents_addPerson_people;
 +
 +
    public People()
 +
    {
 +
        people = null;
 +
    }
 +
 +
    public void addPerson(Person person)
 +
    {
 +
        _bindable_beforeFireEvents_addPerson();
 +
        if(people == null)
 +
            people = new ArrayList();
 +
        people.add(person);
 +
        _bindable_afterFireEvents_addPerson();
 +
    }
 +
 +
    public Collection getPeople()
 +
    {
 +
        return people;
 +
    }
 +
 +
    private PropertyChangeSupport _bindable_getPropertyChangeSupport()
 +
    {
 +
        if(_bindable_propertyChangeSupport == null)
 +
            _bindable_propertyChangeSupport = new PropertyChangeSupport(this);
 +
        return _bindable_propertyChangeSupport;
 +
    }
 +
 +
    public void addPropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(s, propertychangelistener);
 +
    }
 +
 +
    public void addPropertyChangeListener(PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(propertychangelistener);
 +
    }
 +
 +
    public void removePropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(s, propertychangelistener);
 +
    }
 +
 +
    public void removePropertyChangeListener(PropertyChangeListener propertychangelistener)
 +
    {
 +
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(propertychangelistener);
 +
    }
 +
 +
    private void _bindable_beforeFireEvents_addPerson()
 +
    {
 +
        _bindableFireEvents_addPerson_people = getPeople();
 +
    }
 +
 +
    private void _bindable_afterFireEvents_addPerson()
 +
    {
 +
        _bindable_getPropertyChangeSupport().firePropertyChange("people", _bindableFireEvents_addPerson_people, getPeople());
 +
    }
 +
 +
  public static class Person
 +
        implements BindableAware
 +
    {
 +
 +
        private transient PropertyChangeSupport _bindable_propertyChangeSupport;
 +
 +
        public Person()
 +
        {
 +
        }
 +
 +
        private PropertyChangeSupport _bindable_getPropertyChangeSupport()
 +
        {
 +
            if(_bindable_propertyChangeSupport == null)
 +
                _bindable_propertyChangeSupport = new PropertyChangeSupport(this);
 +
            return _bindable_propertyChangeSupport;
 +
        }
 +
 +
        public void addPropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
 +
        {
 +
            _bindable_getPropertyChangeSupport().addPropertyChangeListener(s, propertychangelistener);
 +
        }
 +
 +
        public void addPropertyChangeListener(PropertyChangeListener propertychangelistener)
 +
        {
 +
            _bindable_getPropertyChangeSupport().addPropertyChangeListener(propertychangelistener);
 +
        }
 +
 +
        public void removePropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
 +
        {
 +
            _bindable_getPropertyChangeSupport().removePropertyChangeListener(s, propertychangelistener);
 +
        }
 +
 +
        public void removePropertyChangeListener(PropertyChangeListener propertychangelistener)
 +
        {
 +
            _bindable_getPropertyChangeSupport().removePropertyChangeListener(propertychangelistener);
 +
        }
 +
 +
    }
 +
 +
}</source>
  
 
== Use another Java Agent ==
 
== Use another Java Agent ==
Line 861: Line 1,480:
 
== OSGi Context ==
 
== OSGi Context ==
  
Today I have no idea how use JavaAgent into OSGi context. I believe it's not possible but [http://www.eclipse.org/eclipselink/ EclipseLink] works too with change of bytecode to manage Load Time Weaver.
+
To manage Pojo Bindable into OSGi context, please see [[/PojoBindableSVN|Pojo Bindable SVN]]. It Following the same idea that [http://wiki.eclipse.org/EclipseLink/Examples/OSGi/Equinox_Byte_Code_Weaving EclipseLink/Examples/OSGi/Equinox Byte Code Weaving].
Bindable Java Agent is very basic and call internal ClassBindable (ASM ClassVisitor) wich update bytecode. The whole code of bytecode changed can be reused into OSGi context. Following the same idea that [http://wiki.eclipse.org/EclipseLink/Examples/OSGi/Equinox_Byte_Code_Weaving EclipseLink/Examples/OSGi/Equinox Byte Code Weaving] could be perhaps a solution?
+

Latest revision as of 03:17, 22 April 2010

JFace Data Binding
Home
How to Contribute
FAQ
Snippets
Concepts
Binding
Converter
Observable
Realm

Contents

[edit] Target

Work is underway to support binding with Pojo by using BeansObservable WITHOUT coding firePropertyChange (into setter method)/addPropertyChangeListener/removePropertyChangeListener. For the last version of Pojo Bindable, plese see Pojo Bindable SVN section otherwise, see (for the moment) Bug 307417- you can find 7 plug-in projects that provide Pojo Bindable :

  • org.eclipse.core.databinding.pojo.bindable : Pojo Bindable source.
  • org.eclipse.core.examples.databinding.pojo.bindable : Pojo Bindable samples.
  • org.eclipse.core.tests.databinding.beans.bindable : Pojo Bindable tests.
  • bindable-pojo-test : basic sample with Pojo Bindable explained into the section With Pojo Bindable
  • com.springsource.org.objectweb.asm : ASM bundle "org.objectweb.asm" version of 3.2.0 getted from SpringSource Enterprise Bundle Repository.
  • com.springsource.org.objectweb.asm.commons : ASM Commons bundle "org.objectweb.asm" version of 3.2.0 getted from SpringSource Enterprise Bundle Repository.
  • com.springsource.org.objectweb.asm.tree : ASM Tree bundle "org.objectweb.asm" version of 3.2.0 getted from SpringSource Enterprise Bundle Repository. This bundle is not required for Pojo Bindable but just required for com.springsource.org.objectweb.asm.commons bundle.

[edit] Pojo Bindable in Action

Before explaining how use Pojo Bindable with JFace Databinding, take a simple example with Pojo. You can find sources of this project into the bindable-pojo-test project that you can download on Bug 307417. In this section we will observe a property change of a Pojo. To do that we will do :

[edit] Without Pojo Bindable

Create a Java Project bindable-pojo-test and create a basic Pojo PojoPerson into package org.eclipse.core.examples.databinding.pojo.bindable.model :

package org.eclipse.core.examples.databinding.pojo.bindable.model;
 
public class PojoPerson {
 
	String name = "HelloWorld";
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
}

Create PojoPersonBindableTest class into package org.eclipse.core.examples.databinding.pojo.bindable :

package org.eclipse.core.examples.databinding.pojo.bindable;
 
import org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson;
 
public class PojoPersonBindableTest {
 
	public static void main(String[] args) {
		PojoPerson person = new PojoPerson();
		person.setName("New name");		
	}
}

Now we wish observe change of "name" property of the PojoPerson. To do that we must add java.beans.PropertyChangeSupport in our PojoPerson like this :

package org.eclipse.core.examples.databinding.pojo.bindable.model;
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
 
public class PojoPerson {
 
	private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
			this);
 
	String name = "HelloWorld";
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		Object oldValue = getName();
		Object newValue = name;
		this.name = name;
		firePropertyChange("name", oldValue, newValue);
	}
 
	public void addPropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(propertyName,
				listener);
	}
 
 
	public void removePropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(propertyName,
				listener);
	}
 
	protected void firePropertyChange(String propertyName, Object oldValue,
			Object newValue) {
		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
				newValue);
	}
}

You can notice that we do like this :

public void setName(String name) {
  Object oldValue = getName();
  ...
}

instead of doing :

public void setName(String name) {
  Object oldValue = this.name;
  ...
}

Indead, Experience has shown that sometimes getter methods do not simply return the field value especially in JPA entities. Further, field names could be totally different than the setter/getter method names. This is the reason that getter method is used to get the old value, rather than accessing the field.

By adding java.beans.PropertyChangeSupport in our Pojo, we can observe change of the "name" property of our Pojo (wich become a JavaBean). Update PojoPersonBindableTest code like this  :

package org.eclipse.core.examples.databinding.pojo.bindable;
 
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
 
import org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson;
 
public class PojoPersonBindableTest {
 
	public static void main(String[] args) {
 
		// Create listener
		PropertyChangeListener listener = new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent event) {
				System.out.println("---------- Property PojoPerson changed --------");
				System.out.println("  PropertyName=" + event.getPropertyName());
				System.out.println("  OldValue=" + event.getOldValue());
				System.out.println("  NewValue=" + event.getNewValue());
				System.out
						.println("------------------------------------------");
			}
		};
 
		// Create Pojo instance
		PojoPerson person = new PojoPerson();
		// Add listener
		person.addPropertyChangeListener("name", listener);
		// Here Change "name" property of PojoPerson is tracked.
		person.setName("New name");
		// Remove listener
		person.removePropertyChangeListener("name", listener);
		// Here Change "name" property of PojoPerson is not tracked.
		person.setName("New name2");		
	}
}

When you launch PojoPersonBindableTest, console display :

---------- Property PojoPerson changed --------
  PropertyName=name
  OldValue=HelloWorld
  NewValue=New name
------------------------------------------

This basic sample show you that you must write in your Pojo code, the PropertyChangeSupport to track the change of the property.

[edit] With Pojo Bindable

Pojo Bindable give you the capability to track property change with pur Pojo, without coding PropertyChangeSupport. It means that you can use this Pojo content :

package org.eclipse.core.examples.databinding.pojo.bindable.model;
 
public class PojoPerson {
 
	String name = "HelloWorld";
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
}

Change class PojoPerson with this previous code (your project doesn't compile because addPropertyChangeListener/removePropertyChangeListener doesn' exist. For the moment comments code line wich call addPropertyChangeListener/removePropertyChangeListener to compile PojoPersonBindableTest Class).

[edit] Pojo Bindable Java Agent

Pojo Bindable is a Java Agent (works only with java.version>=5) which change bytecode of the Pojo to add PropertyChangeSupport. Just for your Information, you can find the Bindable Java Agent into class org.eclipse.core.databinding.pojo.bindable.agent.BindableAgent (which define premain method) and where this class is filled into MANIFEST.MF :

 Premain-Class: org.eclipse.core.databinding.pojo.bindable.agent.Binda
 bleAgent

To use Pojo Bindable Java Agent (if you are not into OSGi context), you must launch your program with the JVM parameter :

[edit] -javaagent:<your path>/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar

This parameter means that you execute the BindableAgent before executing your program. The BindableAgent add a ClassFileTransformer into Instrumentation instance to update class bytecode.

[edit] Configure Pojo Bindable Java Agent

Pojo Bindable Java Agent must be configured. The required configuration is to set the property bindable.packages which defines the packages where Pojo are stored in order to Bindable Java Agent change the bytecode only for the Pojo Class. You can configure Pojo Bindable with 3 means :

[edit] with JVM parameter -Dbindable.packages

You can configure Bindable Java Agent with JVM parameter -Dbindable.packages with value org.eclipse.core.examples.databinding.pojo.bindable.model like this :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar 
-Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model
[edit] with Java code by implementing BindableStrategyProvider

You can configure Bindable Java Agent with Java code BindableStrategyProvider with value org.eclipse.core.examples.databinding.pojo.bindable.model by implementing org.eclipse.core.databinding.pojo.bindable.BindableStrategyProvider interface like this :

package org.eclipse.core.examples.databinding.pojo.bindable.provider;
 
import org.eclipse.core.databinding.pojo.bindable.BindableStrategy;
import org.eclipse.core.databinding.pojo.bindable.BindableStrategyProvider;
import org.eclipse.core.databinding.pojo.bindable.DefaultBindableStrategy;
 
public class MyBindableStrategyProvider implements BindableStrategyProvider {
 
	private DefaultBindableStrategy bindableStrategy = null;
 
	public MyBindableStrategyProvider() {
 
		bindableStrategy = new DefaultBindableStrategy(null);
		bindableStrategy
				.addPackageName("org.eclipse.core.examples.databinding.pojo.bindable.model");	
	}
 
	public BindableStrategy getBindableStrategy() {
		return bindableStrategy;
	}
 
}

After you must launch your program with -Dbindable.strategy_provider like this :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar
-Dbindable.strategy_provider=org.eclipse.core.examples.databinding.pojo.bindable.provider.MyBindableStrategyProvider
[edit] with bindable.properties file

You can configure Bindable Java Agent with bindable.properties file'with value org.eclipse.core.examples.databinding.pojo.bindable.model by creating bindable.properties file into src folder with this content :

bindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model

After you can launch your program only with javaagent JVM parameter :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.ja
[edit] Configuration properties of Pojo Bindable
[edit] bindable.packages=<your model package>

BindableAgent requires the property "bindable.packages" to know which Class owned "bindable.packages", must be transformed to add PropertyChangeSupport. If you have several packages you can use ';' character as delimiter (ex : Dbindable.packages=com.example.package1;com.example.package2).

[edit] bindable.use_annotation=<true|false>

BindableAgent use "bindable.use_annotation" property to see if @Bindable annotation must be used or not. This property is not required and by default the value is false.

[edit] bindable.strategy_provider=<provider>

Set an implementation of BindableStrategyProvider to configure BindableStrategy with Java code.

[edit] bindable.gen_basedir=<path>

Set the path of base dir if you wish generate the result of transformed class into a file.

[edit] bindable.debug=<true|false>

If true, display information about BindableAgent and Class Transformation.

[edit] Configure Pojo Bindable Java Agent with -Dbindable.packages

Create a lib folder and copy/paste Pojo Bindable Java Agent org.eclipse.core.databinding.pojo.bindable_1.0.0.jar (that you can find into org.eclipse.core.tests.databinding.pojo.bindable/lib folder).

We must define 2 JVM Parametres with our example :

  • -javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar : our BindableAgent is stored into lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar.
  • -Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model : our Pojo package is org.eclipse.core.examples.databinding.pojo.bindable.model.

To fill JVM Parameters that with Eclipse IDE, click on Run->Run Configurations. Select "PojoPersonBindableTest" launch (from Java Application node) and click on Arguments tabs. Fill VM Arguments textarea with this content :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar 
-Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model

PojoBindable VMParameters.png

[edit] Add ASM in your ClassPath

Click on Run and you will have this error :

Caused by: java.lang.NoClassDefFoundError: org/objectweb/asm/Opcodes
...
	at org.eclipse.core.databinding.pojo.bindable.agent.BindableAgent.premain(BindableAgent.java:67)FATAL ERROR in native method: processing of -javaagent failed
...

This error means that you must add ObjectWeb ASM in your ClassPath. Indead Pojo Bindable use ObjectWeb ASM 3.2.0 to change bytecode. You can download ASM here or use ASM bundle com.springsource.org.objectweb.asm ASM bundle com.springsource.org.objectweb.asm.commons (ASM OSGified) that you can find into the Pojo Bindable projects. For our example you can copy/paste ASM Jars (that you can find on bindable-pojo-test) com.springsource.org.objectweb.asm-3.2.0.jar and com.springsource.org.objectweb.asm.commons-3.2.0.jar into lib folder and add it into Class Path project.

[edit] Add Eclipse/Core/Runtime in your ClassPath

Run it and error will display :

Caused by: java.lang.NoClassDefFoundError: org/eclipse/core/runtime/IStatus
	at org.eclipse.core.databinding.pojo.bindable.agent.BindableAgent.getBindableStrategyFromPackages(BindableAgent.java:160)
...

This erro means that you must add Eclipse/Core/Runtime into your ClassPath because Pojo Bindable use org.eclipse.core.runtime.IStatus to log messages (like ILogger from JFace Databinding). Add org.eclipse.equinox.common_*.jar into lib and add it into Class Path project (you can find this Jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar on bindable-pojo-test).

Run it and error must disappear.

[edit] Pojo Transformed with Pojo Bindable

At this time, when PojoPerson Class is loaded, BindableAgent transform bytecode with this content :

package org.eclipse.core.examples.databinding.pojo.bindable.model;
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
 
import org.eclipse.core.databinding.pojo.bindable.BindableAware;
 
public class PojoPerson implements BindableAware {
 
	private String name = "HelloWorld";
	private transient PropertyChangeSupport _bindable_propertyChangeSupport;
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		String s = getName();
		this.name = name;
		_bindable_getPropertyChangeSupport().firePropertyChange("name", s,
				getName());
	}
 
	private PropertyChangeSupport _bindable_getPropertyChangeSupport() {
		if (_bindable_propertyChangeSupport == null)
			_bindable_propertyChangeSupport = new PropertyChangeSupport(this);
		return _bindable_propertyChangeSupport;
	}
 
	public void addPropertyChangeListener(String s,
			PropertyChangeListener propertychangelistener) {
		_bindable_getPropertyChangeSupport().addPropertyChangeListener(s,
				propertychangelistener);
	}
 
	public void addPropertyChangeListener(
			PropertyChangeListener propertychangelistener) {
		_bindable_getPropertyChangeSupport().addPropertyChangeListener(
				propertychangelistener);
	}
 
	public void removePropertyChangeListener(String s,
			PropertyChangeListener propertychangelistener) {
		_bindable_getPropertyChangeSupport().removePropertyChangeListener(s,
				propertychangelistener);
	}
 
	public void removePropertyChangeListener(
			PropertyChangeListener propertychangelistener) {
		_bindable_getPropertyChangeSupport().removePropertyChangeListener(
				propertychangelistener);
	}
 
 
}

As you can see, Bindable Agent add PropertyChangeSupport capability and update bytecode of PojoPerson#setName(String name) to call PropertyChangeSupport#firePropertyChange(String propertyName, Object oldValue, Object newValue).

[edit] getter getXXX()/isXXX() required

Here code of setName :

public void setName(String name) {
  String s = getName();
  this.name = name;
  _bindable_getPropertyChangeSupport().firePropertyChange("name", s, getName());
}

As you can notice, getName() is used into setName to get the old value. It's a restriction to use Pojo Bindable. For boolean primtive type isXXX is required.

public void setBooleanValue(boolean value) {
  Boolean oldValue = Boolean.valueOf(isBooleanValue());
  this.value = value;
  _bindable_getPropertyChangeSupport().firePropertyChange("booleanValue", oldValue , Boolean.valueOf(isBooleanValue()));
}
[edit] setter with primitive type

When primitive type is used into setter, The old/new value is converted to Java Object (boolean to java.lang.Boolean, short to java.lang.Short....). Here sampel with short value :

public void setShortValue(short value) {
  Short oldValue = Short.valueOf(getShortValue());
  this.value = value;
  _bindable_getPropertyChangeSupport().firePropertyChange("shortValue", oldValue , Short.valueOf(getShortValue()));
}


[edit] Implements BindableAware

You can notice that PojoPerson implements org.eclipse.core.databinding.pojo.bindable.BindableAware interface. Here te content of this BindableAware interface :

package org.eclipse.core.databinding.pojo.bindable;
 
import java.beans.PropertyChangeListener;
 
public interface BindableAware {
 
	void addPropertyChangeListener(String propertyName,
			PropertyChangeListener listener);
 
	void addPropertyChangeListener(PropertyChangeListener listener);
 
	void removePropertyChangeListener(String propertyName,
			PropertyChangeListener listener);
 
	void removePropertyChangeListener(PropertyChangeListener listener);
}

[edit] Add/Remove PropertyChangeListener with Reflection

BindableAgent must transform our Pojo bytecode with PropertyChangeSupport on Class loading of our PojoPerson. Method PojoPerson#addPropertyChangeListener and PojoPerson#removePropertyChangeListener are not available when PojoPersonBindableTest is coding. So we can use Reflection to

  • add PropertyChangeListener, we can do that :
    try {
      Method m = person.getClass().getMethod("addPropertyChangeListener", String.class, PropertyChangeListener.class);
      m.invoke(person, "name", listener);
    } catch (Exception e) {
      e.printStackTrace();
    }
  • remove PropertyChangeListener, we can do that :
    try {
      Method m = person.getClass().getMethod("removePropertyChangeListener", String.class, PropertyChangeListener.class);
      m.invoke(person, "name", listener);
    } catch (Exception e) {
      e.printStackTrace();
    }

Update PojoPersonBindableTest like this :

package org.eclipse.core.examples.databinding.pojo.bindable;
 
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
 
import org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson;
 
public class PojoPersonBindableTest {
 
	public static void main(String[] args) {
 
		// Create listener
		PropertyChangeListener listener = new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent event) {
				System.out
						.println("---------- Property PojoPerson changed --------");
				System.out.println("  PropertyName=" + event.getPropertyName());
				System.out.println("  OldValue=" + event.getOldValue());
				System.out.println("  NewValue=" + event.getNewValue());
				System.out
						.println("------------------------------------------");
			}
		};
 
		// Create Pojo instance
		PojoPerson person = new PojoPerson();
		// Add listener
		// person.addPropertyChangeListener("name", listener);
		try {
			Method m = person.getClass().getMethod("addPropertyChangeListener",
					String.class, PropertyChangeListener.class);
			m.invoke(person, "name", listener);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// Here Change "name" property of PojoPerson is tracked.
		person.setName("New name");
		// Remove listener
		// person.removePropertyChangeListener("name", listener);
		try {
			Method m = person.getClass().getMethod("removePropertyChangeListener",
					String.class, PropertyChangeListener.class);
			m.invoke(person, "name", listener);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// Here Change "name" property of PojoPerson is not tracked.
		person.setName("New name2");
	}
}

Run the program and you will notice that we can track change of "name" property. If you have this error :

java.lang.NoSuchMethodException: org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson.addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
	at java.lang.Class.getMethod(Unknown Source)
...

It means that BindableAgent doesn't execute transformation. Please check your parameter bindable.packages and if you execute your programm with -javaagent.

[edit] Add/Remove PropertyChangeListener with BindableAware

Reflection works very well but the code is awfull. Into section Pojo Transformed with Pojo Bindable we can see that our PojoPerson implements org.eclipse.core.databinding.pojo.bindable.BindableAware interface. To avoid using Reflection, we can cast our PojoPerson with this interface. To use this interface you must add Pojo Bindable Agent in your ClassPath. Add org.eclipse.core.databinding.pojo.bindable_1.0.0.jar library in your ClassPath.

To add Listener you can cast person instance into BindableAware :

PojoPerson person = new PojoPerson();
// Add listener
((BindableAware) person).addPropertyChangeListener("name", listener);

To avoid having ClassCastException (if you launch your program without javaagent), it's better to test if PojoPerson implements BindableAware :

PojoPerson person = new PojoPerson();
// Add listener
if (person instanceof BindableAware) {
  ((BindableAware) person).addPropertyChangeListener("name", listener);
}

Pojo Bindable provides an Helper which do that. You can write :

if (BindableHelper.isBindableAware(person)) {
// Add listener
  ((BindableAware) person).addPropertyChangeListener("name", listener);
}

Update PojoPersonBindableTest like this :

package org.eclipse.core.examples.databinding.pojo.bindable;
 
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
 
import org.eclipse.core.databinding.pojo.bindable.BindableAware;
import org.eclipse.core.databinding.pojo.bindable.BindableHelper;
import org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson;
 
public class PojoPersonBindableTest {
 
	public static void main(String[] args) {
 
		// Create listener
		PropertyChangeListener listener = new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent event) {
				System.out
						.println("---------- Property PojoPerson changed --------");
				System.out.println("  PropertyName=" + event.getPropertyName());
				System.out.println("  OldValue=" + event.getOldValue());
				System.out.println("  NewValue=" + event.getNewValue());
				System.out
						.println("------------------------------------------");
			}
		};
 
		// Create Pojo instance
		PojoPerson person = new PojoPerson();
		if (BindableHelper.isBindableAware(person)) {
			// Add listener
			((BindableAware) person)
					.addPropertyChangeListener("name", listener);
		}
		// Here Change "name" property of PojoPerson is tracked.
		person.setName("New name");
		if (BindableHelper.isBindableAware(person)) {
			// Remove listener
			((BindableAware) person).removePropertyChangeListener("name",
					listener);
		}
		// Here Change "name" property of PojoPerson is not tracked.
		person.setName("New name2");
	}
}

Run it to check that property "name" of pur Pojo PojoPerson can be tracked.

[edit] -Dbindable.debug=true

To debug BindableAgent and check your Pojo class is transformed, you can add the parameter into JVM parameters :

-Dbindable.debug=true

If you lanch the previous example with this parameter, console will display :

Status INFO: NO Transformation for class: org/eclipse/core/internal/runtime/LocalizationUtils
Status INFO: NO Transformation for class: org/eclipse/core/internal/runtime/CommonMessages
Status INFO: Java Bindable Agent called, with configuration: 
bindable.packages=org/eclipse/core/examples/databinding/pojo/bindable/model
bindable.use_annotation=false
bindable.debug=true
bindable.gen_basedir=D://tmp/
bindable.strategy_provider=null
Status INFO: NO Transformation for class: org/eclipse/core/examples/databinding/pojo/bindable/PojoPersonBindableTest
Status INFO: NO Transformation for class: org/eclipse/core/examples/databinding/pojo/bindable/PojoPersonBindableTest$1
Status INFO: Transform class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson
Status INFO: NO Transformation for class: org/eclipse/core/databinding/pojo/bindable/BindableAware
---------- Property PojoPerson changed --------
  PropertyName=name
  OldValue=HelloWorld
  NewValue=New name
------------------------------------------

You can notice that console display each Class wich must be loaded. The message

Status INFO: NO Transformation for class: ...

means that Class is not transformed.

Status INFO: Transform class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson

means that our PojoPerson class is transformed.

You can notice on start of teh console teh configuration of Pojo Bindable :

Status INFO: Java Bindable Agent called, with configuration: 
bindable.packages=org/eclipse/core/examples/databinding/pojo/bindable/model
bindable.use_annotation=false
bindable.debug=true
bindable.gen_basedir=D://tmp/
bindable.strategy_provider=null

[edit] -Dbindable.gen_basedir=D://tmp/

You can if you wish, generate the result of class transformed into file. To do that you must add the parameter into JVM parameters :

-Dbindable.gen_basedir=D://tmp/

Here, result of class transformed will be generate into folder D://tmp/. If you lanch the previous example with this parameter, console will display :

...
Status INFO: Transform class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson
Status INFO: Generate transformed class: org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson into file: D://tmp/org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson.class
Status INFO: Transformed class output written to: D://tmp/org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson.class
...

You can check that you have D://tmp/org/eclipse/core/examples/databinding/pojo/bindable/model/PojoPerson.class file generated. You can decompile it to check that this class implements BindableAware interface.

[edit] -Dbindable.strategy_provider=org...MyBindableStrategyProvider

We have configured BindableAgent with several JVM parameters (-Dbindable.packages, -Dbindable.debug...), but it's possible to configure it with Java code. To do that create a class wich implements org.eclipse.core.databinding.pojo.bindable.BindableStrategyProvider interface. You can found sample org.eclipse.core.examples.databinding.pojo.bindable.provider.MyBindableStrategyProvider into org.eclipse.core.examples.databinding.pojo.bindable bundle.

package org.eclipse.core.examples.databinding.pojo.bindable.provider;
 
import org.eclipse.core.databinding.pojo.bindable.BindableStrategy;
import org.eclipse.core.databinding.pojo.bindable.BindableStrategyProvider;
import org.eclipse.core.databinding.pojo.bindable.DefaultBindableStrategy;
 
public class MyBindableStrategyProvider implements BindableStrategyProvider {
 
	private DefaultBindableStrategy bindableStrategy = null;
 
	public MyBindableStrategyProvider() {
 
		bindableStrategy = new DefaultBindableStrategy(null);
		bindableStrategy
				.addPackageName("org.eclipse.core.examples.databinding.pojo.bindable.model");
		bindableStrategy.setDebugMode(true);
		// bindableStrategy.setGenBaseDir("D://tmp");
		bindableStrategy.setUseAnnotation(false);
	}
 
	public BindableStrategy getBindableStrategy() {
		return bindableStrategy;
	}
 
}

Launch Bindable Agent with -Dbindable.strategy_provider like this :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar 
-Dbindable.strategy_provider=org.eclipse.core.examples.databinding.pojo.bindable.provider.MyBindableStrategyProvider

You can find that into the launch/HelloWorldWithBeansObservablesWithBindableAgentProvider.launch

[edit] Pojo Bindable & JFace Databinding

In this section we will use Pojo Bindable with JFace Databinding. It explain examples of the project org.eclipse.core.examples.databinding.pojo.bindable. In this project you can find 3 samples into org.eclipse.core.examples.databinding.pojo.bindable.snippets package :

  • HelloWorldWithPojoObservables : bind the property "name" of pur Pojo Person with SWT UI Text by using PojoObservables. Binding UI -> Pojo model is only available, on other words when Pojo model change, UI is not updated.
  • HelloWorldWithBeansObservables : bind the property "name" of JavaBean Person with SWT UI Text by using BeansObservables. Binding UI <-> Pojo model is available, on other words when JavaBean model change, UI is too updated.
  • HelloWorldWithBeansObservablesWithBindableAgent : bind the property "name" of pur Pojo PojoPerson with SWT UI Text by using BeansObservables. Binding UI <-> Pojo model is available, on other words when Pojo model change, UI is too updated. This sampel must be launched with Pojo Bindable Agent.

[edit] HelloWorldWithPojoObservables

This sample use pur Pojo :

package org.eclipse.core.examples.databinding.pojo.bindable.model;
 
public class PojoPerson {
 
	String name = "HelloWorld";
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
}

In this sample, we bind the property "name" of pur Pojo Person with SWT UI Text by using PojoObservables. Here the code of HelloWorldWithPojoObservables :

package org.eclipse.core.examples.databinding.pojo.bindable.snippets;
 
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
 
/**
 * Hello, databinding. Bind POJO with {@link PojoObservables}. Model change
 * doesn't update UI.
 */
public class HelloWorldWithPojoObservables {
	public static void main(String[] args) {
		Display display = new Display();
		final ViewModel viewModel = new ViewModel();
 
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
			public void run() {
				final Shell shell = new View(viewModel).createShell();
				// The SWT event loop
				Display display = Display.getCurrent();
				while (!shell.isDisposed()) {
					if (!display.readAndDispatch()) {
						display.sleep();
					}
				}
			}
		});
		// Print the results
		System.out.println("person.getName() = "
				+ viewModel.getPerson().getName());
	}
 
	static class ViewModel {
		// The model to bind
		private PojoPerson person = new PojoPerson();
 
		public PojoPerson getPerson() {
			return person;
		}
	}
 
	// The GUI view
	static class View {
		private ViewModel viewModel;
		private Text name;
 
		public View(ViewModel viewModel) {
			this.viewModel = viewModel;
		}
 
		public Shell createShell() {
			// Build a UI
			Display display = Display.getDefault();
			Shell shell = new Shell(display);
			shell.setLayout(new RowLayout(SWT.VERTICAL));
			name = new Text(shell, SWT.BORDER);
 
			Button changeModelButton = new Button(shell, SWT.BORDER);
			changeModelButton.setText("Change model");
 
			// Bind it
			DataBindingContext bindingContext = new DataBindingContext();
			PojoPerson person = viewModel.getPerson();
 
			bindingContext.bindValue(SWTObservables.observeText(name,
					SWT.Modify), PojoObservables.observeValue(person, "name"));
 
			changeModelButton.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					PojoPerson person = viewModel.getPerson();
					person.setName("HelloWorld");
				}
			});
 
			// Open and return the Shell
			shell.pack();
			shell.open();
			return shell;
		}
	}
 
}

When you launch this code, you can see this window :

PojoBindable HelloWord.png

Binding between SWT Text and Pojo model is done with PojoObservables :

bindingContext.bindValue(SWTObservables.observeText(name, SWT.Modify), BeansObservables.observeValue(person, "name"));

SWT Text is filled with "HelloWord" wich come from of the initial value of the PojoPerson. When UI is changed, property "name" of the Pojo is updated. To check that, change UI Text with "a" value. and close the window, console display the value of "name" property of the Pojo :

person.getName() = a

In this sample there is "Change model" button which change "name" property of Pojo model with "HelloWorld" value :

changeModelButton.addSelectionListener(new SelectionAdapter() {
  @Override
  public void widgetSelected(SelectionEvent e) {
    PojoPerson person = viewModel.getPerson();
      person.setName("HelloWorld");
    }
});

If you update the UI with "a" value and you click on "Change model" button, UI is not updated with new value of the model Pojo.

[edit] HelloWorldWithBeansObservables

We have seen that with PojoObservables we can manage only UI -> Model but not UI <- Model (UI is initialized with Model but when Model change, UI is not updated). To manage UI <- Model, we must use BeansObservables. Copy paste HelloWorldWithPojoObservables and rename it with HelloWorldWithBeansObservables. Update PojoObservables with BeansObservables :

bindingContext.bindValue(SWTObservables.observeText(name, SWT.Modify), BeansObservables.observeValue(person, "name"));

Run the HelloWorldWithBeansObservables and you will have this error :

Status WARNING: org.eclipse.core.databinding code=0 Could not attach listener to org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson@743399 java.lang.NoSuchMethodException: org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson.addPropertyChangeListener(java.beans.PropertyChangeListener)
java.lang.NoSuchMethodException: org.eclipse.core.examples.databinding.pojo.bindable.model.PojoPerson.addPropertyChangeListener(java.beans.PropertyChangeListener)
...

This exception show you that you must define in the model, methods addPropertyChangeListener and removePropertyChangeListener. PojoPerson become JavaBeanPerson :

package org.eclipse.core.examples.databinding.pojo.bindable.model;
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
 
public class JavaBeanPerson {
 
	// A property...
	String name = "HelloWorld";
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		Object oldValue = this.name;
		Object newValue = name;
		this.name = name;
		firePropertyChange("name", oldValue, newValue);
	}
 
	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
			this);
 
 
	public void addPropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		propertyChangeSupport.addPropertyChangeListener(propertyName,
				listener);
	}
 
 
	public void removePropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		propertyChangeSupport.removePropertyChangeListener(propertyName,
				listener);
	}
 
	protected void firePropertyChange(String propertyName, Object oldValue,
			Object newValue) {
		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
				newValue);
	}
}

Use JavaBeanPerson inseatd of PojoPerson and launch HelloWorldWithBeansObservables. Error will disappear, and when you wil click on "Change Model", SWT UI will be updated with "HelloWorld" value comminf from the Model.

[edit] HelloWorldWithBeansObservablesWithBindableAgent

Here we will use Pojo Bindable to use pur Pojo with BeansObservables. Class HelloWorldWithBeansObservablesWithBindableAgent use BeansObservables although our model is a pur Pojo (without addPropertyChangeListener/removeropertyChangeListener methods).

PojoPerson person = ...
...
bindingContext.bindValue(SWTObservables.observeText(name,
SWT.Modify), BeansObservables.observeValue(person, "name"));

HelloWorldWithBeansObservablesWithBindableAgent must launch with Bindable Java Agent. To do that, you must :

  • Add Bindable Java Agent org.eclipse.core.databinding.pojo.bindable_1.0.0.jar into lib project.
  • Add SM in your ClassPath
  • configure Bindable Java Agent into 2 JVM parameters :
    • -javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar : our BindableAgent is stored into lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar.
    • -Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model : our Pojo PojoPerson package is org.eclipse.core.examples.databinding.pojo.bindable.model.

Lanch the program with Bindable JavaAgent filled into JVM Parameters :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar -Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model

You can find this launch into \launch\HelloWorldWithBeansObservablesWithBindableAgent.launch. Launch HelloWorldWithBeansObservablesWithBindableAgent and you will notice that UI<->Model is available by using pur Pojo.

[edit] @Bindable annotation

Sometimes you have "set" method into your model Pojo and you will NOT transform setXXX method by calling PropertyChangeSupport. To do that you can use @Bindable annotation. For instance :

At first you must available @Bindable annotation with JVM parameters bindable.use_annotation=true.

[edit] @Bindable#value

After you can write this to transform setName method and avoid transforming setLogin method :

package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations;
 
import org.eclipse.core.databinding.pojo.bindable.Bindable;
 
public class UserBindable {
 
	private String name;
 
	private String login;
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public String getLogin() {
		return login;
	}
 
	@Bindable(value = false)
	// This method must not be transformed with firePropertyChange.
	public void setLogin(String login) {
		this.login = login;
	}
 
}

You can set @Bindable before class declaration to disable transformation for setXXX method. In this sample setName is not transformed and setLogin is transformed :

package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations;
 
import org.eclipse.core.databinding.pojo.bindable.Bindable;
 
@Bindable(value = false)
// All methods must not be transformed with firePropertyChange.
public class UserNotBindable {
 
	private String name;
 
	private String login;
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public String getLogin() {
		return login;
	}
 
	@Bindable(value = true)
	// This method MUST ne transformed with firePropertyChange.
	public void setLogin(String login) {
		this.login = login;
	}
}

Project org.eclipse.core.tests.databinding.pojo.bindable show you usecase with org.eclipse.core.databinding.pojo.bindable.Bindable annotation.

[edit] @Bindable#dependsOn

If you wish manage computed values, you can use @Bindable#dependsOn. You can find TestCase into ComputedValueTest which use model ComputedValueTestEntity

Imagine you have this Pojo :

package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.computed;
 
import java.beans.PropertyChangeEvent;
 
import org.eclipse.core.databinding.pojo.bindable.annotation.Bindable;
 
public class ComputedValueTestEntity {
 
	private double sellingPrice;
	private double buyingPrice;
 
	public ComputedValueTestEntity(double sellingPrice, double buyingPrice) {
		this.sellingPrice = sellingPrice;
		this.buyingPrice = buyingPrice;
	}
 
	public double getSellingPrice() {
		return sellingPrice;
	}
 
	public void setSellingPrice(double sellingPrice) {
		this.sellingPrice = sellingPrice;
	}
 
	public double getBuyingPrice() {
		return buyingPrice;
	}
 
	public void setBuyingPrice(double buyingPrice) {
		this.buyingPrice = buyingPrice;
	}
 
	public double getRatio() {
		return sellingPrice / buyingPrice;
	}
 
	public boolean isCheapBuyingPrice() {
		return buyingPrice < 100;
	}
 
}

Now you want observe changed of the "ratio" property (ComputedValueTestEntity#getRatio()). As you can notice, the method ComputedValueTestEntity#setRatio(double ratio) doesn't exist, because "ratio" property depends on "sellingPrice" and "buyingPrice" properties. So "ratio" change when "sellingPrice" and "buyingPrice" change, on other words when methods setSellingPrice/setBuyingPrice are called.

Imagine you want bind this "ratio" property with a SWT Label. An event PropertyChangeEvent with property name "ratio" must be fired when setSellingPrice/setBuyingPrice methods are called. It means that "ratio" property, depends on call of setSellingPrice and setBuyingPrice methods. You can manage that with Pojo Bindable with @Bindable#dependsOn declared into (ComputedValueTestEntity#getRatio()) getter method like this :

@Bindable(dependsOn = { "sellingPrice", "buyingPrice" })
public double getRatio() {
    return sellingPrice / buyingPrice;
}


After you can observe "ratio" property. Here a sample code to observe it :

PropertyChangeListener listener = new PropertyChangeListener() {
	public void propertyChange(PropertyChangeEvent event) {
		System.out
				.println("---------- Property ComputedValueTestEntity changed --------");
		System.out.println("  PropertyName=" + event.getPropertyName());
		System.out.println("  OldValue=" + event.getOldValue());
		System.out.println("  NewValue=" + event.getNewValue());
		System.out
				.println("------------------------------------------");		
	}
};
 
// Create ComputedValueTestEntity model
ComputedValueTestEntity p = new ComputedValueTestEntity(4, 2);
 
// addPropertyChangeListener to observe ratio (javagent must be used to transform bytecode!!!) 
((BindableAware)p).addPropertyChangeListener("ratio", listener);
 
// Change buyingPrice, event is fired with 'ratio' property 
p.setBuyingPrice(100);
// Change sellingPrice, event is fired with 'ratio' property
p.setSellingPrice(100);


If you launch this sample with Pojo Bindable Java Agent :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar -Dbindable.packages=org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.computed -Dbindable.use_annotation=true

You will see on console :

---------- Property ComputedValueTestEntity changed --------
  PropertyName=ratio
  OldValue=2.0
  NewValue=0.04
------------------------------------------
---------- Property ComputedValueTestEntity changed --------
  PropertyName=ratio
  OldValue=0.04
  NewValue=1.0
------------------------------------------

For information Pojo Bindable change the bytecode of the Pojo like this :

package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.computed;
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import org.eclipse.core.databinding.pojo.bindable.BindableAware;
 
public class ComputedValueTestEntity
    implements BindableAware
{
 
    private double sellingPrice;
    private double buyingPrice;
    private transient PropertyChangeSupport _bindable_propertyChangeSupport;
    private transient double _bindable_setSellingPrice_ratio;
    private transient double _bindable_setBuyingPrice_ratio;
 
    public ComputedValueTestEntity(double sellingPrice, double buyingPrice)
    {
        this.sellingPrice = sellingPrice;
        this.buyingPrice = buyingPrice;
    }
 
    public double getSellingPrice()
    {
        return sellingPrice;
    }
 
    public void setSellingPrice(double sellingPrice)
    {
        _bindable_beforeDependsOn_setSellingPrice();
        Double double1 = Double.valueOf(getSellingPrice());
        this.sellingPrice = sellingPrice;
        _bindable_getPropertyChangeSupport().firePropertyChange("sellingPrice", double1, Double.valueOf(getSellingPrice()));
        _bindable_afterDependsOn_setSellingPrice();
    }
 
    public double getBuyingPrice()
    {
        return buyingPrice;
    }
 
    public void setBuyingPrice(double buyingPrice)
    {
        _bindable_beforeDependsOn_setBuyingPrice();
        Double double1 = Double.valueOf(getBuyingPrice());
        this.buyingPrice = buyingPrice;
        _bindable_getPropertyChangeSupport().firePropertyChange("buyingPrice", double1, Double.valueOf(getBuyingPrice()));
        _bindable_afterDependsOn_setBuyingPrice();
    }
 
    public double getRatio()
    {
        return sellingPrice / buyingPrice;
    }
 
    private PropertyChangeSupport _bindable_getPropertyChangeSupport()
    {
        if(_bindable_propertyChangeSupport == null)
            _bindable_propertyChangeSupport = new PropertyChangeSupport(this);
        return _bindable_propertyChangeSupport;
    }
 
    public void addPropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(s, propertychangelistener);
    }
 
    public void addPropertyChangeListener(PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(propertychangelistener);
    }
 
    public void removePropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(s, propertychangelistener);
    }
 
    public void removePropertyChangeListener(PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(propertychangelistener);
    }
 
    private void _bindable_beforeDependsOn_setSellingPrice()
    {
        _bindable_setSellingPrice_ratio = getRatio();
    }
 
    private void _bindable_afterDependsOn_setSellingPrice()
    {
        _bindable_getPropertyChangeSupport().firePropertyChange("ratio", Double.valueOf(_bindable_setSellingPrice_ratio), Double.valueOf(getRatio()));
    }
 
    private void _bindable_beforeDependsOn_setBuyingPrice()
    {
        _bindable_setBuyingPrice_ratio = getRatio();
    }
 
    private void _bindable_afterDependsOn_setBuyingPrice()
    {
        _bindable_getPropertyChangeSupport().firePropertyChange("ratio", Double.valueOf(_bindable_setBuyingPrice_ratio), Double.valueOf(getRatio()));
    }
 
}

[edit] @Bindable#fireEvents

If you wish force fire events, when you call methods you can use @Bindable#fireEvents. You can find TestCase into PeopleTestCase which use model People

Imagine you have this Pojo :

package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents;
 
import java.util.ArrayList;
import java.util.Collection;
 
public class People {
 
  private Collection<Person> people = null;
 
  public void addPerson(Person person) {
    if (people == null) {
      people = new ArrayList<Person>();
    }
    people.add(person);
  }
 
  public Collection<Person> getPeople() {
    return people;
  }
 
  public static class Person {
 
  }
}

When People#addPerson(Person) is called you want fire events "people" with old/new list of Person to observe changed of the "people" property. You can manage that with Pojo Bindable with @Bindable#fireEvents declared into (People#addPerson(Person)) method like this :

@Bindable(fireEvents = { "people" })
public void addPerson(Person person) {
	if (people == null) {
		people = new ArrayList<Person>();
	}
	people.add(person);
}

People#getPeople() is required to get the old/new value by using the property "people". After you can observe "people" property. Here a sample code to observe it :

People people = new People();
BindableAware peopleBindable = (BindableAware) people;
 
// Instrumentation was done with successfull, add Listener
PropertyChangeListener listener = new PropertyChangeListener() {
	public void propertyChange(PropertyChangeEvent event) {
		System.out.println("---------- Property People changed --------");
		System.out.println("  PropertyName=" + event.getPropertyName());
		System.out.println("  OldValue=" + event.getOldValue());
		System.out.println("  NewValue=" + event.getNewValue());
		System.out.println("------------------------------------------");
		testEvent = event;
	}
};
 
peopleBindable.addPropertyChangeListener("people", listener);

If you launch this sample with Pojo Bindable Java Agent :

-javaagent:lib/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar -Dbindable.packages=org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents
-Dbindable.use_annotation=true

You will see on console :

---------- Property People changed --------
  PropertyName=people
  OldValue=null
  NewValue=[org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents.People$Person@92e78c]
------------------------------------------

For information Pojo Bindable change the bytecode of the Pojo like this :

package org.eclipse.core.tests.databinding.pojo.bindable.domain.annotations.fireEvents;
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.core.databinding.pojo.bindable.BindableAware;
 
public class People
    implements BindableAware
{
 
    private Collection people;
    private transient PropertyChangeSupport _bindable_propertyChangeSupport;
    private transient Collection _bindableFireEvents_addPerson_people;
 
    public People()
    {
        people = null;
    }
 
    public void addPerson(Person person)
    {
        _bindable_beforeFireEvents_addPerson();
        if(people == null)
            people = new ArrayList();
        people.add(person);
        _bindable_afterFireEvents_addPerson();
    }
 
    public Collection getPeople()
    {
        return people;
    }
 
    private PropertyChangeSupport _bindable_getPropertyChangeSupport()
    {
        if(_bindable_propertyChangeSupport == null)
            _bindable_propertyChangeSupport = new PropertyChangeSupport(this);
        return _bindable_propertyChangeSupport;
    }
 
    public void addPropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(s, propertychangelistener);
    }
 
    public void addPropertyChangeListener(PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().addPropertyChangeListener(propertychangelistener);
    }
 
    public void removePropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(s, propertychangelistener);
    }
 
    public void removePropertyChangeListener(PropertyChangeListener propertychangelistener)
    {
        _bindable_getPropertyChangeSupport().removePropertyChangeListener(propertychangelistener);
    }
 
    private void _bindable_beforeFireEvents_addPerson()
    {
        _bindableFireEvents_addPerson_people = getPeople();
    }
 
    private void _bindable_afterFireEvents_addPerson()
    {
        _bindable_getPropertyChangeSupport().firePropertyChange("people", _bindableFireEvents_addPerson_people, getPeople());
    }
 
   public static class Person
        implements BindableAware
    {
 
        private transient PropertyChangeSupport _bindable_propertyChangeSupport;
 
        public Person()
        {
        }
 
        private PropertyChangeSupport _bindable_getPropertyChangeSupport()
        {
            if(_bindable_propertyChangeSupport == null)
                _bindable_propertyChangeSupport = new PropertyChangeSupport(this);
            return _bindable_propertyChangeSupport;
        }
 
        public void addPropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
        {
            _bindable_getPropertyChangeSupport().addPropertyChangeListener(s, propertychangelistener);
        }
 
        public void addPropertyChangeListener(PropertyChangeListener propertychangelistener)
        {
            _bindable_getPropertyChangeSupport().addPropertyChangeListener(propertychangelistener);
        }
 
        public void removePropertyChangeListener(String s, PropertyChangeListener propertychangelistener)
        {
            _bindable_getPropertyChangeSupport().removePropertyChangeListener(s, propertychangelistener);
        }
 
        public void removePropertyChangeListener(PropertyChangeListener propertychangelistener)
        {
            _bindable_getPropertyChangeSupport().removePropertyChangeListener(propertychangelistener);
        }
 
    }
 
}

[edit] Use another Java Agent

Only one Java Agent can be filled into JVM parameter -javaagent. If you wish using another Java Agent (like spring-agent.jar), the only thing to do is to find a mean to get the instance of Instrumentation and initialize Pojo Bindable BEFORE loading Class model wich must be transformed.

// Initialize Bindable.
static {
	// Get Instrumentation instance from 
	Instrumentation instrumentation = ???
    // Add it Bindable
	// ClassFileTransformer for Domain POJO.
	BindableStrategy bindableStrategy = new DefaultBindableStrategy( new String[] { "org.eclipse.core.tests.databinding.pojo.bindable.domain" });
	BindableHelper.initialize(bindableStrategy,	instrumentation);
}

Spring-agent provides for instance InstrumentationSavingAgent :

package org.springframework.instrument;
 
import java.lang.instrument.Instrumentation;
 
public class InstrumentationSavingAgent {
 
	private static volatile Instrumentation instrumentation;
 
	/**
	 * Save the {@link Instrumentation} interface exposed by the JVM.
	 */
	public static void premain(String agentArgs, Instrumentation inst) {
		instrumentation = inst;
	}
 
	public static Instrumentation getInstrumentation() {
		return instrumentation;
	}
 
}

Pojo Bindable include this class, and Instrumentation instance can be getted with the code :

Instrumentation instrumentation = InstrumentationSavingAgent.getInstrumentation();

Once you have the instance you can do :

// Initialize Bindable.
static {
	// Get Instrumentation instance from 
	Instrumentation instrumentation = InstrumentationSavingAgent.getInstrumentation();
    // Add it Bindable
	// ClassFileTransformer for Domain POJO.
	BindableStrategy bindableStrategy = new DefaultBindableStrategy( new String[] { "org.eclipse.core.tests.databinding.pojo.bindable.domain" });
	BindableHelper.initialize(bindableStrategy,	instrumentation);
}

You can use too SpringInstrumentationProvider from Pojo Bindable like this :

// Initialize Bindable.
static {
	// Get Instrumentation instance from spring-agent and add it Bindable
	// ClassFileTransformer for Domain POJO.
	BindableStrategy bindableStrategy = new DefaultBindableStrategy( new String[] { "org.eclipse.core.tests.databinding.pojo.bindable.domain" });
	BindableHelper.initialize(bindableStrategy,	SpringInstrumentationProvider.INSTANCE);
}

[edit] OSGi Context

To manage Pojo Bindable into OSGi context, please see Pojo Bindable SVN. It Following the same idea that EclipseLink/Examples/OSGi/Equinox Byte Code Weaving.