I would like to drag and drop from my application into a system folder, so that when I drop an item, it shows up as a new file that represents the object I dragged.
What DataFlavor should I use?
I figured it out myself. You just need to use DataFlavor.javaFileListFlavor and create a temporary file somewhere, and it all works well. Here's what worked for me:
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class FileTransferable implements Transferable
{
final private List<File> files;
final private DataFlavor[] flavors;
/**
* A drag-and-drop object for transfering a file.
* #param file file to transfer -- this file should already exist,
* otherwise it may not be accepted by drag targets.
*/
public FileTransferable(Collection<File> files) {
this.files = Collections.unmodifiableList(
new ArrayList<File>(files));
this.flavors = new DataFlavor[]
{ DataFlavor.javaFileListFlavor };
}
static FileTransferable createFileInTempDirectory(String filename)
throws IOException
{
File dir = new File(System.getProperty("java.io.tmpdir"));
File f = new File(dir, filename);
f.createNewFile();
return new FileTransferable(Collections.singletonList(f));
}
public List<File> getFiles() { return this.files; }
#Override public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException
{
if (isDataFlavorSupported(flavor))
return this.files;
else
return null;
}
#Override public DataFlavor[] getTransferDataFlavors() {
return this.flavors;
}
#Override public boolean isDataFlavorSupported(DataFlavor flavor) {
return DataFlavor.javaFileListFlavor.equals(flavor);
}
}
Related
Reading Events from CSV file into ESPER with POJO, the code below seams to scan through the file (larger files take longer), however no output is generated by events reaching listener.
the CSV file content is:
Geotimestamp,closeoutBid,closeoutAsk,tradable
"20170301 000000643",1.236550,1.236680,0
"20170301 000001893",1.236540,1.236680,0
"20170301 000002893",1.236550,1.236680,0
"20170301 000004410",1.236560,1.236700,0
"20170301 000006160",1.236540,1.236680,0
"20170301 000006393",1.236540,1.236670,0
The code is based on article here
import com.espertech.esper.client.*;
import com.espertech.esperio.csv.AdapterInputSource;
import com.espertech.esperio.csv.CSVInputAdapter;
import com.espertech.esperio.csv.CSVInputAdapterSpec;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import java.io.File;
import java.time.Instant;
public class hAppY implements Runnable {
private static final Log log = LogFactory.getLog(AppMod.class);
public static void main(String[] args) {
SimpleLayout layout = new SimpleLayout();
ConsoleAppender appender = new ConsoleAppender(new SimpleLayout());
Logger.getRootLogger().addAppender(appender);
Logger.getRootLogger().setLevel((Level) Level.WARN);
new AppMod().run();
}
public hAppY() {
}
public void run() {
//The Configuration is meant only as an initialization-time object.
Configuration configuration = new Configuration();
// We register Ticks as objects the engine will have to handle
configuration.addEventType("HistData", HistDataEventClass.class);//.getName()
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(configuration);
File file = new File("./data/DAT_ASCII_GBPUSD_T_201703.csv");
AdapterInputSource ais = new AdapterInputSource(file);
CSVInputAdapterSpec spec = new CSVInputAdapterSpec(ais, "HistDataEventClass");
spec.setUsingExternalTimer(true); //will not pause for time between ticks when true
EPAdministrator epAdmin = epService.getEPAdministrator();
String vel1 = "select count(*) from HistData()";//()from HistData().win:time(3 sec)";
EPStatement cepStatement1 = epAdmin.createEPL(vel1);
//attach 1st statement listener to cep statement obj
cepStatement1.addListener(new CEPListener());
(new CSVInputAdapter(epService, ais,"HistDataEventClass")).start();
}
public class HistDataEventClass {
private java.lang.String /*Instant*/ Geotimestamp;
private java.lang.String receivedTS;
private double closeoutBid;
private double closeoutAsk;
private byte tradable; //ignore
//c'tor
public HistDataEventClass(java.lang.String timestamp, java.lang.Double closeoutBid, java.lang.Double closeoutAsk, byte tradable) {
try {
this.receivedTS = Instant.now().toString(); //received time stamp at time of instantiation of event object (this)
this.Geotimestamp = timestamp;
// NEED TO PROPERLY PARSE THE TS
this.closeoutBid = Double.parseDouble(java.lang.String.valueOf(closeoutBid));
this.closeoutAsk = Double.parseDouble(java.lang.String.valueOf(closeoutAsk));
this.tradable = tradable;
}
catch (Exception e) {
e.printStackTrace();
}
}
// getters/setters
//timestamp
public java.lang.String getTimestamp() {
return this.Geotimestamp;
}
public void setGeoTimestamp(String ts) {this.Geotimestamp = ts;}
// receivedTS
public java.lang.String getReceivedTS() {return this.receivedTS;}
public void setReceivedTS(String rts) {this.receivedTS = rts;}
//CloseoutBid()
public double getCloseoutBid() {return this.closeoutBid;}
public void setCloseoutBid(double bid){this.closeoutBid=bid;}
//CloseoutAsk()
public double getCloseoutAsk() {return this.closeoutAsk;}
public void setCloseoutAsk(double ask){this.closeoutAsk=ask;}
//tradable()
public byte getTradable() {return this.tradable;}
public void setTradable(byte t){this.tradable=t;}
}
public class CEPListener implements UpdateListener { //removed static to satisfy compiler
public void update(EventBean[] newData, EventBean[] oldData) {
System.out.println("my print statement: " + newData[0].getUnderlying());
}
}//CEPListener
}
I am trying to send json object through volley in android studio to a server (mvc spring project in eclipse + tomcat is listening) but the app crashes. I'm new to the volley library. Also, the json object is made up of the data gotten from user inputs in combo box and textviews.
Login Activity:
package com.example.mujtaba.quizzer;
import android.app.ProgressDialog;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import com.android.volley.Cache;
import com.android.volley.Network;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.DiskBasedCache;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.StringRequest;
import com.example.mujtaba.quizzer.Activity.QuizMaking;
import com.example.mujtaba.quizzer.Activity.QuizTaking;
import org.w3c.dom.Text;
import java.util.HashMap;
import java.util.Map;
public class Login extends AppCompatActivity {
private Button button;
private TextView username;
private TextView password;
private Spinner role;
private String url = "http://localhost:8080/users/signup";
private RequestQueue queue;
private ProgressDialog progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
username=(TextView) findViewById(R.id.username);
password=(TextView) findViewById(R.id.password);
button=(Button) findViewById(R.id.button);
role = (Spinner) findViewById(R.id.role);
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.role_spinner, android.R.layout.simple_spinner_item);
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner
role.setAdapter(adapter);
}
public void Quiz(View v) { //select a new activity on the basis of role
// Instantiate the cache
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap
// Set up the network to use HttpURLConnection as the HTTP client.
Network network = new BasicNetwork(new HurlStack());
// Instantiate the RequestQueue with the cache and network.
queue = new RequestQueue(cache, network);
// Start the queue
queue.start();
StringRequest MyStringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//This code is executed if the server responds, whether or not the response contains data.
//The String 'response' contains the server's response.
}
}, new Response.ErrorListener() { //Create an error listener to handle errors appropriately.
#Override
public void onErrorResponse(VolleyError error) {
//This code is executed if there is an error.
}
}) {
protected Map<String, String> getParams() {
Map<String, String> MyData = new HashMap<String,String>();
MyData.put("Username", username.getText().toString() ); //Add the data you'd like to send to the server.
MyData.put("Password",password.getText().toString());
MyData.put("Role",role.getSelectedItem().toString());
MyData.put("Score","0");
return MyData;
}
};
queue.add(MyStringRequest);
}
}
Logcat error:
*3430-3440/com.android.dialer E/StrictMode: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
java.lang.Throwable: Explicit termination method 'close' not called
at dalvik.system.CloseGuard.open(CloseGuard.java:184)
at android.os.ParcelFileDescriptor.<init>(ParcelFileDescriptor.java:180)
at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:916)
at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:906)
at android.app.IBackupAgent$Stub.onTransact(IBackupAgent.java:57)
at android.os.Binder.execTransact(Binder.java:446)*
I have got xades XML as InputStream. I do not care if certyficates are valid, check sign, etc. I can't provide any CA or any other type of certificate storage/validation. What I need is just get documents embedded in xades file as streams or temporary files on disk so I can process them as they were plain files from disk. Could someone provide snippet that extracts embedded documents? TIA
To extract Base64-encoded signed content from XAdES signed file i use code like below. It doesn't use xades4j at all.
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.bouncycastle.util.encoders.Base64;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class Utils {
/**
* extract ds:Object from .xades file
*
* #param xadesIn .xades file input stream
* #return base64 decoded bytes
* #throws Exception
*/
public static byte[] extractContentFromXadesSignedFile(InputStream xadesIn) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(xadesIn);
xadesIn.close();
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
xpath.setNamespaceContext(new SimpleNamespaceContext(new HashMap<String, String>() {{
put("ds", "http://www.w3.org/2000/09/xmldsig#");
}}));
XPathExpression expr = xpath.compile("//ds:SignedInfo/ds:Reference");
NodeList referenceNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
/**
* loop over all Reference nodes
* i need to find Object node with Id that fits URI value of Reference
*/
for(int i=0;i<referenceNodes.getLength();i++){
Node referenceNode = referenceNodes.item(i);
NamedNodeMap attributes = referenceNode.getAttributes();
if(attributes != null) {
Node uri = attributes.getNamedItem("URI");
if(uri != null) {
String objectId = uri.getNodeValue();
XPathExpression expr2 = xpath.compile("//ds:Object[#Id='"+objectId.substring(1)+"']");
Node contentNode = (Node) expr2.evaluate(doc, XPathConstants.NODE);
if(contentNode != null) {
String base64 = contentNode.getFirstChild().getNodeValue();
return Base64.decode(base64);
}
}
}
}
return null;
}
/**
* http://stackoverflow.com/a/6392700/404395
*/
private static class SimpleNamespaceContext implements NamespaceContext {
private final Map<String, String> PREF_MAP = new HashMap<String, String>();
public SimpleNamespaceContext(final Map<String, String> prefMap) {
PREF_MAP.putAll(prefMap);
}
#Override
public String getNamespaceURI(String prefix) {
return PREF_MAP.get(prefix);
}
#Override
public String getPrefix(String uri) {
throw new UnsupportedOperationException();
}
#Override
public Iterator getPrefixes(String uri) {
throw new UnsupportedOperationException();
}
}
}
Sample usage of that:
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Test;
public class XadesExtractTest {
#Test
public void extract() throws Exception {
InputStream in = XadesExtractTest.class.getClassLoader().getResourceAsStream("test.xades");
byte[] bytes = Utils.extractContentFromXadesSignedFile(in);
Assert.assertNotNull(bytes);
in.close();
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
File f = File.createTempFile("test", ".zip");
System.out.println(f.getAbsolutePath());
FileOutputStream fout = new FileOutputStream(f);
IOUtils.copy(bin, fout);
bin.close();
fout.close();
}
}
I'm getting a Null pointer exception in two lines with three stars as shown in the code. Please see to it. I'm a beginnner in android. Thanks in advance. I tried instantiating tiles JSONarray but even that won't work.
package pack.assignment; ***
import java.net.URL;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.InputStream;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.Toast;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.io.IOException;
import android.widget.ImageView;
import java.net.MalformedURLException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.app.Activity;
import android.app.ProgressDialog;
public class Landing extends Activity {
// url to make request
private static String url = "http://playup-jo.s3.amazonaws.com/dev/config.json";
private ProgressDialog pDialog;
JSONParser jParser = new JSONParser();
// tiles JSONArray
JSONArray tiles=null;
ImageView img;
// JSON Node names
private static final String TAG_TILES = "tiles";
private static final String TAG_IMAGE = "image";
private static final String TAG_URL = "url";
private static final String TAG_MDPI = "mdpi";
private static final String TAG_NAME = "name";
// Intent go = new Intent(this, Google.class);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Loading JSON in Background Thread
new LoadJSON().execute();
img = (ImageView)findViewById(R.id.image);
// getting JSON string from URL
JSONObject json = jParser.getJSONFromUrl(url);
}
/**
* Background Async Task to Load all INBOX messages by making HTTP Request
* */
class LoadJSON extends AsyncTask<String, String, Bitmap> {
/**
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(Landing.this);
pDialog.setMessage("Loading ...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
/**
* getting JSON
* */
protected Bitmap doInBackground(String... args) {
// getting JSON string from URL
JSONObject json = jParser.getJSONFromUrl(url);
Bitmap bitmap=null;
// Check your log cat for JSON reponse
// Log.d("JSONNN: ", json.toString());
try {
// Getting Array of Tiles
tiles= new JSONArray();
tiles = json.getJSONArray(TAG_TILES); ***
// looping through All Tiles
for(int i = 0; i < 1; i++){
JSONObject c = tiles.getJSONObject(i);
// Storing json item in variable
String name = c.getString(TAG_NAME);
// mdpi is again JSONObject
JSONObject mdpi= c.getJSONObject(TAG_MDPI);
String image=mdpi.getString(TAG_IMAGE);
String url=mdpi.getString(TAG_URL);
// Using the variable to get the bitmap
try {
bitmap = BitmapFactory.decodeStream((InputStream)new URL(image).getContent());
}
catch (MalformedURLException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
catch (JSONException e) {
e.printStackTrace();
}
return bitmap;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(final Bitmap result) {
// dismiss the dialog after getting all products
pDialog.dismiss();
// updating UI from Background Thread
runOnUiThread(new Runnable() {
public void run() {
/**
* Updating parsed JSON data
* */
img.setImageBitmap(result); // Rule:2 Always access UI toolkit for UI thread not worker thread
MyEventHandler myEvHandler = new MyEventHandler();
// making the downloaded image clickable
img.setOnClickListener(myEvHandler);
}
});
}
}
class MyEventHandler implements OnClickListener
{
public void onClick(View v)
{
if (v instanceof ImageView)
{
// startActivity(go);
}
}
}
}
The error was in JSONParser class, I was using httpPost and getting a status code of 405 and not 200. I used httpGet and it got resolved.
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.