I need to add a SimpleMapProperty to a JavaFX service class, but I am not sure of the correct syntax of if I am using the correct approach. Note that I am not trying to make the JavaFX service appear like a Java Bean, I just need to know how to listen for updates to an EnumMap from a enum ModuleType (that can be TYPEA or TYPEB) and an associated Boolean flag. Essentially, this can be thought of as a pair of watchdog timers wrapped in a single EnumMap.
I am having trouble understanding how to add the underlying EnumMap entries (there should be 2 - one for each ModuleType described above).
public class UDPListenerService extends Service<Void> {
// 'watchdog' property
private final MapProperty<ModuleType, Boolean> watchdog;
// 'watchdog' SimpleMapProperty bound property getter
public ObservableMap<ModuleType, Boolean> getWatchdog() {
return watchdog.get();
}
// 'watchdog' SimpleMapProperty bound property setter
public void setWatchdog(ObservableMap<ModuleType, Boolean> aValue) {
watchdog.set(aValue);
}
// 'watchdog' SimpleMapProperty bound property
public MapProperty<ModuleType, Boolean> watchdogProperty() {
return watchdog;
}
/**
* Constructor
*/
public UDPListenerService()
{
this.watchdog = new SimpleMapProperty<>(
FXCollections.observableHashMap());
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
updateMessage("Running...");
while (!isCancelled()) {
try {
Thread.sleep(1000);
Platform.runLater(() -> {
try {
// update do some processing here
// . . .
// pet the watchdog
// setWatchdog
if (testforModuleType==ModuleType.TYPEA) {
// please help with syntax
setWatchdog(ModuleType.TYPEA, false);
} else {
// please help with syntax
setWatchdog(ModuleType.TYPEB, false);
}
} catch (StatusRuntimeException ex) {
// watchdog timed out - listener will
// update gui components
if (testforModuleType==ModuleType.TYPEA) {
// please help with syntax
setWatchdog(ModuleType.TYPEA, true);
} else {
// please help with syntax
setWatchdog(ModuleType.TYPEB, true);
}
}
});
} catch (InterruptedException ex) {}
}
updateMessage("Cancelled");
return null;
}
};
}
}
The way I use this class is in the JavaFX controller class where I add a listener that populates java gui elements depending on whether the associated Boolean flag is true or false.
Usually a readonly map property is used for this kind of behavior, i.e. a ObservableMap field with only a getter. Only the contents of the map are modified; no new map is assigned to the field after the initial map is assigned.
private final ObservableMap<ModuleType, Boolean> watchdog;
public ObservableMap<ModuleType, Boolean> getWatchdog() {
return watchdog;
}
The map itself is modified the same way a java.util.Map would be modified, e.g. in this case using the put method. Changes can be observed e.g. using a MapChangeListener or Bindings.valueAt.
Furthermore EnumMap can be used as backing Map for a ObservableMap, but to do this the observableMap method needs to be used instead of the observableHashMap method.
The following example randomly selects / deselects values of 2 checkboxes based on values in a ObservableMap.
private CheckBox checkBoxA;
private CheckBox checkBoxB;
private ObservableMap<ModuleType, Boolean> map;
#Override
public void start(Stage primaryStage) {
checkBoxA = new CheckBox("type A");
checkBoxB = new CheckBox("type B");
map = FXCollections.observableMap(new EnumMap<>(ModuleType.class));
initMapListeners();
Thread t = new Thread(() -> {
Random random = new Random();
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
boolean b1 = random.nextBoolean();
boolean b2 = random.nextBoolean();
Platform.runLater(() -> {
map.put(ModuleType.TYPEA, b1);
map.put(ModuleType.TYPEB, b2);
});
}
});
t.setDaemon(true);
t.start();
Scene scene = new Scene(new VBox(10, checkBoxA, checkBoxB));
primaryStage.setScene(scene);
primaryStage.show();
}
Both the following implementations of initMapListeners() would both set the CheckBox.selected states based on the map values.
private void initMapListeners() {
checkBoxA.selectedProperty().bind(Bindings.valueAt(map, ModuleType.TYPEA));
checkBoxB.selectedProperty().bind(Bindings.valueAt(map, ModuleType.TYPEB));
}
private void initMapListeners() {
map.addListener((MapChangeListener.Change<? extends ModuleType, ? extends Boolean> change) -> {
if (change.wasAdded()) {
if (change.getKey() == ModuleType.TYPEA) {
checkBoxA.setSelected(change.getValueAdded());
} else if (change.getKey() == ModuleType.TYPEB) {
checkBoxB.setSelected(change.getValueAdded());
}
}
});
}
Related
I have a function which is supposed to return a list from the result of a Task API.
#Override
public List performQuery(boolean isPaginationQuery, boolean isSortingQuery {
try {
TaskImpl taskImpl = new TaskImpl(isPaginationQuery,
isSortingQuery);
queryExecutor.submit(taskImpl).get();
return taskImpl.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Inner class which performs the updates
private class TaskImpl extends Task<List> {
private boolean isPaginationQuery, isSortingQuery;
public TaskImpl(boolean isPaginationQuery, boolean isSortingQuery) {
this.isPaginationQuery = isPaginationQuery;
this.isSortingQuery = isSortingQuery;
}
#Override
protected List call() throws Exception {
Platform.runLater(() -> {
loaderContainer.setVisible(true);
loaderContainer.toFront();
});
HSession hSession = new HSession();
TaskInfoDao taskInfoDao = new TaskInfoDaoImpl(hSession.getSession(), currentConnection.getConnectionId());
if (!isPaginationQuery && !isSortingQuery) {
paginator.setTotal(taskInfoDao.getTaskInfoWithFiltersCount(paginator.getFilterMap(), false));
}
Stream<TaskInfo> resultStream = taskInfoDao.getTaskInfoWithFilters(paginator.getFilterMap(), false,
paginator.getStartIndex() * paginator.getPageSize(),
paginator.getPageSize() * paginator.getPageGap());
List<TaskInfoTableView> data = createData(resultStream);
hSession.close();
return data;
}
#Override
protected void succeeded() {
super.succeeded();
try {
//set the pagination if the task is complete
//and it is not a pagination query
if (!isPaginationQuery) {
((TaskInfoViewController) uiController).setPagination(
FXCollections.observableArrayList(get()));
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
#Override
protected void cancelled() {
super.cancelled();
updateMessage("Cancelled!");
}
#Override
protected void failed() {
super.failed();
updateMessage("Failed!");
}
}
performQuery function calls the thread and waits for its result.
The loader is being displayed from inside the TaskImpl class using Platform.runLater.
But the loader does not appear until the task has finished i.e. loader appears after the completion of call() function's execution.
When i remove the taskImpl.get() the loader works fine.
Any help is appreciated.
P.S. : Under any case, I need the result of the Task API outside the Inner class( outside TaskImpl )
First of all, it seems like you are not very familiar with asynchronous programming. Having performQuery() to return a List shows that you are expecting to run this synchronously - there is no way for you to return results before you get the results. This is exactly why you are freezing your UI.
The important thing to understand about asynchronous programming is, you would start doing something (i.e. a task) in another thread, and return immediately. When there is result returned from the task, you switch back to the UI (JavaFX Application) thread to update it. You can see this as event-driven approach.
Therefore, for your case, you should directly update the list (the list which you are returning in performQuery()) in the succeeded() method that you have overridden in TaskImpl class.
If the list that you should be updating is not in the scope of TaskImpl, then you can the functional interfaces in java.util.function package to do it for you. This means that you would create that functional interface object at the right scope, and pass in into TaskImpl during object construction, and call that interface in succeeded().
Update
If I assume this is what calls performQuery():
public class MyController {
#FXML
TableView<Foo> tableView;
public void initialize() {
List result = queryController.performQuery(true, true);
tableView.getItems().addAll(result);
}
}
Then, I would probably do something like this:
public class MyController {
#FXML
TableView<Foo> tableView;
public void initialize() {
List result = queryController.performQuery(true, true, list -> tableView.getItems.addAll(list));
}
}
public class QueryController {
#Override
public void performQuery(boolean isPaginationQuery, boolean isSortingQuery, java.util.function.Consumer<List> onQuerySucceeded) {
try {
TaskImpl taskImpl = new TaskImpl(isPaginationQuery,
isSortingQuery, onQuerySucceeded);
queryExecutor.submit(taskImpl);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
private class TaskImpl extends Task<List> {
private final java.util.function.Consumer<List> onQuerySucceeded;
public TaskImpl(boolean isPaginationQuery, boolean isSortingQuery, java.util.function.Consumer<List> onQuerySucceeded) {
this.isPaginationQuery = isPaginationQuery;
this.isSortingQuery = isSortingQuery;
this.onQuerySucceeded = onQuerySucceeded;
}
#Override
protected void succeeded() {
super.succeeded();
// Not sure what the original codes are doing.
try {
//set the pagination if the task is complete
//and it is not a pagination query
if (!isPaginationQuery) {
((TaskInfoViewController) uiController).setPagination(
FXCollections.observableArrayList(get()));
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// This is what is being added in
onQuerySucceeded.accept(this.getValue());
}
}
I am trying to add a ComboBox into the TableView but for some reason I cannot make the conversion. Behind the scenes, I want to make the conversation if the value is
0 then it should display 'free' if the value is 1 then it will say 'taken' in the ComboBox, and once the user changes the value in the ComboBox
it will save its integer value.
I am not sure how to add the converter and it gives the following error at event.getNewValue():
cant convert int to string.
Any help where I am doing wrong?
private final IntegerProperty mode;
public int getMode() {
return mode.get();
}
public void setMode(int mode) {
this.mode.set(mode);
}
public IntegerProperty modeProperty() {
return mode;
}
Set<String> modeList = new HashSet<>();
modeList.add("Free");
modeList.add("Taken");
var converter=modeConverter();
TableColumn<Review, String> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter); //How to apply the converter.
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(FXCollections.observableList(modeList))));
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue()); //Method cannot be applied java.lang.String. But I already make the conversatin via modeConverter
});
...
private StringConverter modeConverter() {
return new StringConverter<Integer>() {
#Override
public String toString(Integer object) {
if (object == 0) {
return "FREE";
} else {
return "Taken";
}
}
#Override
public Integer fromString(String string) {
if (string.equalsIgnoreCase("free")) {
return 0;
} else {
return 1;
}
}
};
}
You need to use the overloaded method:
forTableColumn​(StringConverter converter,
ObservableList items)
Please note that ComboBox backing list should contain 0, 1 instead of Free, Taken. The converter is responsible for displaying 0 as Free and 1 as Taken.
Also, the TableColumn should be of type <Review, Integer> instead of <Review, String>.
In your code, you can do something as follows:
ObservableList<Integer> modeList = FXCollections.observableList(0, 1);
TableColumn<Review, Integer> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter, modeList)));
Once your basic type is fixed, the following should work:
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue());
});
I'm using RxVertx which is a sort of RxJava along with Java8 and I have a compilation error.
Here is my code:
public rx.Observable<Game> findGame(long templateId, GameModelType game_model, GameStateType state) {
return context.findGame(templateId, state)
.flatMap(new Func1<RxMessage<byte[]>, rx.Observable<Game>>() {
#Override
public Observable<Game> call(RxMessage<byte[]> gameRawReply) {
Game game = null;
switch(game_model) {
case SINGLE: {
ebs.subscribe(new Action1<RxMessage<byte[]>>() {
#Override
public void call(RxMessage<byte[]> t1) {
if(!singleGame.contains(0) {
game = new Game(); // ERROR is at this line
singleGames.put(0, game);
} else {
game = singleGames.get(0); // ERROR is at this line
}
}
});
}
}
return rx.Observable.from(game);
}
});
}
The compilation error is:
"Local variable game defined in an enclosing scope must be final or effectively final"
I cannot define 'game' as final since I do allocation\set and return it at the end of the function.
How can I make this code compile??
Thanks.
I have a Holder class that I use for situations like this.
/**
* Make a final one of these to hold non-final things in.
*
* #param <T>
*/
public class Holder<T> {
private T held = null;
public Holder() {
}
public Holder(T it) {
held = it;
}
public void hold(T it) {
held = it;
}
public T held() {
return held;
}
public boolean isEmpty() {
return held == null;
}
#Override
public String toString() {
return String.valueOf(held);
}
}
You can then do stuff like:
final Holder<Game> theGame = new Holder<>();
...
theGame.hold(myGame);
...
{
// Access the game through the `final Holder`
theGame.held() ....
Since you need to not modify the reference of the object you can wrap the Game in something else.
The quickest (but ugly) fix is to use an array of size 1, then set the content of the array later. This works because the the array is effectively final, what is contained in the array doesn't have to be.
#Override
public Observable<Game> call(RxMessage<byte[]> gameRawReply) {
Game[] game = new Game[1];
switch(game_model) {
case SINGLE: {
ebs.subscribe(new Action1<RxMessage<byte[]>>() {
#Override
public void call(RxMessage<byte[]> t1) {
if(!singleGame.contains(0) {
game[0] = new Game();
singleGames.put(0, game[0]);
} else {
game[0] = singleGames.get(0);
}
}
});
}
}
return rx.Observable.from(game[0]);
}
Another similar option is to make a new class that has a Game field and you then set that field later.
Cyclops has Mutable, and LazyImmutable objects for handling this use case. Mutable is fully mutable, and LazyImmutable is set once.
Mutable<Game> game = Mutable.of(null);
public void call(RxMessage<byte[]> t1) {
if(!singleGame.contains(0) {
game.mutate(g -> new Game());
singleGames.put(0, game.get());
} else {
game[0] = game.mutate(g->singleGames.get(0));
}
}
LazyImmutable can be used to set a value, lazily, once :
LazyImmutable<Game> game = LazyImmutable.def();
public void call(RxMessage<byte[]> t1) {
//new Game() is only ever called once
Game g = game.computeIfAbsent(()->new Game());
}
You cant. At least not directly. U can use a wrapper class however: just define a class "GameContainer" with game as its property and foward a final reference to this container instead.
#dkatzel's suggestion is a good one, but there's another option: extract everything about retrieving/creating the Game into a helper method, and then declare final Game game = getOrCreateGame();. I think that's cleaner than the final array approach, though the final array approach will certainly work.
Although the other approaches look acceptable, I'd like to mention that you can't be sure subscribing to ebs will be synchronous and you may end up always returning null from the inner function. Since you depend on another Observable, you could just simply compose it through:
public rx.Observable<Game> findGame(
long templateId,
GameModelType game_model,
GameStateType state) {
return context.findGame(templateId, state)
.flatMap(gameRawReply -> {
switch(game_model) {
case SINGLE: {
return ebs.map(t1 -> {
Game game;
if (!singleGame.contains(0) {
game = new Game();
singleGames.put(0, game);
} else {
game = singleGames.get(0);
}
return game;
});
}
}
return rx.Observable.just(null);
});
}
A long standing issue (some call it - arguably - feature :) is the weakness of all listeners installed by all fx-bindings. As a consequence, we can't build "chains" of properties without keeping a strong reference to each link of the chain.
A particular type of such a chain link is a JavaBeanProperty: its purpose is to adapt a javabean property to a fx-property. Typically, nobody is interested in the adapter as such, so its usage would do something like
private Parent createContentBean() {
...
// local ref only
Property property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
.. wondering why the label isn't updated. Changing property to a strong reference will work as expected (leaving me puzzeld as to who is responsible to feed the dummy, but that's another question):
Property property;
private Parent createContentBean() {
...
// instantiate the field
property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
Long intro, but nearly there: jdk8 somehow changed the implementation so that the first approach is now working, there's no longer any need to keep a strong reference to a JavaBeanProperty. On the other hand, custom implementations of "chain links" still need a strong reference.
Questions:
is the change of behaviour intentional and if so, why?
how is it achieved? The code looks very similar ... and I would love to try something similar in custom adapters
A complete example to play with:
public class BeanAdapterExample extends Application {
private Counter counter;
public BeanAdapterExample() {
this.counter = new Counter();
}
Property property;
private Parent createContentBean() {
VBox content = new VBox();
Label label = new Label();
// strong ref
property = createJavaBeanProperty();
// local property
Property property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
Slider slider = new Slider();
slider.valueProperty().bindBidirectional(property);
Button button = new Button("increase");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent paramT) {
counter.increase();
}
});
content.getChildren().add(label);
content.getChildren().add(slider);
content.getChildren().add(button);
return content;
}
protected JavaBeanDoubleProperty createJavaBeanProperty(){
try {
return JavaBeanDoublePropertyBuilder.create()
.bean(counter).name("count").build();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
#Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(createContentBean());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
public static class Counter {
private double count;
public Counter() {
this(0);
}
public Counter(double count) {
this.count = count;
}
/**
* Increases the counter by 1.
*/
public void increase() {
setCount(getCount()+ 1.);
}
/**
* #return the count
*/
public double getCount() {
return count;
}
/**
* #param count the count to set
*/
public void setCount(double count) {
double old = getCount();
this.count = count;
firePropertyChange("count", old, getCount());
}
PropertyChangeSupport support = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
protected void firePropertyChange(String name, Object oldValue,
Object newValue) {
support.firePropertyChange(name, oldValue, newValue);
}
}
}
BTW: added the Swing tag because adapting core beans will be a frequent task in migration
Reminds me on an issue I've stumbled across last year - a binding does not create a strong reference so the property will be garbage collected if the property is a method local field.
Gropingly trying to answer part of my own answer:
tentative guess: it's not intentional. Looks like now the JavaBeanProperty is never garbage collected, which couldn't have been the requirement.
the only difference I could find is a phantomReference (Cleaner in the snippet) to the property, created in its constructor: that seems to keep it strong enough to never (?) be released. If I mimic that in custom properties, they "work" in a chain but are not garbage collected as well. Not an option, IMO.
The jdk8 constructor of the property:
JavaBeanDoubleProperty(PropertyDescriptor descriptor, Object bean) {
this.descriptor = descriptor;
this.listener = descriptor.new Listener<Number>(bean, this);
descriptor.addListener(listener);
Cleaner.create(this, new Runnable() {
#Override
public void run() {
JavaBeanDoubleProperty.this.descriptor.removeListener(listener);
}
});
}
The other way round: if I add such a reference to an arbitrary custom property, then it's stuck in memory just the same way as the javabeanProperty:
protected SimpleDoubleProperty createPhantomedProperty(final boolean phantomed) {
SimpleDoubleProperty adapter = new SimpleDoubleProperty(){
{
// prevents the property from being garbage collected
// must be done here in the constructor
// otherwise reclaimed immediately
if (phantomed) {
Cleaner.create(this, new Runnable() {
#Override
public void run() {
// empty, could do what here?
LOG.info("runnable in cleaner");
}
});
}
}
};
return adapter;
}
To reproduce the non-collection, add the code snippet below to my example code in the question, run in jdk7/8 and monitor with your favourite tool (used VisualVM): while running, click the "create" to create 100k of free-flying JavaBeanProperties. In jdk7, they never even show up in the memory sampler. In jdk8, they are created (sloooowly! so you might reduce the number) and build up. Forced garbage collection has no effect, even after nulling the underlying bean they are bound to.
Button create100K = new Button("create 100k properties");
create100K.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent paramT) {
Property propertyFX;
/// can't measure any effect
for (int i = 0; i < 100000; i++) {
propertyFX = createCountProperty();
}
LOG.info("created 100k adapters");
}
});
Button releaseCounter = new Button("release counter");
releaseCounter.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent paramT) {
counter = null;
}
});
Just FYI: created an issue for the potential memory leak - which is already marked as fixed, that was quick! Unfortunately, the fix-version is 8u20, not sure what to do until then. The only thingy coming to my mind is to c&p all JavaBeanXXProperty/Builders and add the fix. At the price of heavy warnings and unavailability in security-restricted environments. Also, we are back to the jdk7 behaviour (would have been too lucky, eating the cake and still have it :-)
I have a GWT application that loads a product when the page is loaded. I am using PropertyChangeEvent on the product object (and its sub-objects) to update the values of fields, whenever a change happens.
Of course, I do not want this PropertyChangeEvent to raise when the product is loaded for the first time. For this, I am setting the raisePropertyChange value to false, but it doesn't seem to work. Please find below the code base:
// Class ProductBaseImpl
public abstract class PropChangeImpl {
// The raise property change event, should be turned off conditionally
private boolean raisePropertyChangeEvent = true;
protected boolean getRaisePropertyChangeEvent() {
return this.raisePropertyChangeEvent;
}
protected void setRaisePropertyChangeEvent(final boolean value) {
this.raisePropertyChangeEvent = value;
}
protected void raisePropertyChangeEvent(String fieldName, Object oldValue, Object newValue) {
if (this.raisePropertyChangeEvent ) {
// --> HERE IS THE PROBLEM <--
// This IF loop must not be true when loading the product first time
System.out.println("Property change event raised!");
// the update operations go here
} else {
System.out.println("Property change event not raised!");
}
}
}
// Class ProductBaseImpl
public abstract class ProductBaseImpl extends PropChangeImpl {
private static HandlerRegistration productChangeBeginRegistration;
private static HandlerRegistration productChangeEndRegistration;
protected E instance;
protected ProductBaseImpl(final E instance) {
this.instance = instance;
// Stop updates when a new product loads
if (ProductBaseImpl.productChangeBeginRegistration == null) {
ProductBaseImpl.productChangeBeginRegistration = Core.getEventBus().addHandler(ProductChangeBeginEvent.TYPE, new ProductChangeBeginEventEventHandler() {
#Override
public void onProductChangeBegin(final ProductChangeBeginEvent event) {
ProductBaseImpl.this.raisePropertyChangeEvent(false);
}
});
}
if (ProductBaseImpl.productChangeEndRegistration == null) {
ProductBaseImpl.productChangeEndRegistration = Core.getEventBus().addHandler(ProductChangeEndEvent.TYPE, new ProductChangeEndEventtHandler() {
#Override
public void onProductChangeEnd(final ProductChangeEndEvent event) {
ProductBaseImpl.this.raisePropertyChangeEvent(true);
}
});
}
}
}
// Class ProductSubObj1
public class ProductSubObj1 extends ProductBaseImpl {
public ProductSubObj1 (final E instance) {
super(instance);
// some other operations
}
}
// similar to above, I have classes ProductSubObj1, ProductSubObj2 ...
// Class ProductProvider, that fetches the product from service to UI
public class ProductProvider {
// some properties and members
public void fetchProduct(String productId) {
// Let listeners know the product is about to change
Core.getEventBus().fireEvent(new ProductChangeBeginEvent(productId));
// Call the service to get the product in Json data
// After processing the data to be available for the UI (and scheduleDeferred)
Core.getEventBus().fireEvent(new ProductChangeEndEvent(productId));
}
}
As commented inline in the code, the control always goes within the
if (this.raiseDataChangeEvent)
block which I don't want to happen when the product is loaded for the first time.
Could you please advise what am I doing wrong?
Thanks.
Can you just do this:?
protected void raisePropertyChangeEvent(String fieldName, Object oldValue, Object newValue) {
if (this.raisePropertyChangeEvent && oldValue != null /*Or whatever your default unloaded value is*/) {
// --> HERE IS THE PROBLEM <--
// This IF loop must not be true when loading the product first time
System.out.println("Property change event raised!");
// the update operations go here
} else {
System.out.println("Property change event not raised!");
}
}