Java - Can't access arraylist in another class - java

I have a runnable class "TemperatureSensor" which is periodically adding a new randomized floating point value to an array list TemperatureList as an object Temperature. The last added object in the array (index 0) is then sent from RMI client to RMI server - this happens without problems.
However, when I click a button on GUI to display the size of this object array, I always get a 0. If I print out the size of the array from RMI client class, it shows a correct size.
My question is, how do I access the same array from multiple classes correctly?
Here is the UML:
TemperatureSensor:
import java.text.DecimalFormat;
import java.util.Random;
public class TemperatureSensor implements Runnable
{
private int waitingTime;
private Model model;
public TemperatureSensor(Model model, int waitingTime)
{
this.model = model;
this.waitingTime = waitingTime;
}
#Override
public void run()
{
float temperature = 25.0f;
while(true)
{
temperature = measureTemperature(temperature);
model.addTemperatureData(temperature);
System.out.println("Sending: " + temperature);
waiting();
}
}
private float measureTemperature(float temperature)
{
Random rand = new Random();
float minTempFloat = 0.1f;
float maxTempFloat = 0.2f;
int incrementSwitch = rand.nextInt(3-0) + 0;
if (incrementSwitch == 0)
{
temperature += minTempFloat + rand.nextFloat() * (maxTempFloat - minTempFloat);
}
else if(incrementSwitch == 1)
{
//Do nothing
}
else if (incrementSwitch == 2)
{
temperature -= minTempFloat + rand.nextFloat() * (maxTempFloat -
minTempFloat);
}
return temperature;
}
private void waiting()
{
try
{
Thread.sleep(waitingTime);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
Model:
public interface Model
{
public void addTemperatureData(float value);
public Temperature getLatestTemperatureData();
public int getTempListSize();
}
ModelManager:
public class ModelManager implements Model
{
private TemperatureList temperatureList;
public ModelManager()
{
temperatureList = new TemperatureList();
}
#Override
public void addTemperatureData(float value)
{
Temperature temperature = new Temperature(value);
//this.temperatureList.clearTemperatureDataList();
this.temperatureList.addTemperatureDataToList(temperature);
}
#Override
public Temperature getLatestTemperatureData()
{
return temperatureList.getLatestTemperatureDataFromList();
}
#Override
public int getTempListSize()
{
return temperatureList.size();
}
}
RMIsensorClient:
import java.rmi.Naming;
import java.rmi.RemoteException;
public class RMIsensorClient
{
private RMIserverInterface serverInterface;
private static Model model = new ModelManager();
public static void main(String[] args) throws RemoteException, InterruptedException
{
TemperatureSensor tempSensor = new TemperatureSensor(model, 5000);
Thread tempThread = new Thread(tempSensor, "TempSensor");
tempThread.start();
RMIsensorClient sensorClient = new RMIsensorClient();
}
public RMIsensorClient() throws RemoteException
{
super();
try
{
serverInterface = (RMIserverInterface) Naming.lookup("rmi://localhost:1099/rmiServer");
while(true)
{
serverInterface.getTemperature(model.getLatestTemperatureData());
System.out.println(model.getTempListSize());
Thread.sleep(5000);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Controler:
public class Controller
{
private static Model model;
public Controller ()
{
this.model = new ModelManager();
}
public int getNumberOfListElements()
{
return model.getTempListSize();
}
}
GUI:
public class GUItemperatureController implements Initializable
{
private Controller controller = new Controller();
#FXML
private Label tlTemperature;
#FXML
private Pane mainPane;
#FXML
private TextField tfTemperature;
#FXML
private Button btnUpdate;
#Override
public void initialize(URL arg0, ResourceBundle arg1)
{
tfTemperature.setEditable(false);
}
#FXML
void showArraySize(ActionEvent event)
{
tfTemperature.setText(Integer.toString(controller.getNumberOfListElements()));
}
}
TemperatureList:
import java.io.Serializable;
import java.util.ArrayList;
public class TemperatureList implements Serializable
{
private ArrayList<Temperature> temperatureList;
public TemperatureList()
{
this.temperatureList = new ArrayList<>();
}
public void addTemperatureDataToList(Temperature temperature)
{
temperatureList.add(0,temperature);
}
public Temperature getLatestTemperatureDataFromList()
{
return this.temperatureList.get(0);
}
public void clearTemperatureDataList()
{
temperatureList.clear();
}
public int size()
{
return temperatureList.size();
}
}
Here is where I launch the GUI:
import java.rmi.RemoteException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class userMain extends Application
{
public FXMLLoader loader;
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception
{
loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML/FXMLtemperature.fxml"));
loader.setController(new GUItemperatureController());
Parent root = loader.load();
Scene scene = new Scene(root);
primaryStage.setTitle("GEMS - Test");
primaryStage.setScene(scene);
primaryStage.show();
}
}

Your problem is not about classes.
You run two separate applications. One runs your RMIsensorClient and one runs your GUI. They know nothing about each other, your RMIsensorClient and your Controller have their own separate instances of ModelManager and you have no code anywhere that would share any data between them.
You need to make the data you want to show in your GUI accessible somehow.
One solution could be to use a network interface for that. Create two different ModelManagers, one that opens and listens to a ServerSocket, and one that uses a Socket in getLatestTemperatureData() to connect to the other one.
Use the former in your RMIsensorClient and the latter in your GUI's Controller.
Research networking in Java.
This is a very crude solution, but there are plenty of great tutorials for networking and sharing data between multiple Java applications.

You haven't included your TemperatureList implementation, but if as you say it's an ArrayList it's likely you're not properly synchronizing access across threads. It's imperative that you properly synchronize cross-thread work otherwise you'll wind up with some sort of undefined behavior, such as changes not propagating or data structures winding up broken.
There are a number of thread-safe collections in java.util.collect.concurrent that you might consider, otherwise you'll need to ensure you use synchronized blocks or methods anywhere you're working with this list.

The most obvious problem that I found(and this might not be all) is that your array list is method specific. it is not static. Meaning that it can only be accessed by the method it originates in. the easiest fix for this is to add a static modifier to your array list when it is created and create it outside of the methods.

Related

JavaFX Optimized Asynchronous Lazy Loading of TreeItems on TreeView

I have an app where i have a TreeView which will have TreeItems holding a large number of leaf TreeItems. Having a huge number of TreeItems in the treeview hurts the performance of the app noticeably, to avoid that, what i will do, is i will allow only one non-leaf TreeItem to be expanded at a time, and once a TreeItem is folded, i will clear it's children, and load them asynchronously once needed (When the user expands the TreeItem).
The weird issue is, in this test below, when i first click the expand arrow on the treeitem, the children load fine, and if i fold it (which will clear children) and unfold it again, sometimes it works and others the program hogs and starts consuming 30% of the cpu for a couple of minutes then gets back running. What's weirder is that if i double click on the TreeItem to expand it (Not using the arrow) the hog starts right away, even at first program launch.
What could i be possibly doing wrong here?
PS:
Some of the code in the LazyTreeItem class is inspired by James_D's answer Here
I tried running the loadItems task on the fx thread(Not using the ItemLoader), but it didn't make any difference.
Same issue occurs using both JAVA 8 and JAVA 9
App.java
public class App extends Application {
private TreeView<Item> treeView = new TreeView<>();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("TreeView Lazy Load");
primaryStage.setScene(new Scene(new StackPane(treeView), 300, 275));
initTreeView();
primaryStage.show();
}
private void initTreeView() {
treeView.setShowRoot(false);
treeView.setRoot(new TreeItem<>(null));
List<SingleItem> items = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
items.add(new SingleItem(String.valueOf(i)));
}
TreeItem<Item> parentItem = new TreeItem<>(new Item());
parentItem.getChildren().add(new LazyTreeItem(new MultipleItem(items)));
treeView.getRoot().getChildren().add(parentItem);
}
public static void main(String[] args) {
launch(args);
}
}
LazyTreeItem.java
public class LazyTreeItem extends TreeItem<Item> {
private boolean childrenLoaded = false;
private boolean isLoadingItems = false;
public LazyTreeItem(Item value) {
super(value);
// Unload data on folding to reduce memory
expandedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
flush();
}
});
}
#Override
public ObservableList<TreeItem<Item>> getChildren() {
if (childrenLoaded || !isExpanded()) {
return super.getChildren();
}
if (super.getChildren().size() == 0) {
// Filler node (will translate into loading icon in the
// TreeCell factory)
super.getChildren().add(new TreeItem<>(null));
}
if (getValue() instanceof MultipleItem) {
if (!isLoadingItems) {
loadItems();
}
}
return super.getChildren();
}
public void loadItems() {
Task<List<TreeItem<Item>>> task = new Task<List<TreeItem<Item>>>() {
#Override
protected List<TreeItem<Item>> call() {
isLoadingItems = true;
List<SingleItem> downloadSet = ((MultipleItem) LazyTreeItem.this.getValue()).getEntries();
List<TreeItem<Item>> treeNodes = new ArrayList<>();
for (SingleItem download : downloadSet) {
treeNodes.add(new TreeItem<>(download));
}
return treeNodes;
}
};
task.setOnSucceeded(e -> {
Platform.runLater(() -> {
super.getChildren().clear();
super.getChildren().addAll(task.getValue());
childrenLoaded = true;
isLoadingItems = false;
});
});
ItemLoader.getSingleton().load(task);
}
private void flush() {
childrenLoaded = false;
super.getChildren().clear();
}
#Override
public boolean isLeaf() {
if (childrenLoaded) {
return getChildren().isEmpty();
}
return false;
}
}
ItemLoader.java
public class ItemLoader implements Runnable {
private static ItemLoader instance;
private List<Task> queue = new ArrayList<>();
private Task prevTask = null;
private ItemLoader() {
Thread runner = new Thread(this);
runner.setName("ItemLoader thread");
runner.setDaemon(true);
runner.start();
}
public static ItemLoader getSingleton() {
if (instance == null) {
instance = new ItemLoader();
}
return instance;
}
public <T> void load(Task task) {
if (queue.size() < 1) {
queue.add(task);
}
}
#Override
public void run() {
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!queue.isEmpty()) {
Task task = queue.get(0);
if (task != prevTask) {
prevTask = task;
task.run();
queue.remove(task);
}
}
}
}
}
Model (Item.java, SingleItem.java, MultipleItem.java)
public class Item {
}
/****************************************************************
********** SingleItem ************
****************************************************************/
public class SingleItem extends Item {
private String id;
public SingleItem(String id) {
this.id = id;
}
public void setId(String id) {
this.id = id;
}
}
/****************************************************************
********** MultipleItem ************
****************************************************************/
public class MultipleItem extends Item {
private List<SingleItem> entries = new ArrayList<>();
public MultipleItem(List<SingleItem> entries) {
this.entries = entries;
}
public List<SingleItem> getEntries() {
return entries;
}
public void setEntries(List<SingleItem> entries) {
this.entries = entries;
}
}
The issue, as pointed out by #kleopatra, is caused by adding a large amount of children when there are one or more items selected. One way to fix this is to try and implement your own FocusModel, as the default FocusModel seems to be the source of the problem. Another, and in my opinion easier, way to create a workaround is to clear the selection before adding the large group of children; afterwards, you can re-select the items that were previously selected.
The way I went about doing this is by firing TreeModificationEvents with custom EventTypes. Also, I decided not to override isLeaf() inside my lazy TreeItem. I find it easier to use a placeholder TreeItem for when the parent TreeItem is a lazy branch. Since there is a placeholder the parent will register as a branch automatically.
Here's an example that browses the default FileSystem. To test if the solution worked I created a 100,000 file directory and opened it; there was no hang for me. Hopefully that means this can be adapted to your code.
Note: This example does remove the children when the branch is collapsed, just like you are doing in your code.
App.java
import java.nio.file.FileSystems;
import java.nio.file.Path;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.stage.Stage;
public class App extends Application {
private static String pathToString(Path p) {
if (p == null) {
return "null";
} else if (p.getFileName() == null) {
return p.toString();
}
return p.getFileName().toString();
}
#Override
public void start(Stage primaryStage) {
TreeView<Path> tree = new TreeView<>(new TreeItem<>());
tree.setShowRoot(false);
tree.setCellFactory(LazyTreeCell.forTreeView("Loading...", App::pathToString));
TreeViewUtils.installSelectionBugWorkaround(tree);
for (Path fsRoot : FileSystems.getDefault().getRootDirectories()) {
tree.getRoot().getChildren().add(new LoadingTreeItem<>(fsRoot, new DirectoryLoader(fsRoot)));
}
primaryStage.setScene(new Scene(tree, 800, 600));
primaryStage.show();
}
}
DirectoryLoader.java
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import javafx.scene.control.TreeItem;
public class DirectoryLoader implements Callable<List<? extends TreeItem<Path>>> {
private static final Comparator<Path> COMPARATOR = (left, right) -> {
boolean leftIsDir = Files.isDirectory(left);
if (leftIsDir ^ Files.isDirectory(right)) {
return leftIsDir ? -1 : 1;
}
return left.compareTo(right);
};
private final Path directory;
public DirectoryLoader(Path directory) {
this.directory = directory;
}
#Override
public List<? extends TreeItem<Path>> call() throws Exception {
try (Stream<Path> stream = Files.list(directory)) {
return stream.sorted(COMPARATOR)
.map(this::toTreeItem)
.collect(Collectors.toList());
}
}
private TreeItem<Path> toTreeItem(Path path) {
return Files.isDirectory(path)
? new LoadingTreeItem<>(path, new DirectoryLoader(path))
: new TreeItem<>(path);
}
}
LoadingTreeItem.java
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Supplier;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventType;
import javafx.scene.control.TreeItem;
public class LoadingTreeItem<T> extends TreeItem<T> {
private static final EventType<?> PRE_ADD_LOADED_CHILDREN
= new EventType<>(treeNotificationEvent(), "PRE_ADD_LOADED_CHILDREN");
private static final EventType<?> POST_ADD_LOADED_CHILDREN
= new EventType<>(treeNotificationEvent(), "POST_ADD_LOADED_CHILDREN");
#SuppressWarnings("unchecked")
static <T> EventType<TreeModificationEvent<T>> preAddLoadedChildrenEvent() {
return (EventType<TreeModificationEvent<T>>) PRE_ADD_LOADED_CHILDREN;
}
#SuppressWarnings("unchecked")
static <T> EventType<TreeModificationEvent<T>> postAddLoadedChildrenEvent() {
return (EventType<TreeModificationEvent<T>>) POST_ADD_LOADED_CHILDREN;
}
private final Callable<List<? extends TreeItem<T>>> callable;
private boolean needToLoadData = true;
private CompletableFuture<?> future;
public LoadingTreeItem(T value, Callable<List<? extends TreeItem<T>>> callable) {
super(value);
this.callable = callable;
super.getChildren().add(new TreeItem<>());
addExpandedListener();
}
#SuppressWarnings("unchecked")
private void addExpandedListener() {
expandedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
needToLoadData = true;
if (future != null) {
future.cancel(true);
}
super.getChildren().setAll(new TreeItem<>());
}
});
}
#Override
public ObservableList<TreeItem<T>> getChildren() {
if (needToLoadData) {
needToLoadData = false;
future = CompletableFuture.supplyAsync(new CallableToSupplierAdapter<>(callable))
.whenCompleteAsync(this::handleAsyncLoadComplete, Platform::runLater);
}
return super.getChildren();
}
private void handleAsyncLoadComplete(List<? extends TreeItem<T>> result, Throwable th) {
if (th != null) {
Thread.currentThread().getUncaughtExceptionHandler()
.uncaughtException(Thread.currentThread(), th);
} else {
Event.fireEvent(this, new TreeModificationEvent<>(preAddLoadedChildrenEvent(), this));
super.getChildren().setAll(result);
Event.fireEvent(this, new TreeModificationEvent<>(postAddLoadedChildrenEvent(), this));
}
future = null;
}
private static class CallableToSupplierAdapter<T> implements Supplier<T> {
private final Callable<T> callable;
private CallableToSupplierAdapter(Callable<T> callable) {
this.callable = callable;
}
#Override
public T get() {
try {
return callable.call();
} catch (Exception ex) {
throw new CompletionException(ex);
}
}
}
}
LazyTreeCell.java
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.util.Callback;
public class LazyTreeCell<T> extends TreeCell<T> {
public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView(String placeholderText,
Callback<? super T, String> toStringCallback) {
return tree -> new LazyTreeCell<>(placeholderText, toStringCallback);
}
private final String placeholderText;
private final Callback<? super T, String> toStringCallback;
public LazyTreeCell(String placeholderText, Callback<? super T, String> toStringCallback) {
this.placeholderText = placeholderText;
this.toStringCallback = toStringCallback;
}
/*
* Assumes that if "item" is null **and** the parent TreeItem is an instance of
* LoadingTreeItem that this is a "placeholder" cell.
*/
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if (item == null && getTreeItem().getParent() instanceof LoadingTreeItem) {
setText(placeholderText);
} else {
setText(toStringCallback.call(item));
}
}
}
TreeViewUtils.java
import java.util.ArrayList;
import java.util.List;
import javafx.beans.value.ChangeListener;
import javafx.event.EventHandler;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
public class TreeViewUtils {
public static <T> void installSelectionBugWorkaround(TreeView<T> tree) {
List<TreeItem<T>> selected = new ArrayList<>(0);
EventHandler<TreeModificationEvent<T>> preAdd = event -> {
event.consume();
selected.addAll(tree.getSelectionModel().getSelectedItems());
tree.getSelectionModel().clearSelection();
};
EventHandler<TreeModificationEvent<T>> postAdd = event -> {
event.consume();
selected.forEach(tree.getSelectionModel()::select);
selected.clear();
};
ChangeListener<TreeItem<T>> rootListener = (observable, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.removeEventHandler(LoadingTreeItem.preAddLoadedChildrenEvent(), preAdd);
oldValue.removeEventHandler(LoadingTreeItem.postAddLoadedChildrenEvent(), postAdd);
}
if (newValue != null) {
newValue.addEventHandler(LoadingTreeItem.preAddLoadedChildrenEvent(), preAdd);
newValue.addEventHandler(LoadingTreeItem.postAddLoadedChildrenEvent(), postAdd);
}
};
rootListener.changed(tree.rootProperty(), null, tree.getRoot());
tree.rootProperty().addListener(rootListener);
}
private TreeViewUtils() {}
}
As implemented, the utility method that installs the workaround is tied to you using LoadingTreeItems in the TreeView. I couldn't think of a good way to make the solution general enough to apply to any arbitrary TreeView; for that, I believe creating a custom FocusModel would be necessary.
There is probably a better way to implement the LazyTreeCell by using a class to wrap the real data—like what you're doing with Item. Then you could have an actual placehoder Item instance that tells the TreeCell that it is a placeholder rather than relying on what type the parent TreeItem is. As it is, my implementation is likely brittle.

Know when a different class is exited

I'm currently coding a project Java in eclipse which has two classes. The first class (open) I use to send a specific string to my second class (viewer) and then run my second class. The second class (viewer) I have imported into my program in the form of a jar file. I have done it this way as class viewer is a pdf viewer that i created using apache PDFBox and class open sends the file to the viewer to use, but the file will be different depending on many conditions (that are not relevant) in class open. The point is that class open needs to be separate from class viewer and can not simply be two different methods in one class. I would like to know if there is a way for class open to know when class viewer has been closed, as currently I am using a while loop, which just eats up memory and is very inefficient. The code I have does currently work, but I feel there is a better way, perhaps using listeners. This is the code for closing class viewer:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
//and import swing components ect
Public class viewer extends javax.swing.JFrame
implements KeyListener,
ActionListener{
private javax.swing.JButton zoomIn;
private javax.swing.JButton zoomOut;
//and a bunch more swing components
public static boolean closed = false;
public static String fileName = "";
public viewer()
{
}
private void initComponents() throws IOException
{
addWindowListener(new java.awt.event.WindowAdapter()
{
#Override
public void windowClosing(java.awt.event.WindowEvent evt)
{
exitApplication();
}
});
}
private void exitMenuItemActionPerformed(ActionEvent evt)
{
if( document != null )
{
try
{
document.close();
}
catch( IOException e )
{
throw new RuntimeException(e);
}
}
closed = true;
System.exit(0);
}
public static void main(String filename) throws Exception
{
fileName = filename;
viewer mainViewer = new viewer();
String[] splittedStr = fileName.split("/");
BASETITLE = splittedStr[splittedStr.length - 1];
if (fileName != null)
{
mainViewer.openPDFFile(fileName);
}
mainViewer.setVisible(true);
}
This is my code from class open:
public static void main(String[] args) throws Exception {
String fileName = "C:/Files/Test.pdf";
viewer.main(fileName);
while(viewer.closed == false)
{
if(viewer.closed == true)
{
System.out.print("The Viewer Has Been Closed");
}
}
}
I want to know when it is closed so I can delete the file on the local drive. Thanks for your help!
Either you pass a callback (e.g. a Runnable) as argument to viewer.main, like viewer.main(fileName, () -> System.out.print("The Viewer Has Been Closed")) and make sure that it is called when the process is done, or you do as you've done except that you sleep your main thread a short time in the while loop, like Thread.sleep(100).
okay so I actually found an answer to my question and I'll just post it in case someone else had the same problem as me. I added a interface to the open class with the method close as shown:
import mainpackage.viewer;
public class Open implements closeInterface{
public Open() { }
public static String fileName;
public static void main(String[] args) throws Exception {
fileName = "C:/Files/Test.pdf";
run();
}
#Override
public void close() {
System.out.print("The Viewer Has Been Closed");
}
public static void run() throws Exception
{
viewer view = new viewer();
view.main(fileName);
view.addListener(new Open());
}
}
This was my code for my interface:
package mainpackage;
public interface closeInterface {
public void close();
}
And this was the snipit of code for my Viewer class
public class viewer extends javax.swing.JFrame {
private static closeInterface Closed;
private void initComponents() throws IOException
{
addWindowListener(new java.awt.event.WindowAdapter()
{
#Override
public void windowClosing(java.awt.event.WindowEvent evt)
{
exitApplication();
}
});
}
private void exitApplication()
{
try
{
if( document != null )
{
document.close();
}
}
catch( IOException io )
{
//do nothing because we are closing the application
}
Closed.close();
this.setVisible( false );
this.dispose();
}
public void addListener(closeInterface closed){
Closed = closed;
}
}
Thanks for everyone's help!

Custom Event in GWT widget is not working

I am working on application in Vaadin for my classes.
I have to draw some map on the screen so I'm using gwt-graphics lib.
I have also some servlet which is waiting for requests.
When some specific request will come view of the map should be changed.
It lead me to prepare custom event:
// class NewModulePositionHandler
package com.example.locator;
import com.google.gwt.event.shared.EventHandler;
public interface NewModulePositionHandler extends EventHandler {
void onNewModulePosition(NewModulePositionEvent event);
}
Below implementation of my custom event:
import com.google.gwt.event.shared.GwtEvent;
public class NewModulePositionEvent extends GwtEvent<NewModulePositionHandler> {
private static final Type<NewModulePositionHandler> TYPE = new Type<NewModulePositionHandler>();
private final String m_Color;
public NewModulePositionEvent(String color) {
m_Color = color;
}
public static Type<NewModulePositionHandler> getType() {
return TYPE;
}
public String getColor() {
return m_Color;
}
#Override
public com.google.gwt.event.shared.GwtEvent.Type<NewModulePositionHandler> getAssociatedType() {
// TODO Auto-generated method stub
return TYPE;
}
#Override
protected void dispatch(NewModulePositionHandler handler) {
handler.onNewModulePosition(this);
}
}
And it's time for implementation of my custom widget:
a) MyComp.java
import com.google.gwt.event.shared.GwtEvent;
public class NewModulePositionEvent extends GwtEvent<NewModulePositionHandler> {
private static final Type<NewModulePositionHandler> TYPE = new Type<NewModulePositionHandler>();
private final String m_Color;
public NewModulePositionEvent(String color) {
m_Color = color;
}
public static Type<NewModulePositionHandler> getType() {
return TYPE;
}
public String getColor() {
return m_Color;
}
#Override
public com.google.gwt.event.shared.GwtEvent.Type<NewModulePositionHandler> getAssociatedType() {
// TODO Auto-generated method stub
return TYPE;
}
#Override
protected void dispatch(NewModulePositionHandler handler) {
handler.onNewModulePosition(this);
}
}
b) MyCompClientRpc.java
import com.vaadin.shared.communication.ClientRpc;
public interface MyCompClientRpc extends ClientRpc {
// TODO example API
public void alert(String message);
public void changeColor(String color);
}
c) MyCompConnector.java
package com.example.locator.widgetset.client.mycomp;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.shared.ui.Connect;
import com.example.locator.MyComp;
import com.example.locator.NewModulePositionEvent;
import com.example.locator.NewModulePositionHandler;
import com.example.locator.widgetset.client.mycomp.MyCompWidget;
import com.example.locator.widgetset.client.mycomp.MyCompServerRpc;
import com.vaadin.client.communication.RpcProxy;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.client.MouseEventDetailsBuilder;
import com.example.locator.widgetset.client.mycomp.MyCompClientRpc;
import com.example.locator.widgetset.client.mycomp.MyCompState;
import com.vaadin.client.communication.StateChangeEvent;
#Connect(MyComp.class)
public class MyCompConnector extends AbstractComponentConnector {
MyCompServerRpc rpc = RpcProxy
.create(MyCompServerRpc.class, this);
public MyCompConnector() {
registerRpc(MyCompClientRpc.class, new MyCompClientRpc() {
public void alert(String message) {
// TODO Do something useful
Window.alert(message);
}
public void changeColor(String color) {
getWidget().InitMap(color);
}
});
// TODO ServerRpc usage example, do something useful instead
getWidget().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
final MouseEventDetails mouseDetails = MouseEventDetailsBuilder
.buildMouseEventDetails(event.getNativeEvent(),
getWidget().getElement());
rpc.clicked(mouseDetails);
}
});
getWidget().addNewModulePositionHandler(new NewModulePositionHandler() {
public void onNewModulePosition(NewModulePositionEvent event) {
// TODO Auto-generated method stub
rpc.newModulePosition(event.getColor());
}
});
}
#Override
protected Widget createWidget() {
return GWT.create(MyCompWidget.class);
}
#Override
public MyCompWidget getWidget() {
return (MyCompWidget) super.getWidget();
}
#Override
public MyCompState getState() {
return (MyCompState) super.getState();
}
#Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
// TODO do something useful
final String color = getState().color;
getWidget().InitMap(color);
}
}
d) MyCompServerRpc.java
package com.example.locator.widgetset.client.mycomp;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.communication.ServerRpc;
public interface MyCompServerRpc extends ServerRpc {
// TODO example API
public void clicked(MouseEventDetails mouseDetails);
public void newModulePosition(String color);
}
e) MyCompState.java
package com.example.locator.widgetset.client.mycomp;
public class MyCompState extends com.vaadin.shared.AbstractComponentState {
// TODO example state
public String color = "#000000";
}
And finally implementation of the widget:
f) MyCompWidget.java
package com.example.locator.widgetset.client.mycomp;
import org.vaadin.gwtgraphics.client.DrawingArea;
import org.vaadin.gwtgraphics.client.Line;
import org.vaadin.gwtgraphics.client.shape.Circle;
import org.vaadin.gwtgraphics.client.shape.Rectangle;
import com.example.locator.HasNewModulePositionHandlers;
import com.example.locator.NewModulePositionEvent;
import com.example.locator.NewModulePositionHandler;
import com.google.gwt.dev.util.collect.HashMap;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.touch.client.Point;
import com.example.locator.Module;
// TODO extend any GWT Widget
public class MyCompWidget extends DrawingArea implements HasNewModulePositionHandlers {
public static final String CLASSNAME = "mycomp";
public static double m_AreaWidth = 64.355;
public static double m_AreaHeight = 17.385;
public static int m_PictureWidth;
public static int m_PictureHeight;
public static double m_AreaToMapRatio;
public static double m_RouteWidth = 3.5;
public static double m_MainRouteCoordinateY = 8.0828;
public Circle circle;
//public HashMap<Integer, Module> ModuleMap = new HashMap<Integer, Module>();
public MyCompWidget(){
super(640, 320);
//ModuleMap.put(666, new Module(666, 30.0, 8.08, new Circle((int)TranslateCoordinate(30.0), (int)TranslateCoordinate(8.0), 7)));
//ModuleMap.put(15, new Module(15, 27.0, 8.08, new Circle((int)TranslateCoordinate(30.0), (int)TranslateCoordinate(8.0), 7)));
double xRatio = m_AreaWidth / 640;
double yRatio = m_AreaHeight / 320;
m_AreaToMapRatio = xRatio > yRatio ? xRatio : yRatio;
InitMap("#919491");
setStyleName(CLASSNAME);
}
public void InitMap(String color)
{
m_PictureWidth = (int)TranslateCoordinate(m_AreaWidth);
m_PictureHeight = (int)TranslateCoordinate(m_AreaHeight);
Rectangle rectangle = new Rectangle(0, 0, m_PictureWidth, m_PictureHeight);
rectangle.setFillColor(color);
add(rectangle);
Point point1Beg = new Point(0.0, 8.0828);
Point point1End = new Point(64.355, 8.0838);
Point point2Beg = new Point(20.2825, 8.0828);
Point point2End = new Point(20.2825, 17.385);
Point point3Beg = new Point(59.325, 0.0);
Point point3End = new Point(59.325, 8.0828);
point1Beg = TranslatePoint(point1Beg);
point1End = TranslatePoint(point1End);
point2Beg = TranslatePoint(point2Beg);
point2End = TranslatePoint(point2End);
point3Beg = TranslatePoint(point3Beg);
point3End = TranslatePoint(point3End);
Line line1 = new Line((int)point1Beg.getX(), (int)point1Beg.getY(), (int)point1End.getX(), (int)point1End.getY());
Line line2 = new Line((int)point2Beg.getX(), (int)point2Beg.getY(), (int)point2End.getX(), (int)point2End.getY());
Line line3 = new Line((int)point3Beg.getX(), (int)point3Beg.getY(), (int)point3End.getX(), (int)point3End.getY());
line1.setStrokeColor("#FFFFFF");
line2.setStrokeColor("#FFFFFF");
line3.setStrokeColor("#FFFFFF");
line1.setStrokeWidth((int)TranslateCoordinate(m_RouteWidth));
line2.setStrokeWidth((int)TranslateCoordinate(m_RouteWidth));
line3.setStrokeWidth((int)TranslateCoordinate(m_RouteWidth));
add(line1);
add(line2);
add(line3);
DrawWall(TranslateCoordinate(10.0));
DrawWall(TranslateCoordinate(20.0));
DrawWall(TranslateCoordinate(30.0));
DrawWall(TranslateCoordinate(40.0));
DrawWall(TranslateCoordinate(50.0));
DrawWall(TranslateCoordinate(60.0));
DrawDoor(3.0, 3.0);
DrawDoor(13.0, 3.0);
DrawDoor(23.0, 3.0);
DrawDoor(33.0, 3.0);
DrawDoor(43.0, 3.0);
DrawDoor(53.0, 3.0);
circle = new Circle((int)TranslateCoordinate(25.0), (int)TranslateCoordinate(8.0), 15);
add(circle);
}
public void DrawWall(double a_Place)
{
Line line = new Line((int)a_Place, 0, (int)a_Place, (int)TranslateCoordinate(m_AreaHeight));
line.setStrokeColor("#FFFFFF");
add(line);
}
public void DrawDoor(double a_Position, double a_Width)
{
double realDoorPositionY = m_MainRouteCoordinateY - (m_RouteWidth / 2);
int doorPositionYTop = (int)TranslateCoordinate(realDoorPositionY) - 1;
int doorPositionYBottom = (int)TranslateCoordinate(realDoorPositionY + m_RouteWidth) + 1;
Line line = new Line((int)TranslateCoordinate(a_Position), doorPositionYTop, (int)TranslateCoordinate(a_Position) + (int)TranslateCoordinate(a_Width), doorPositionYTop);
line.setStrokeColor("#000000");
line.setStrokeWidth(2);
add(line);
Line line2 = new Line((int)TranslateCoordinate(a_Position), doorPositionYBottom, (int)TranslateCoordinate(a_Position) + (int)TranslateCoordinate(a_Width), doorPositionYBottom);
line2.setStrokeColor("#000000");
line2.setStrokeWidth(2);
add(line2);
}
public Point TranslatePoint(Point a_Point)
{
Point translatedPoint = new Point(TranslateCoordinate(a_Point.getX()), TranslateCoordinate(a_Point.getY()));
return translatedPoint;
}
public double TranslateCoordinate(double a_Coordinate)
{
return (a_Coordinate) / (m_AreaToMapRatio);
}
public void Move(int id) {
//ModuleMap.get(id).GetCircle().setX(10 + circle.getX());
}
public HandlerRegistration addNewModulePositionHandler(
NewModulePositionHandler handler) {
return addHandler(handler, NewModulePositionEvent.TYPE);
}
private void someMethod() {
fireEvent(new NewModulePositionEvent("#000000"));
}
public void emulateEvent() {
someMethod();
}
}
g) HasNewModulesPositionHandlers.java
package com.example.locator;
import com.google.gwt.event.shared.HandlerRegistration;
public interface HasNewModulePositionHandlers {
// Attention! method returns HandlerRegistration, so that handler can be cancelled
public HandlerRegistration addNewModulePositionHandler(
NewModulePositionHandler handler);
}
If I compile the widgets set containing MyCompWidget and then run my application on glassfish I get the following message:
Widgetset 'com.example.locator.widgetset.LocatorWidgetset' does not contain implementation for com.example.locator.MyComp. Check its component connector's #Connect mapping, widgetsets GWT module description file and re-compile your widgetset. In case you have downloaded a vaadin add-on package, you might want to refer to add-on instructions.
If I cut
public void addNewModulePositionHandler(
NewModulePositionHandler handler) {
handlerManager.addHandler(NewModulePositionEvent.getType(), handler);
// TODO Auto-generated method stub
}
widget works properly (of course I have to comment out these lines from MyCompConnector as well):
getWidget().addNewModulePositionHandler(new NewModulePositionHandler() {
public void onNewModulePosition(NewModulePositionEvent event) {
// TODO Auto-generated method stub
rpc.newModulePosition(event.getColor());
}
});
Can anyone tell me where is the problem? It seems that compilation of the widget failes but I can't find any information about that.
Please, help me.
Thanks in advance.
Compilation problem is very clear: GWT compiler cannot fins source code for a specified java class. You add source files to GWT compiler scope in two steps:
*.java files must be accessible through classpath (eclipse gwt compiler automatically includes all source folders to classpath)
You need to tell GWT that it should consider your packages during compilation
Create .gwt.xml file in PARENT package (relative to your source files) with the following content (if your package is pkg1.pkg2.pkg3, then you should in package pkg1.pkg2 create file "Pkg3.gwt.xml", pkg = "pkg3", by convention pkg3 is typically named "client")
<module>
<source path="pkg3" />
</module>
!!!Be careful with letter cases, By convention those are the correct names.
Add to your widgetset.gwt.xml file "inherits" directive like that
<inherits name="pkg1.pkg2.Pkg3" />
!!! Pay attention to letter cases (P is capital in Pkg3, after file name Pkg3.gwt.xml), .gwt.xml is omitted here
Then, first of all, Vaadin uses GWT with a certain twist. I would recommend browsing through custom widgetset samples. You may need to understand how to propagate events to and from Vaadin-specific components. Below I explain how GWT event are supposed to be implemented and used. I'm not an expert in Vaadin, especially in advanced topics such as customizing widgetsets.
So, speaking of GWT.
Basically, you need to understand only two things to make (non-DOM, also knows as bitless) events work in GWT.
Code support
Event class. NewModulePositionEvent in your case.
Event Handler interface. NewModulePositionHandler in your case.
Feature interface (optional but advised). HasNewModulePositionHandlers in your case.
Usage pattern
Basically, the component that is supposed to fire event should create relevant Event object and pass it to the fireEvent method. All the logic to invoke necessary habdler is provided by EventBus (internally)
If you need to provide API to fire events externally (such as click() method for a Button), it should be done by providing special methods (do not expose internal stuff how exactly event is fired)
DOM events are special in details how they are created (by browser) and dispatched (they need to be explicitly enabled). Anyway, all the browser events are already implemented in GWT out of the box.
So, typical implementations for the above items: For the sake of clarity, I provide excerpts from our production code.
1) Event class
public class QuestionClickEvent extends GwtEvent<QuestionClickHandler> {
public QuestionClickEvent(SScript script, SQuestion question) {
super();
this.script = script;
this.question = question;
}
public static final Type<QuestionClickHandler> TYPE = new Type<QuestionClickHandler>();
// internal event state
private final SScript script;
private final SQuestion question;
#Override
public Type<QuestionClickHandler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(QuestionClickHandler handler) {
handler.onQuestionClicked(this);
}
// provide access to internal event state
public SScript getScript() {
return script;
}
public SQuestion getQuestion() {
return question;
}
}
2) Handler interface
public interface QuestionClickHandler extends EventHandler {
public void onQuestionClicked(QuestionClickEvent event);
}
3) Feature interface
public interface HasQuestionClickHandlers {
// Attention! method returns HandlerRegistration, so that handler can be cancelled
public HandlerRegistration addQuestionClickHandler(
QuestionClickHandler handler);
}
4) Your widget/component
public class SummaryPanel extends Widget implements HasQuestionClickHandlers {
// blah-blah-blah
// implement your handler registration method
#Override
public HandlerRegistration addQuestionClickHandler(
QuestionClickHandler handler) {
return addHandler(handler, QuestionClickEvent.TYPE);
}
// blah-blah-blah
private someMethod() {
// suddenly you realized that you need to to fire your event
fireEvent(new QuestionClickEvent(script, question));
}
// sample external API method
public void emulateEvent() {
someMethod();
}
}
And finally usage example:
SummaryPanel summary = new SummaryPanel();
summary.addQuestionClickHandler(new QuestionClickHandler() {
#Override
public void onQuestionClicked(QuestionClickEvent event) {
// your reaction goes here
}
});

Java Event Listener to detect a variable change

I cannot seem to find an answer anywhere to my question. Is there any event listener which can detect the changing of a boolean or other variable and then act on it. Or is it possible to create a custom event listener to detect this?
Please I cannot seem to find a solution to this anywhere and I found this website explaining how to create custom events
Use PropertyChangeSupport. You wont have to implement as much and it is thread safe.
public class MyClassWithText {
protected PropertyChangeSupport propertyChangeSupport;
private String text;
public MyClassWithText () {
propertyChangeSupport = new PropertyChangeSupport(this);
}
public void setText(String text) {
String oldText = this.text;
this.text = text;
propertyChangeSupport.firePropertyChange("MyTextProperty",oldText, text);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
}
public class MyTextListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getPropertyName().equals("MyTextProperty")) {
System.out.println(event.getNewValue().toString());
}
}
}
public class MyTextTest {
public static void main(String[] args) {
MyClassWithText interestingText = new MyClassWithText();
MyTextListener listener = new MyTextListener();
interestingText.addPropertyChangeListener(listener);
interestingText.setText("FRIST!");
interestingText.setText("it's more like when you take a car, and you...");
}
}
Just like you need to create an event listener, you will also need to create the event firer -- since there is nothing automatic that will do this for you. I've provided sample code that shows you how to implement such a firer.
This test implementation isn't perfect. It only includes a way to add listeners. You may wish to include a way to remove listeners who are no longer interested in receiving events. Also note that this class is not thread-safe.
import java.util.List;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.awt.EventQueue;
/**
* This class uses the EventQueue to process its events, but you should only
* really do this if the changes you make have an impact on part of a GUI
* eg. adding a button to a JFrame.
*
* Otherwise, you should create your own event dispatch thread that can handle
* change events
*/
public class BooleanChangeTest implements BooleanChangeDispatcher {
public static void main(String[] args) {
BooleanChangeListener listener = new BooleanChangeListener() {
#Override
public void stateChanged(BooleanChangeEvent event) {
System.out.println("Detected change to: "
+ event.getDispatcher().getFlag()
+ " -- event: " + event);
}
};
BooleanChangeTest test = new BooleanChangeTest(false);
test.addBooleanChangeListener(listener);
test.setFlag(false); // no change, no event dispatch
test.setFlag(true); // changed to true -- event dispatched
}
private boolean flag;
private List<BooleanChangeListener> listeners;
public BooleanChangeTest(boolean initialFlagState) {
flag = initialFlagState;
listeners = new ArrayList<BooleanChangeListener>();
}
#Override
public void addBooleanChangeListener(BooleanChangeListener listener) {
listeners.add(listener);
}
#Override
public void setFlag(boolean flag) {
if (this.flag != flag) {
this.flag = flag;
dispatchEvent();
}
}
#Override
public boolean getFlag() {
return flag;
}
private void dispatchEvent() {
final BooleanChangeEvent event = new BooleanChangeEvent(this);
for (BooleanChangeListener l : listeners) {
dispatchRunnableOnEventQueue(l, event);
}
}
private void dispatchRunnableOnEventQueue(
final BooleanChangeListener listener,
final BooleanChangeEvent event) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
listener.stateChanged(event);
}
});
}
}
interface BooleanChangeDispatcher {
public void addBooleanChangeListener(BooleanChangeListener listener);
public boolean getFlag();
public void setFlag(boolean flag);
}
/**
* Listener interface for classes interested in knowing about a boolean
* flag change.
*/
interface BooleanChangeListener extends EventListener {
public void stateChanged(BooleanChangeEvent event);
}
/**
* This class lets the listener know when the change occured and what
* object was changed.
*/
class BooleanChangeEvent extends EventObject {
private final BooleanChangeDispatcher dispatcher;
public BooleanChangeEvent(BooleanChangeDispatcher dispatcher) {
super(dispatcher);
this.dispatcher = dispatcher;
}
// type safe way to get source (as opposed to getSource of EventObject
public BooleanChangeDispatcher getDispatcher() {
return dispatcher;
}
}
you can also try to implement an Observer.
First create the observable object:
import java.util.Observable;
public class StringObservable extends Observable {
private String name;
public StringObservable(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
setChanged();
notifyObservers(name);
}
}
Then the observer:
import java.util.Observable;
import java.util.Observer;
public class NameObserver implements Observer {
private String name;
public NameObserver() {
name = null;
}
public void update(Observable obj, Object arg) {
if (arg instanceof String) {
name = (String) arg;
System.out.println("NameObserver: Name changed to " + name);
} else {
System.out.println("NameObserver: Some other change to subject!");
}
}
}
And in your main (or wherever else):
public class TestObservers {
public static void main(String args[]) {
// Create the Subject and Observers.
StringObservable s = new StringObservable("Test");
NameObserver nameObs = new NameObserver();
// Add the Observer
s.addObserver(nameObs);
// Make changes to the Subject.
s.setName("Test1");
s.setName("Test2");
}
}
Mostly found here
Very late to answer, but this is a problem that can be solved with Observer/Observable. Example
The boolean you are setting should be allowed to do only through a setter method like:
public void setFlag(boolean flag){
//Method code goes here
}
Now in now set method, you can decide based on what value comes in, what event needs to be fired. I am explaining in simple terms without introducing complex terms so you can understand better, so code snippet would look like:
public void setFlag(boolean flag){
//if flag is TRUE do something
//If flag is FALSE then do something
//And finally do what you needed to do with flag
}
Ask questions if you need more info
you create a listener when you want to listen for I/O changes. mostly on graphics.
the answer to your question is to keep state of the running program, then check if variables change from the state inside the infinite loop of your program.
You can use AOP for that, perhaps AspectJ? Check a few examples here (if you use Eclipse, then using AspectJ is really simple with their plugin).
For you, you would have a pointcut similar to the one used in the SampleAspect, but one that will only be used when someone makes a new SET to a boolean variable (this doesn't mean that the value has changed, just that someone loaded a value to the variable).

Detecting a change in an integer

Is there any way to detect change in an integer? Such as creating a listener to listen to the integer to detect and change in value it has. I know this is possible with booleans with a few tricks but I cannot seem to adapt this to an int value. Does anyone have any idea how this could be done? I need to know how to do this in the Java language. Below is code that I found online that allows for a boolean listener. How can I convert this to an integer listener?
import java.util.List;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.awt.EventQueue;
//can u see this austin? can u see this i typed this at 9:33 my time
/**
* This class uses the EventQueue to process its events, but you should only
* really do this if the changes you make have an impact on part of a GUI
* eg. adding a button to a JFrame.
*
* Otherwise, you should create your own event dispatch thread that can handle
* change events
*/
public class test1 implements BooleanChangeDispatcher {
public static void main(String[] args) {
BooleanChangeListener listener = new BooleanChangeListener() { // add this to the class
#Override
public void stateChanged(BooleanChangeEvent event) {
System.out.println("Detected change to: "
+ event.getDispatcher().getFlag()
+ " -- event: " + event);
}
};
test1 test = new test1(false);
test.addBooleanChangeListener(listener);
// test.setFlag(false); // no change, no event dispatch
// test.setFlag(true); // changed to true -- event dispatched
}
private boolean flag;
private List<BooleanChangeListener> listeners;
public test1(boolean initialFlagState) {
flag = initialFlagState;
listeners = new ArrayList<BooleanChangeListener>();
}
#Override
public void addBooleanChangeListener(BooleanChangeListener listener) {
listeners.add(listener);
}
#Override
public void setFlag(boolean flag) {
if (this.flag != flag) {
this.flag = flag;
dispatchEvent();
}
}
#Override
public boolean getFlag() {
return flag;
}
private void dispatchEvent() {
final BooleanChangeEvent event = new BooleanChangeEvent(this);
for (BooleanChangeListener l : listeners) {
dispatchRunnableOnEventQueue(l, event);
}
}
private void dispatchRunnableOnEventQueue(
final BooleanChangeListener listener,
final BooleanChangeEvent event) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
listener.stateChanged(event);
}
});
}
}
interface BooleanChangeDispatcher {
public void addBooleanChangeListener(BooleanChangeListener listener);
public boolean getFlag();
public void setFlag(boolean flag);
}
/**
* Listener interface for classes interested in knowing about a boolean
* flag change.
*/
interface BooleanChangeListener extends EventListener {
public void stateChanged(BooleanChangeEvent event);
}
/**
* This class lets the listener know when the change occured and what
* object was changed.
*/
class BooleanChangeEvent extends EventObject {
private final BooleanChangeDispatcher dispatcher;
public BooleanChangeEvent(BooleanChangeDispatcher dispatcher) {
super(dispatcher);
this.dispatcher = dispatcher;
}
// type safe way to get source (as opposed to getSource of EventObject
public BooleanChangeDispatcher getDispatcher() {
return dispatcher;
}
}
I would create a class capable of registering listeners. Below is a mocked up example. It might even compile as is (assuming you write the corresponding VocalIntegerListener interface exists and is implemented somehow... it's pretty simple).
class VocalInteger {
private int value;
private final Object lock = new Object();
Set<VocalIntegerListener> listeners; // assume interface exists - it's easy
public VocalInteger() {
this(0);
}
public VocalInteger(int value) {
this.value = value;
listeners = new HashSet<VocalIntegerListener>();
}
public int getValue() {
return value;
}
public void setValue(int value) {
synchronized(lock) {
int oldValue = this.value;
this.value = value;
for(VocalIntegerListener listener : listeners) {
listener.fireChangedEvent(oldvalue, value); // assume exists
}
}
}
public void registerListener(VocalIntegerListener listener) {
synchronized(lock) {
listeners.add(listener);
}
}
}
Have a look at "Java Beans" and "bound properties" for the standard approach how to listen for property changed events:
http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html
http://docs.oracle.com/javase/tutorial/javabeans/

Categories