Selenium 2 and JUnit4: How to capture screenshot on exception? - exception

I want to capture a screenshot only upon unexpected exception.

Note.- This answer could be outdated. The answer is based on Selenium 2.15
Using TestWatcher does the trick (the unit test must extend following BaseTest):
public abstract class BaseTest {
// ...
protected WebDriver driver;
#Rule
public TestRule testWatcher = new TestWatcher() {
#Override
public void starting(Description desc) {
LOG.info("Launching browser...");
driver = Utils.getFirefoxDriver();
}
#Override
public void finished(Description desc) {
LOG.info("Quitting driver...");
driver.quit();
}
#Override
public void failed(Throwable e, Description d) {
LOG.debug("Creating screenshot...");
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(
OutputType.FILE);
String scrFilename = "Screenshot.png";
File outputFile = new File(SCREEN_SHOTS_RESULTS_PATH, scrFilename);
LOG.info(scrFilename + " screenshot created.");
try {
org.​apache.​commons.​io.FileUtils.copyFile(scrFile, outputFile);
} catch (IOException ioe) {
LOG.error("Error copying screenshot after exception.", ioe);
}
}
};
}
Note
Utils.getFirefoxDriver() returns a customized WebDriver. Something like:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxBinary;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
public class Utils {
// ...
public static WebDriver getFirefoxDriver() {
FirefoxProfile firefoxProfile = new FirefoxProfile();
// Profile customization. For example:
// firefoxProfile.addExtension("firebug-1.8.4-fx.xpi");
// firefoxProfile.setPreference("extensions.firebug.currentVersion","1.8.4");
FirefoxBinary firefox = new FirefoxBinary();
// Firefox customization. For example:
// firefox.setEnvironmentProperty("DISPLAY", display);
WebDriver driver = new FirefoxDriver(firefox, firefoxProfile);
// WebDriver customizations. For example:
// driver.manage().timeouts().implicitlyWait(SHORT_TIMEOUT_S, TimeUnit.SECONDS);
return driver;
}
}

Related

How can I include native code in Flutter to display a KML Layer on Google Map?

I've been looking into running native code in my Flutter app to display a KML Layer on my Google Map and while I do have the app running native code (at least on Android), I am having difficulty with getting the KML stuff to work.
I have created a MethodChannel in my Flutter class to run the native code and this works fine. Please see below.
// Run java code for KML Campus Map Overlay
Future<void> _showCampusMap() async {
const platform = MethodChannel('uk.ac.manchestermaps/kmlLayer');
try {
final campusMapOverlay =
await platform.invokeMethod('retrieveFileFromUrl');
print(campusMapOverlay);
} on PlatformException catch (error) {
print(error);
}
}
I am taking some code that I used on the Android only pre-alpha version of the app, so I know it works, but I have 4 errors.
In my MainActivity java file,I have these package.
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import org.xmlpull.v1.XmlPullParserException;
import android.os.AsyncTask;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
and the MainActivity class consists of this.
public class MainActivity extends FlutterActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), "**<MY CHANNEL>**").setMethodCallHandler(new MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("retrieveFileFromUrl")) {
retrieveFileFromUrl();
result.success("KMLLayer Retrieved");
}
}
});
}
private void retrieveFileFromUrl() {
new DownloadKmlFile("**<REMOTE KML FILE>**")
.execute();
}
private class DownloadKmlFile extends AsyncTask<String, Void, byte[]> {
private final String mUrl;
public DownloadKmlFile(String url) {
mUrl = url;
}
protected byte[] doInBackground(String... params) {
try {
InputStream is = new URL(mUrl).openStream();
// Log.d(TAG, "doInBackground: " + mUrl.toString());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(byte[] byteArr) {
try {
KmlLayer kmlLayer = new KmlLayer(mMap, new ByteArrayInputStream(byteArr), getApplicationContext());
kmlLayer.addLayerToMap();
// moveCameraToKml(kmlLayer);
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Here are my errors
Running Gradle task 'assembleDebug'...
.....\MainActivity.java:73: error: cannot find symbol
KmlLayer kmlLayer = new KmlLayer(mMap, new ByteArrayInputStream(byteArr), getApplicationContext());
^
symbol: class KmlLayer
location: class MainActivity.DownloadKmlFile
.....\MainActivity.java:73: error: cannot find symbol
KmlLayer kmlLayer = new KmlLayer(mMap, new ByteArrayInputStream(byteArr), getApplicationContext());
^
symbol: class KmlLayer
location: class MainActivity.DownloadKmlFile
.....\MainActivity.java:73: error: cannot find symbol
KmlLayer kmlLayer = new KmlLayer(mMap, new ByteArrayInputStream(byteArr), getApplicationContext());
^
symbol: variable mMap
location: class MainActivity.DownloadKmlFile
3 errors
I have looked around, but can't find anything to help this this specific case as I don't think a lot of people are doing on it. I know KML support has been request for the google_maps_flutter package, but it seems to have gone quiet.
Any help with this would be very much appreciated, as it is pretty central to the app I am developing. Without this, the app is next to useless.
Thanks

Does google_maps_flutter or any other map plugin for flutter supports kml file for google maps?

I want to load kml files on google map in flutter. i can't find it on google _maps_flutter plugin. is there any other plugins which can do it in flutter?
It's been a month, so you may have figured this out, but hopefully, if you haven't this can help.
You can run native code in flutter, so if you can adapt this, it should work. You will need to create a Method Channel to run the native code with something like this.
// Run java code for KML Campus Map Overlay
Future<void> _showCampusMap() async {
const platform = MethodChannel(**<YOUR METHOD CHANNEL>**);
try {
final campusMapOverlay = await platform.invokeMethod('downloadKmlLayer');
print(campusMapOverlay);
} on PlatformException catch (error) {
print(error);
}
}
KML Layer code can be found in the URL below.
https://github.com/googlemaps/android-maps-utils/commit/d606fcde40467abb5fae2ba78b8562a2cd1c517b
Even though I have managed to get native code to work, with simply displaying some text, I haven't figured out how to get the KML Code to work yet.I think the problem lies in it not knowing what mMap is in the onPostExecute method, but it is very possible there is more than I do not know.
import java.io.Console;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import org.xmlpull.v1.XmlPullParserException;
import android.os.AsyncTask;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
public class MainActivity extends FlutterActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), "**<YOUR METHOD CHANNEL>**").setMethodCallHandler(new MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("retrieveFileFromUrl")) {
String KMLLayer = retrieveFileFromUrl();
result.success(KMLLayer);
}
}
});
}
private void retrieveFileFromUrl() {
new DownloadKmlFile("**<YOUR KML LAYER>**")
.execute();
}
private class DownloadKmlFile extends AsyncTask<String, Void, byte[]> {
private final String mUrl;
public DownloadKmlFile(String url) {
mUrl = url;
}
protected byte[] doInBackground(String... params) {
try {
InputStream is = new URL(mUrl).openStream();
// Log.d(TAG, "doInBackground: " + mUrl.toString());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(byte[] byteArr) {
try {
KmlLayer kmlLayer = new KmlLayer(mMap, new ByteArrayInputStream(byteArr), getApplicationContext());
kmlLayer.addLayerToMap();
// moveCameraToKml(kmlLayer);
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
You can see here for a bit more details.
https://medium.com/47billion/creating-a-bridge-in-flutter-between-dart-and-native-code-in-java-or-objectivec-5f80fd0cd713
I hope gets you on the right path.
--------NOTE: only for android-------------
This solution won't work for iOS but there is a workaround for android, you can see the solution here in the medium article

Moving JavaFX Nodes Between Stages

I'm rewriting a Swing application in JavaFX, where I allow users to present multiple workspaces as either windows or tabs. However, my FX code will not display the contents moved from more than one tab into a new stage; only the contents of the currently-selected tab appear in my new stages. I've distilled my code into a small example below. Can anyone clue me in as to what's gone wrong?
package scenes;
import java.util.ArrayList;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class StageSwapper extends Application {
static public void main(String[] args) {
launch(args);
}
private TabPane tabs = new TabPane();
public void start(Stage stage) {
stage.setTitle("Stage Swapper");
BorderPane p = new BorderPane();
p.setCenter(tabs);
tabs.getTabs().addAll(new Swapee("First").createTab(), new Swapee("Second").createTab());
Scene s = new Scene(p);
stage.setScene(s);
stage.show();
launchSwap();
}
private void launchSwap() {
new Thread() {
public void run() {
try {
sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
Platform.runLater(new Runnable() {
public void run() {
for (Swapee s : Swapee.list) {
createWindow(s);
}
}
});
}
}.start();
}
public void createWindow(Swapee s) {
Stage window = new Stage();
window.setTitle("New Window");
window.setY(200);
window.setX(200);
BorderPane p = new BorderPane();
p.setCenter(s);
window.setScene(new Scene(p));
window.show();
}
}
class Swapee extends Label {
static private int count;
static ArrayList<Swapee> list = new ArrayList<>();
String name;
Swapee(String name) {
super("Swappable Item " + ++count);
this.name = name;
list.add(this);
}
Tab createTab() {
Tab t = new Tab(name);
t.setContent(this);
return t;
}
}
You haven't specified the size of the windows that you're creating. Right now they have width and length equal to 0. You may use the following approach:
BorderPane p = new BorderPane();
p.setPrefSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
p.setCenter(s);
BorderPane will be resized according to its content and the window will be resized as well.

Basic JUnit test for JavaFX 8

I want to create basic JUnit test for JavaFX 8 application. I have this simple code sample:
public class Main extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tabs");
Group root = new Group();
Scene scene = new Scene(root, 400, 250, Color.WHITE);
TabPane tabPane = new TabPane();
BorderPane borderPane = new BorderPane();
for (int i = 0; i < 5; i++) {
Tab tab = new Tab();
tab.setText("Tab" + i);
HBox hbox = new HBox();
hbox.getChildren().add(new Label("Tab" + i));
hbox.setAlignment(Pos.CENTER);
tab.setContent(hbox);
tabPane.getTabs().add(tab);
}
// bind to take available space
borderPane.prefHeightProperty().bind(scene.heightProperty());
borderPane.prefWidthProperty().bind(scene.widthProperty());
borderPane.setCenter(tabPane);
root.getChildren().add(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I only have this code so far:
import javafx.application.Application;
import javafx.stage.Stage;
import org.junit.BeforeClass;
public class BasicStart extends Application {
#BeforeClass
public static void initJFX() {
Thread t = new Thread("JavaFX Init Thread") {
#Override
public void run() {
Application.launch(BasicStart.class, new String[0]);
}
};
t.setDaemon(true);
t.start();
}
#Override
public void start(Stage primaryStage) throws Exception {
// noop
}
}
Can you tell me how I can create JUnit test for the above code?
I use a Junit Rule to run unit tests on the JavaFX thread. The details are in this post. Just copy the class from that post and then add this field to your unit tests.
#Rule public JavaFXThreadingRule javafxRule = new JavaFXThreadingRule();
This code works for both JavaFX 2 and JavaFX 8.
The easiest aproach is the following:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.stage.Stage;
import org.junit.Test;
public class BasicStart {
#Test
public void testA() throws InterruptedException {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
new JFXPanel(); // Initializes the JavaFx Platform
Platform.runLater(new Runnable() {
#Override
public void run() {
new Main().start(new Stage()); // Create and
// initialize
// your app.
}
});
}
});
thread.start();// Initialize the thread
Thread.sleep(10000); // Time to use the app, with out this, the thread
// will be killed before you can tell.
}
}
Hope it helps!
Based on Brian Blonski 's answer I created a JUnit-Testrunner, that does essentially the same thing, but is a bit simpler to use in my opinion.
Using it, your test would look like this:
#RunWith( JfxTestRunner.class )
public class MyUnitTest
{
#Test
public void testMyMethod()
{
//...
}
}

Swing GUI Client Listeners not responding to update of Remote RMI Property

I am having some trouble with getting a JTree to redraw when an explicit call is made to its model (a call which I make once I have added some new nodes to it).
The code, which initially worked fine, fails now that the application is exported to RMI.
I store the DefaultTreeModel object in the Controller class, which is a Remote Object.
I add the DefaultTreeModel object to the JTree in my Client, using tree.addModel(controller.getModel());
I use an ActionListener subscribed to a button on the Client GUI to call a method in the Controller which performs the "Add new node" action.
I use a TreeModelListener to print a message to screen to prove that the Model Listener has fired.
Do Client side Swing listeners not work over RMI?
I have managed to reproduce the problem. I include the code for completeness but anticipate that someone will be able to reel off the answer based on experience.
Server Driver Class:
package server;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import client.controller.TestTreeControllerService;
import server.controller.TestTreeControllerImpl;
public class TestTreeServerStart {
/**
* #param args
*/
public static void main(String[] args) {
new TestTreeServerStart();
}
public TestTreeServerStart() {
try {
LocateRegistry.createRegistry(1099);
TestTreeControllerService c = new TestTreeControllerImpl();
Registry registry = LocateRegistry.getRegistry();
registry.rebind("TestTreeControllerService", c);
System.out.println("Started the RMI Server");
}
catch (RemoteException e) {
System.out.println(e.getMessage());
}
}
}
Server Controller Implementation Class:
package server.controller;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import client.controller.TestTreeControllerService;
#SuppressWarnings("serial")
public class TestTreeControllerImpl extends UnicastRemoteObject implements TestTreeControllerService {
/**
*
*/
//private static final long serialVersionUID = -8137864611400855504L;
private DefaultTreeModel m ;
public DefaultTreeModel getModel() {
return m;
}
public TestTreeControllerImpl() throws RemoteException {
super();
m = new DefaultTreeModel(new DefaultMutableTreeNode("Root"));
}
public void addNodeAction() throws RemoteException {
DefaultTreeModel m = (DefaultTreeModel) getModel();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New Node");
DefaultMutableTreeNode root = (DefaultMutableTreeNode) m.getRoot();
root.add(newNode);
//m.insertNodeInto(newNode, (DefaultMutableTreeNode) m.getRoot(), m.getChildCount(m.getRoot()));
m.nodeStructureChanged(root);
}
}
Client Driver Class:
package client;
import java.rmi.Naming;
import java.rmi.RemoteException;
import client.controller.TestTreeControllerService;
import client.view.TreeTestClient;
public class TreeTestClientStart {
/**
* #param args
*/
public static void main(String[] args) {
try {
TestTreeControllerService c = (TestTreeControllerService) Naming.lookup("rmi://localhost:1099/TestTreeControllerService");
new TreeTestClient(c);
}
catch(RemoteException e) {
System.out.println("Remote service not found: " + e.getLocalizedMessage());
}
catch (Exception e) {
System.out.println("Splat");
}
}
}
Client Controller Interface:
package client.controller;
import javax.swing.tree.DefaultTreeModel;
public interface TestTreeControllerService extends java.rmi.Remote {
public DefaultTreeModel getModel() throws java.rmi.RemoteException;
public void addNodeAction() throws java.rmi.RemoteException;
}
Client UI:
package client.view;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import client.controller.TestTreeControllerService;
import client.view.action.AddNodeAction;
import client.view.action.RefreshTreeAction;
public class TreeTestClient {
private JTree t;
private TestTreeControllerService c;
public JTree getTree() {
return t;
}
public TestTreeControllerService getController() {
return c;
}
public void setTree(JTree tIn) {
t = tIn;
}
public TreeTestClient(TestTreeControllerService cIn) {
//Add controller
try {
c = cIn;
//Draw Frame & Panel - set dimensions
JFrame f = new JFrame();
f.setSize(new Dimension(800,600));
JPanel p = new JPanel();
p.setSize(new Dimension(800,600));
//Create a tree and add the Model from the Controller to it
t = new JTree();
t.setModel(c.getModel());
//Try a Tree Model Listener
t.getModel().addTreeModelListener(new RefreshTreeAction(this));
//Add listener to a button which adds nodes to the tree when clicked
JButton addNode = new JButton("Add node");
addNode.addActionListener(new AddNodeAction(this));
JScrollPane s = new JScrollPane(t);
p.add(s);
p.add(addNode);
p.setVisible(true);
f.add(p);
f.setVisible(true);
}
catch(Exception e) {
System.out.println("Splat");
}
}
}
*Client "Add Node" Action Listener (invokes Add Action in Controller) *
package client.view.action;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import client.view.TreeTestClient;
public class AddNodeAction implements ActionListener {
private TreeTestClient treeTest;
public AddNodeAction(TreeTestClient treeTestIn) {
treeTest=treeTestIn;
}
#Override
public void actionPerformed(ActionEvent arg0) {
try {
treeTest.getController().addNodeAction();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Client "Refresh Action" Tree Listener (Prints to Screen to prove that Listener fired)
package client.view.action;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import client.view.TreeTestClient;
public class RefreshTreeAction implements PropertyChangeListener, TreeModelListener {
private TreeTestClient treeTest;
public RefreshTreeAction(TreeTestClient treeTestIn) {
treeTest = treeTestIn;
}
private void refreshTree() {
System.out.println("Refresh tree fired");
}
#Override
public void treeNodesChanged(TreeModelEvent arg0) {
refreshTree();
}
#Override
public void treeNodesInserted(TreeModelEvent arg0) {
refreshTree();
}
#Override
public void treeNodesRemoved(TreeModelEvent arg0) {
refreshTree();
}
#Override
public void treeStructureChanged(TreeModelEvent arg0) {
refreshTree();
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
refreshTree();
}
}
The TreeModel exported by the server is serialized to the client as the client's own copy. The server doesn't know anything about what happens to the client's copy, and the client doesn't know anything about what happens to the server's copy. They are not the same object.
By adding the following code to an ActionListener subscribed to a new button on the GUI, I have been able to examine the contents of the Model at the click of a button:
//Loop contents of model attached to Client Tree
for (int i=0; i<t.getModel().getChildCount(t.getModel().getRoot()); i++) {
System.out.println("From Tree: Row #" + i + ": " + t.getModel().getChild(t.getModel().getRoot(), i));
}
//Loop contents of model object stored in Controller
try {
for (int i=0; i<c.getModel().getChildCount(c.getModel().getRoot()); i++) {
System.out.println("From Controller: Row #" + i + ": " + c.getModel().getChild(c.getModel().getRoot(), i));
}
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I found that the reference to the Client's Model was a different version to the state being maintained in the Controller's model object. There was no output in the Client loop, but the Controller's loop gave the correct state.
I have subsequently added a Swing Timer to the GUI to refresh the tree's model to match that of the Constructor. An updated GUI Class and GUI Refresh Action follow, which work:
Updated UI:
package client.view;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.Timer;
import client.controller.TestTreeControllerService;
import client.view.action.AddNodeAction;
import client.view.action.GUIRefreshAction;
import client.view.action.RefreshTreeAction;
public class TreeTestClient {
private JTree t;
private TestTreeControllerService c;
public JTree getTree() {
return t;
}
public TestTreeControllerService getController() {
return c;
}
public void setTree(JTree tIn) {
t = tIn;
}
public TreeTestClient(TestTreeControllerService cIn) {
//Add controller
try {
c = cIn;
//Draw Frame & Panel - set dimensions
JFrame f = new JFrame();
f.setSize(new Dimension(800,600));
JPanel p = new JPanel();
p.setSize(new Dimension(800,600));
//Create a tree and add the Model from the Controller to it
t = new JTree();
t.setModel(c.getModel());
//Try a listener that doesn't use the Remote object
t.addTreeSelectionListener(new RefreshTreeAction(this));
//Try a property change listener on the TreeModel
t.addPropertyChangeListener("treeModel", new RefreshTreeAction(this));
//Try a Tree Model Listener
t.getModel().addTreeModelListener(new RefreshTreeAction(this));
//Add listener to a button which adds nodes to the tree when clicked
JButton addNode = new JButton("Add node");
addNode.addActionListener(new AddNodeAction(this));
JScrollPane s = new JScrollPane(t);
//Add a GUI redraw timer
Timer timer = new Timer(1000, new GUIRefreshAction(this));
timer.setInitialDelay(1);
timer.start();
p.add(s);
p.add(addNode);
p.setVisible(true);
f.add(p);
f.setVisible(true);
}
catch(Exception e) {
System.out.println("Splat");
}
}
}
GUI Refresh Listener Class
package client.view.action;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import client.view.TreeTestClient;
public class GUIRefreshAction implements ActionListener {
private TreeTestClient client;
public GUIRefreshAction(TreeTestClient clientIn) {
client = clientIn;
}
#Override
public void actionPerformed(ActionEvent e) {
//Update the Tree's Model to match latest on Server
try {
client.getTree().setModel(client.getController().getModel());
} catch (RemoteException e1) {
e1.printStackTrace();
}
}
}
I hope this helps someone who has the same requirements for a RMI Client Swing GUI to update in reaction to changes on the Server.