Skip to main content

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

Jump to: navigation, search

JFace Data Binding/PojoBindable

< JFace Data Binding
Revision as of 12:17, 7 April 2010 by Azerr.redhat.com (Talk | contribs) (Pojo Bindable Java Agent)

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

Target

Work is underway to support binding with Pojo by using BeansObservable WITHOUT coding firePropertyChange (into setter method)/addPropertyChangeListener/removePropertyChangeListener. See (for the moment) Bug 307417- you can find 4 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.

Pojo Bindable in Action

Before explaining how use Pojo Bindable with JFace Databinding, take a simple example with Pojo. In this section we will observe a property change of a Pojo. To do that we will do :

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.

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).

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, you must launch your program with several JVM parameters :

-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.

-Dbindable.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).

-Dbindable.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.

-Dbindable.strategy_provider=<provider>

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

-Dbindable.gen_basedir=<path>

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

Dbindable.debug=<true|false>

If true, display information about BindableAgent and Class Transformation.

Configure Pojo Bindable Java Agent

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

Add ASM in your ClassPath

Click on Run and you will have this error :

Caused by: java.lang.NoClassDefFoundError: org/objectweb/asm/ClassVisitor
	at org.eclipse.core.databinding.pojo.bindable.BindableHelper.initialize(BindableHelper.java:47)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 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.

Run it and error must disappear.

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;
 
public class PojoPerson implements org.eclipse.core.databinding.pojo.bindable.BindableAware {
 
	private transient PropertyChangeSupport _bindable_propertyChangeSupport = null;
 
	String name = "HelloWorld";
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		Object oldValue = this.name;
		Object newValue = name;
		this.name = name;
		_bindable_getPropertyChangeSupport().firePropertyChange("name ",
				oldValue, newValue);
 
	}
 
	private PropertyChangeSupport _bindable_getPropertyChangeSupport() {
		if (_bindable_propertyChangeSupport == null) {
			this._bindable_propertyChangeSupport = new PropertyChangeSupport(
					this);
		}
		return _bindable_propertyChangeSupport;
	}
 
	public void addPropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		_bindable_getPropertyChangeSupport().addPropertyChangeListener(
				propertyName, listener);
	}
 
	public void removePropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		_bindable_getPropertyChangeSupport().removePropertyChangeListener(
				propertyName, listener);
	}
}

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). 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 removePropertyChangeListener(String propertyName,
			PropertyChangeListener listener);
}

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.

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.

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.

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.

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.

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.

@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. 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.

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);
}

OSGi Context

Today I have no idea how use JavaAgent into OSGi context. I believe it's not possible but EclipseLink works too with change of bytecode to manage Load Time Weaver. 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 EclipseLink/Examples/OSGi/Equinox Byte Code Weaving could be perhaps a solution?

Back to the top