Problem updating JTree via RMI

I'm programming an Instant Messenger application in Java using RMI extensively. Much like most IM applications, my IM clients display a "buddy list" window upon successful authentication with the server. I have reached a huge stumbling block at this point. My Buddylist JFrame contains a JTree that is used to display the status of the user's buddies (i.e. online, offline). I am able to populate the JTree without any problems BEFORE the JFrame is displayed by using the DefaultTreeModel's insertNodeInto() method. But the problem I'm having is that, once the Buddylist is displayed to the user, I can't successfully update the JTree to reflect changing user status. For example, let's say a user with the screename "qwerty" logs in to the server. Now "qwerty" sees his Buddylist pop up on screen. His Buddylist contains 1 user (for simplicity's sake) with screename "foo". "foo" is currently not logged into the system so "foo" is shown in the Buddylist as a child of node Offline. But right now, let's say that "foo" logs into the system. "qwerty's" Buddylist should be updated to reflect the fact that "foo" just signed in by removing "foo" as a child node of Offline and adding a new node to Online called "foo".

I currently have this functionality implemented as an RMI callback method on the server side. When "foo" logs in, the server calls the method fireBuddyLoggedOnEvent() with "foo" as the argument. Because "qwerty" is already logged in, and both users are each other's buddy, the statement

c.getCallback().buddySignedOn(screenname);

will be executed on the client side. Unfortunately, even though this code is successfully executed remotely, "qwerty's" Buddylist's JTree does not update to show that "foo" is now online. My only suspicion is that this is due to the fact that the Buddylist is already visible and this somehow affects its ability to be updated (remember that I have no problem populating the tree BEFORE it's visible). However, I've weeded out this possibility by creating a test frame in which its JTree is successfully updated, but in response to an ActionEvent in response to a button click. Of course, this test case was not an RMI application and does not come with the complexities of RMI. Please help me resolve this issue as it's preventing me from proceeding with my project.

~BTW, sorry for the poor code formatting. I added all the code in wordpad and pasted it in here, which stripped the formatting.

/*

* Frame that allows the user to enter information to

* be used to login to the server.

*/

public class LoginFrame extends JFrame {

...

...

...

signonButton.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

if (!screenameTextField.getText().equals("") &&

!passwordTextField.getPassword().equals("")) {

try {

serverInter = (ServerInter) Naming.lookup(serverName);

String username = screenameTextField.getText();

String password = String.valueOf(passwordTextField.getPassword());

int connectionID = serverInter.connect(username, password);

System.out.println("authenticate successful");

dispose();

Buddylist buddyList = new Buddylist(username, connectionID);

// Registers the buddylist with the server so that

// the server can remotely call methods on the

// Buddylist.

serverInter.registerCallback(buddyList, connectionID);

return;

} catch (Exception e1) {

JOptionPane.showMessageDialog(LoginFrame.this, e1.getMessage(),

"Connection Error", JOptionPane.ERROR_MESSAGE);

passwordTextField.setText("");

signonButton.setEnabled(false);

e1.printStackTrace();

}

}

}

};

...

...

...

public static void main(String[] args) {

new LoginFrame();

}

}

public class Buddylist extends JFrame implements BuddylistInter {

...

...

...

public Buddylist(String username, int connectionID) {

try {

serverInter = (ServerInter) Naming.lookup(serverName);

this.username = username;

this.connectionID = connectionID;

this.setTitle(username + "'s BuddyList");

initialize();

} catch (Exception e) {

e.printStackTrace();

}

}

/*

* Method of interest. Note that this method uses a DynamicTree

* object as included in the Sun tutorial on JTree's

* (located at http://www.iam.ubc.ca/guides/javatut99/uiswing/components/tree.html#dynamic).

* Don't worry too much about where the node is getting added

* but rather, that i wish to verify that an arbitrary node

* can successfully be inserted into the tree during this

* remote method call

*/

public void buddySignedOn(String screenname) throws RemoteException {

// should add screename at some location in the tree

// but doesn't!

treePanel.addObject(screename);

}

...

...

...

}

/*

* Oversimplified interface for the Buddylist that is intended

* to be used to allow callbacks to the clientside.

*/

public interface BuddylistInter extends Remote {

public void buddySignedOn(String screenname) throws RemoteException;

}

/*

* Another oversimplified interface that is to be

* implemented by the server so that the client can

* call remote server methods.

*/

public interface ServerInter extends java.rmi.Remote {

// "Registers" the given Buddylist with this server to allow

// remote callbacks on the Buddylist.

public void registerCallback(Buddylist buddylist, int connectionID)

throws RemoteException;

}

public class Server extends UnicastRemoteObject implements ServerInter {

...

...

...

private Vector loggedInUsers = new Vector();

// Note that this method assumes that a Connection object

// representing the Buddylist with connectionID was added

// to the loggedInUsers list during authentication.

public void registerCallback(Buddylist buddylist, int connectionID) throws RemoteException {

int index = loggedInUsers.indexOf(new Connection(connectionID));

Connection c = (Connection) loggedInUsers.get(index);

c.setCallback(buddylist);

}

// Method that's called whenever a client successfully

// connects to this server object.

// screename is the name of the user that successfully

// logged in.

private void fireBuddyLoggedOnEvent(String screenname) {

// Examines each logged in client to determine whether

// or not that client should be notified of screename's

// newly logged in status.

for (int i = 0; i < loggedInUsers.size(); i++) {

Connection c = (Connection) loggedInUsers.get(i);

if (database1.areBuddies(screenname, c.getUsername())) {

try {

// getCallback() returns a reference to

// the Buddylist that was registered

// with this server. At this point,

// the server attempts to notify the

// client that one of his "buddies" has

// just logged into the server.

c.getCallback().buddySignedOn(screenname);

} catch (RemoteException e) {

e.printStackTrace();

}

}

}

}

...

...

...

}

[7497 byte] By [java_sunya] at [2007-10-3 10:04:29]
# 1
The callback doesn't execute in the AWT thread so it shouldn't call Swing methods directly. It needs to use SwingUtilities.invokeLater() to update the GUI.
ejpa at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...
# 2

Thanks for responding, ejp. I changed the buddySignedOn() method so that it now looks like this:

public void buddySignedOn(final String screenname) throws RemoteException {

Runnable addBuddy = new Runnable() {

public void run() {

// Should add screename to the online node

treePanel.addObject(p1, screenname);

}

};

SwingUtilities.invokeLater(addBuddy);

}

But this code doesn't work. After careful debugging, I found that the JTree is updated successfully within the buddySignedOn() method, but after the call returns back to the Server, the changes are no longer there. I am under the impression that the Buddylist that was registered with the server initially is somehow a different copy of the client buddylist, and therefore, the changes are being made to a buddylist object on the server but not back on the client side buddylist. I overrode the equals() and hashcode() methods in Buddylist to hopefully ensure that the object is serialized properly but it doesn't help.

public boolean equals(Object o) {

Buddylist buddylist = (Buddylist) o;

return connectionID == buddylist.connectionID;

}

public int hashCode() {

return connectionID;

}

How can I ensure that when buddySignedOn() is called from within the server, that the method is actually operating on the client-side object rather than a server side copy?

java_sunya at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...
# 3
You're right, I think BuddyList is being passed to the server as a serializable object. You need to call UnicastRemoteObject.exportObject(buddyList,...) on it to export it as an RMI server from the client host.
ejpa at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...
# 4

Ok, so I export Buddylist right before a call to the remote method, registerCallback() as follow:

UnicastRemoteObject.exportObject(buddyList);

serverInter.registerCallback(buddyList, connectionID);

But now I get an IllegalArgumentException as detailed by the stack trace below. I'd like to mention that I run rmic on both the Buddylist and the Server literally every time right before I do a test run of the program so I don't believe this exception is related to stubs being "out of date" with their respective class files (As is the first concern that code reviewers bring up after seeing this kind of exception). Any ideas?

java.lang.IllegalArgumentException: argument type mismatch

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:585)

at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:294)

at sun.rmi.transport.Transport$1.run(Transport.java:153)

at java.security.AccessController.doPrivileged(Native Method)

at sun.rmi.transport.Transport.serviceCall(Transport.java:149)

at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:460)

at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:701)

at java.lang.Thread.run(Thread.java:595)

at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:247)

at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:223)

at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:126)

at server_package.Server_Stub.registerCallback(Unknown Source)

at client_package.LoginFrame$3.actionPerformed(LoginFrame.java:271)

at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1849)

at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2169)

at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)

at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)

at javax.swing.AbstractButton.doClick(AbstractButton.java:302)

at javax.swing.AbstractButton.doClick(AbstractButton.java:282)

at client_package.LoginFrame$2.keyPressed(LoginFrame.java:183)

at java.awt.Component.processKeyEvent(Component.java:5446)

at javax.swing.JComponent.processKeyEvent(JComponent.java:2713)

at java.awt.Component.processEvent(Component.java:5265)

at java.awt.Container.processEvent(Container.java:1966)

at java.awt.Component.dispatchEventImpl(Component.java:3955)

at java.awt.Container.dispatchEventImpl(Container.java:2024)

at java.awt.Component.dispatchEvent(Component.java:3803)

at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1810)

at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:672)

at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:920)

at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:798)

at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:636)

at java.awt.Component.dispatchEventImpl(Component.java:3841)

at java.awt.Container.dispatchEventImpl(Container.java:2024)

at java.awt.Window.dispatchEventImpl(Window.java:1774)

at java.awt.Component.dispatchEvent(Component.java:3803)

at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)

at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)

at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)

at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)

at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)

at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)

java_sunya at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...
# 5
Delete all .class files and recompile everything.
ejpa at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...
# 6

Ok, I deleted all .class files, recomplied, and rmic'd, and I still get the IllegalArgumentException. So I've decided to just post all the code here because I don't want to make an assumption that all the code is correct. Thanks for helping me out with this very stressful problem.

/*

* Created on Nov 13, 2006

*/

import java.awt.Image;

import java.awt.image.BufferedImage;

import java.io.IOException;

import java.net.MalformedURLException;

import java.rmi.Naming;

import java.rmi.NotBoundException;

import java.rmi.RemoteException;

import java.rmi.server.UnicastRemoteObject;

import javax.imageio.ImageIO;

import javax.swing.ImageIcon;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JMenu;

import javax.swing.JMenuBar;

import javax.swing.JPanel;

import javax.swing.JSeparator;

import javax.swing.SwingUtilities;

import javax.swing.tree.DefaultMutableTreeNode;

public class Buddylist extends JFrame implements BuddylistInter {

private String serverName = "//Broom:" + "5001" + "/IMServer";

private DynamicTree treePanel = null;

private String onlineString = "Online";

private String offlineString = "Offline";

private DefaultMutableTreeNode onlineNode = null;

private DefaultMutableTreeNode offlineNode = null;

private ImageUpdater imageUpdater = null;

private javax.swing.JPanel jContentPane = null;

private ServerInter serverInter = null;

private String username = null;

// A connectionID of -1 indicates that this Buddylist

// has not yet received a valid id from the server.

private int connectionID = -1;

private JMenuBar jJMenuBar = null;

private JMenu jMenu = null;

private JMenu jMenu1 = null;

private JMenu jMenu2 = null;

private JPanel imagePanel = null;

private JSeparator jSeparator1 = null;

public Buddylist(String username, int connectionID) {

try {

serverInter = (ServerInter) Naming.lookup(serverName);

this.username = username;

this.connectionID = connectionID;

this.setTitle(username + "'s BuddyList");

imageUpdater = new ImageChooser(this);

initialize();

/*

* This statement is causing an IllegalArgumentException

* to be thrown! I've tried inserting it at the beginning

* of the constructor and that doesn't help.

*/

UnicastRemoteObject.exportObject(this);

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (RemoteException e) {

e.printStackTrace();

} catch (NotBoundException e) {

e.printStackTrace();

}

}

/**

* This method initializes this

*

* @return void

*/

private void initialize() {

this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

this.setJMenuBar(getJJMenuBar());

this.setPreferredSize(new java.awt.Dimension(196,466));

this.setMinimumSize(new java.awt.Dimension(150,439));

this.setSize(196, 466);

this.setContentPane(getJContentPane());

this.setLocationRelativeTo(null);

this.setVisible(true);

}

/**

* This method initializes jContentPane

*

* @return javax.swing.JPanel

*/

private javax.swing.JPanel getJContentPane() {

if(jContentPane == null) {

jContentPane = new javax.swing.JPanel();

jContentPane.setLayout(null);

jContentPane.add(getImagePanel(), null);

jContentPane.add(getJSeparator1(), null);

jContentPane.add(getTreePanel(), null);

}

return jContentPane;

}

private DynamicTree getTreePanel() {

if (treePanel == null) {

treePanel = new DynamicTree();

onlineNode = treePanel.addObject(null, onlineString);

offlineNode = treePanel.addObject(null, offlineString);

treePanel.setBounds(6, 138, 177, 196);

populateTree();

return treePanel;

}

return null;

}

private void populateTree() {

try {

String [] buddies = serverInter.getBuddyList(this.username);

for (int i = 0; i < buddies.length; i++) {

try {

if (serverInter.isBuddyOnline(buddies[i])) {

treePanel.addObject(onlineNode, buddies[i]);

}

else {

treePanel.addObject(offlineNode, buddies[i]);

}

} catch (RemoteException e1) {

e1.printStackTrace();

}

}

} catch (RemoteException e) {

e.printStackTrace();

}

}

/**

* This method initializes jJMenuBar

*

* @return javax.swing.JMenuBar

*/

private JMenuBar getJJMenuBar() {

if (jJMenuBar == null) {

jJMenuBar = new JMenuBar();

jJMenuBar.add(getJMenu());

jJMenuBar.add(getJMenu1());

jJMenuBar.add(getJMenu2());

}

return jJMenuBar;

}

/**

* This method initializes jMenu

*

* @return javax.swing.JMenu

*/

private JMenu getJMenu() {

if (jMenu == null) {

jMenu = new JMenu();

jMenu.setText("My IM");

}

return jMenu;

}

/**

* This method initializes jMenu1

*

* @return javax.swing.JMenu

*/

private JMenu getJMenu1() {

if (jMenu1 == null) {

jMenu1 = new JMenu();

jMenu1.setText("People");

}

return jMenu1;

}

/**

* This method initializes jMenu2

*

* @return javax.swing.JMenu

*/

private JMenu getJMenu2() {

if (jMenu2 == null) {

jMenu2 = new JMenu();

jMenu2.setText("Help");

}

return jMenu2;

}

/**

* This method initializes imagePanel

*

* @return javax.swing.JPanel

*/

private JPanel getImagePanel() {

if (imagePanel == null) {

imagePanel = new JPanel();

imagePanel.setBounds(6, 2, 176, 125);

try {

BufferedImage bi =

ImageIO.read(

getClass().getClassLoader().getResourceAsStream("images/cute_dog.jpg"));

Image scaled = bi.getScaledInstance(

imagePanel.getWidth(), imagePanel.getHeight(), BufferedImage.SCALE_FAST);

final JLabel imageLabel = new JLabel(new ImageIcon(scaled));

imageLabel.setToolTipText("Double click to change image");

imagePanel.add(imageLabel, imageLabel.getName());

imageLabel.addMouseListener(new java.awt.event.MouseAdapter() {

public void mouseClicked(java.awt.event.MouseEvent e) {

if (e.getClickCount() == 2) {

Image selected = imageUpdater.getSelectedImage();

if (selected != null) {

BufferedImage bi = (BufferedImage) selected;

Image scaled = bi.getScaledInstance(

imageLabel.getWidth(), imageLabel.getHeight(), BufferedImage.SCALE_DEFAULT);

imageLabel.setIcon(new ImageIcon(scaled));

}

}

}

});

} catch (IOException e) {

e.printStackTrace();

}

}

return imagePanel;

}

/**

* This method initializes jSeparator1

*

* @return javax.swing.JSeparator

*/

private JSeparator getJSeparator1() {

if (jSeparator1 == null) {

jSeparator1 = new JSeparator();

jSeparator1.setBounds(6, 132, 176, 1);

}

return jSeparator1;

}

public void buddySignedOn(String screenname) throws RemoteException {

final String temp = screenname;

Runnable addBuddy = new Runnable() {

public void run() {

treePanel.addObject(onlineNode, temp);

}

};

SwingUtilities.invokeLater(addBuddy);

}

public void buddySignedOff(String screenname) throws RemoteException {

// TODO Auto-generated method stub

}

public boolean equals(Object o) {

Buddylist buddylist = (Buddylist) o;

return connectionID == buddylist.connectionID;

}

public int hashCode() {

return connectionID;

}

} // @jve:decl-index=0:visual-constraint="10,11"

/*

* Created on Nov 4, 2006

*/

import java.rmi.Remote;

import java.rmi.RemoteException;

public interface BuddylistInter extends Remote {

/**

*/

public void buddySignedOn(String screenname) throws RemoteException;

/**

*/

public void buddySignedOff(String screenname) throws RemoteException;

}

/*

* Created on Oct 14, 2006

*/

/**

* Models a single endpoint of a connection between machines.

* @author Josh Feldman

*/

public class Connection {

private String username;

private final int connectionID;

private Buddylist callback = null;

public Connection(String username, int connectionID) {

this.username = username;

this.connectionID = connectionID;

}

public Connection(int connectionID) {

this.connectionID = connectionID;

}

public String getUsername() {

return username;

}

public int getConnectionID() {

return connectionID;

}

public void setCallback(Buddylist buddylist) {

this.callback = buddylist;

}

public Buddylist getCallback() {

return callback;

}

public boolean equals(Object o) {

Connection otherConnection = (Connection) o;

if (otherConnection.getConnectionID() == this.connectionID) {

return true;

}

else {

return false;

}

}

public int hashCode() {

return connectionID;

}

}

/*

* Created on Nov 4, 2006

*/

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.Serializable;

import java.util.Properties;

public class Database implements Serializable{

private final String regex = ";";

private Properties db = null;

private String dbPath = "buddies.txt";

public Database() throws IOException {

db = new Properties();

File buddiesFile = new File(dbPath);

if (!buddiesFile.canRead()) {

throw new IOException("Can't read database!");

}

try {

FileInputStream fis = new FileInputStream(buddiesFile);

db.load(fis);

System.out.println("database loaded from file");

} catch (IOException e) {

e.printStackTrace();

System.err.println("Can't load the database! Exiting program...");

System.exit(0);

}

}

/**

* called when a user adds/deletes a user from the buddylist

*

*/

public void changeBuddyList() {

//TODO

}

public boolean doesUserExist(String username) {

System.out.println(db.getProperty(username));

return db.getProperty(username) != null;

}

public String getPassword(String username) {

String temp = db.getProperty(username);

String [] split = temp.split(regex);

if (split.length == 2)

return split[0];

else {

return null;

}

}

public String getBuddies(String username) {

String temp = db.getProperty(username);

if (temp == null)

return null;

String [] split = temp.split(regex);

if (split.length != 2)

return null;

else {

return split[1];

}

}

/**

* Determines whether screename1 is a buddy of screename2

* @return

*/

public boolean areBuddies(String screename1, String screename2) {

String [] buddies = getUserBuddies(screename2);

if (buddies == null) {

return false;

}

else {

for (int i = 0; i < buddies.length; i++) {

if (buddies[i].equals(screename1)) {

return true;

}

}

}

return false;

}

public String [] getUserBuddies(String username) {

System.out.println("in db getUserBuddies: username = " + username);

String temp = db.getProperty(username);

if (temp == null)

return null;

String [] split = temp.split(regex);

if (split.length != 2)

return null;

else {

return split[1].split(",");

}

}

}

import java.awt.GridLayout;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTree;

import javax.swing.event.TreeModelEvent;

import javax.swing.event.TreeModelListener;

import javax.swing.tree.DefaultMutableTreeNode;

import javax.swing.tree.DefaultTreeModel;

import javax.swing.tree.MutableTreeNode;

import javax.swing.tree.TreePath;

import javax.swing.tree.TreeSelectionModel;

// Note that this is not my code but rather code taken

// from java Sun tutorial

public class DynamicTree extends JPanel {

protected DefaultMutableTreeNode rootNode;

protected DefaultTreeModel treeModel;

protected JTree tree;

public DynamicTree() {

rootNode = new DefaultMutableTreeNode("Root Node");

treeModel = new DefaultTreeModel(rootNode);

treeModel.addTreeModelListener(new MyTreeModelListener());

tree = new JTree(treeModel);

tree.setRootVisible(false);

tree.setEditable(false);

tree.getSelectionModel().setSelectionMode

(TreeSelectionModel.SINGLE_TREE_SELECTION);

tree.setShowsRootHandles(false);

JScrollPane scrollPane = new JScrollPane(tree);

setLayout(new GridLayout(1,0));

add(scrollPane);

}

/** Remove all nodes except the root node. */

public void clear() {

rootNode.removeAllChildren();

treeModel.reload();

}

/** Remove the currently selected node. */

public void removeCurrentNode() {

TreePath currentSelection = tree.getSelectionPath();

if (currentSelection != null) {

DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode)

(currentSelection.getLastPathComponent());

MutableTreeNode parent = (MutableTreeNode)(currentNode.getParent());

if (parent != null) {

treeModel.removeNodeFromParent(currentNode);

return;

}

}

}

public void removeObject(DefaultMutableTreeNode child) {

treeModel.removeNodeFromParent(child);

//treeModel.reload();

}

/** Add child to the currently selected node. */

public DefaultMutableTreeNode addObject(Object child) {

DefaultMutableTreeNode parentNode = null;

TreePath parentPath = tree.getSelectionPath();

if (parentPath == null) {

parentNode = rootNode;

} else {

parentNode = (DefaultMutableTreeNode)

(parentPath.getLastPathComponent());

}

return addObject(parentNode, child, true);

}

public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,

Object child) {

return addObject(parent, child, false);

}

public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,

Object child,

boolean shouldBeVisible) {

DefaultMutableTreeNode childNode =

new DefaultMutableTreeNode(child);

if (parent == null) {

parent = rootNode;

}

treeModel.insertNodeInto(childNode, parent,

parent.getChildCount());

// Make sure the user can see the lovely new node.

if (shouldBeVisible) {

tree.scrollPathToVisible(new TreePath(childNode.getPath()));

}

return childNode;

}

class MyTreeModelListener implements TreeModelListener {

public void treeNodesChanged(TreeModelEvent e) {

DefaultMutableTreeNode node;

node = (DefaultMutableTreeNode)

(e.getTreePath().getLastPathComponent());

/*

* If the event lists children, then the changed

* node is the child of the node we've already

* gotten. Otherwise, the changed node and the

* specified node are the same.

*/

try {

int index = e.getChildIndices()[0];

node = (DefaultMutableTreeNode)

(node.getChildAt(index));

} catch (NullPointerException exc) {}

System.out.println("The user has finished editing the node.");

System.out.println("New value: " + node.getUserObject());

}

public void treeNodesInserted(TreeModelEvent e) {

}

public void treeNodesRemoved(TreeModelEvent e) {

}

public void treeStructureChanged(TreeModelEvent e) {

}

}

public DefaultTreeModel getModel() {

return treeModel;

}

}

/*

* Created on Sep 24, 2006

*/

import java.awt.Frame;

import java.awt.Image;

import javax.swing.JComponent;

/**

* Generic Dialog for allowing a user to browse

* his filesystem for an image file. Dialog

* displays a preview of the image (if it is in fact

* displayable).

* @author Josh Feldman

*/

public class ImageChooser extends JComponent implements ImageUpdater{

private Frame parent = null;

public ImageChooser(Frame parent) {

super();

this.parent = parent;

}

public Image getSelectedImage() {

ImageChooserDialog dialog = new ImageChooserDialog(parent);

if (dialog.showDialog() == ImageChooserDialog.OK_OPTION) {

return dialog.getSelectedImage();

}

return null;

}

} // @jve:decl-index=0:visual-constraint="10,10"

/*

* Created on Sep 24, 2006

*/

import java.awt.Frame;

import java.awt.Image;

import java.awt.image.BufferedImage;

import java.io.File;

import java.io.IOException;

import javax.imageio.ImageIO;

import javax.swing.JDialog;

import javax.swing.ImageIcon;

import javax.swing.JFileChooser;

import javax.swing.JPanel;

import javax.swing.JLabel;

import javax.swing.JTextField;

import javax.swing.JButton;

/**

* Class that displays a dialog that allows a user

* to browse his/her filesystem for an image file

* for selection.

* @author Josh Feldman

*/

public class ImageChooserDialog extends JDialog {

private Frame parent = null;

private JFileChooser fileChooser = new JFileChooser();

private int option;

public final static int OK_OPTION = 1;

public final static int CANCEL_OPTION = 2;

private Image selectedImage = null;

private javax.swing.JPanel jContentPane = null;

private JPanel previewPanel = null;

private JLabel jLabel = null;

private JTextField filenameTextField = null;

private JButton browseButton = null;

private JButton cancelButton = null;

private JButton okButton = null;

private JLabel previewLabel = null;

/**

* This is the default constructor

*/

public ImageChooserDialog(Frame parent) {

super();

this.parent = parent;

this.setTitle("Select Image");

initialize();

}

public ImageChooserDialog(Frame parent, String title) {

super();

this.parent = parent;

this.setTitle(title);

initialize();

}

/**

* This method initializes this

*

* @return void

*/

private void initialize() {

this.setModal(true);

this.setSize(377, 246);

this.setContentPane(getJContentPane());

this.setVisible(false);

this.setLocationRelativeTo(parent);

this.addWindowListener(new java.awt.event.WindowAdapter() {

public void windowClosing(java.awt.event.WindowEvent e) {

selectedImage = null;

option = CANCEL_OPTION;

}

});

}

/**

* This method initializes jContentPane

*

* @return javax.swing.JPanel

*/

private javax.swing.JPanel getJContentPane() {

if(jContentPane == null) {

jLabel = new JLabel();

jContentPane = new javax.swing.JPanel();

jContentPane.setLayout(null);

jLabel.setBounds(87, 192, 58, 10);

jLabel.setText("Preview");

jContentPane.add(getPreviewPanel(), null);

jContentPane.add(jLabel, null);

jContentPane.add(getFilenameTextField(), null);

jContentPane.add(getBrowseButton(), null);

jContentPane.add(getCancelButton(), null);

jContentPane.add(getOkButton(), null);

}

return jContentPane;

}

/**

* This method initializes previewPanel

*

* @return javax.swing.JPanel

*/

private JPanel getPreviewPanel() {

if (previewPanel == null) {

previewPanel = new JPanel();

previewLabel = new JLabel();

previewPanel.setLayout(null);

previewPanel.setLocation(25, 62);

previewPanel.setSize(172, 125);

previewPanel.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));

previewLabel.setText("");

previewLabel.setLocation(2, 2);

previewLabel.setSize(172, 125);

previewPanel.add(previewLabel, null);

}

return previewPanel;

}

/**

* This method initializes jTextField

*

* @return javax.swing.JTextField

*/

private JTextField getFilenameTextField() {

if (filenameTextField == null) {

filenameTextField = new JTextField();

filenameTextField.setBounds(26, 17, 212, 23);

filenameTextField.setEditable(false);

}

return filenameTextField;

}

/**

* This method initializes jButton

*

* @return javax.swing.JButton

*/

private JButton getBrowseButton() {

if (browseButton == null) {

browseButton = new JButton();

browseButton.setBounds(254, 18, 102, 21);

browseButton.setText("Browse");

browseButton.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

ImageFilter imageFilter = new ImageFilter();

fileChooser.setFileFilter(imageFilter);

int value = fileChooser.showOpenDialog(ImageChooserDialog.this);

if (value == JFileChooser.APPROVE_OPTION) {

File selected = fileChooser.getSelectedFile();

if (selected.canRead()) {

try {

BufferedImage bi = ImageIO.read(selected);

selectedImage = bi;

Image scaled = bi.getScaledInstance(

previewPanel.getWidth(), previewPanel.getHeight(), BufferedImage.SCALE_FAST);

ImageIcon imageIcon = new ImageIcon(scaled);

previewLabel.setIcon(imageIcon);

filenameTextField.setText(selected.getAbsolutePath());

} catch (IOException e1) {

previewLabel.setText("Preview unavailable...");

selectedImage = null;

e1.printStackTrace();

}

}

}

}});

}

return browseButton;

}

/**

* This method initializes jButton1

*

* @return javax.swing.JButton

*/

private JButton getCancelButton() {

if (cancelButton == null) {

cancelButton = new JButton();

cancelButton.setBounds(254, 122, 100, 24);

cancelButton.setText("Cancel");

cancelButton.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

selectedImage = null;

option = CANCEL_OPTION;

ImageChooserDialog.this.dispose();

}

});

}

return cancelButton;

}

/**

* This method initializes jButton2

*

* @return javax.swing.JButton

*/

private JButton getOkButton() {

if (okButton == null) {

okButton = new JButton();

okButton.setBounds(256, 159, 97, 24);

okButton.setText("OK");

okButton.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

option = OK_OPTION;

ImageChooserDialog.this.dispose();

}

});

}

return okButton;

}

/**

* Displays this chooser dialog.

* @return - The user selected option

* (i.e. OK_OPTION, CANCEL_OPTION)

*/

public int showDialog() {

this.setVisible(true);

return option;

}

/**

* Returns the image chosen by the user.

* @return

*/

public Image getSelectedImage() {

return selectedImage;

}

}

import java.io.File;

import javax.swing.filechooser.FileFilter;

public class ImageFilter extends FileFilter {

//Accept all directories and all gif, jpg, tiff, or png files.

public boolean accept(File f) {

if (f.isDirectory()) {

return true;

}

String extension = Utils.getExtension(f);

if (extension != null) {

if (extension.equals(Utils.tiff) ||

extension.equals(Utils.tif) ||

extension.equals(Utils.gif) ||

extension.equals(Utils.jpeg) ||

extension.equals(Utils.jpg) ||

extension.equals(Utils.png)) {

return true;

} else {

return false;

}

}

return false;

}

//The description of this filter

public String getDescription() {

return "Just Images";

}

}

/*

* Created on Sep 24, 2006

*/

import java.awt.Image;

/**

* Contract that specifies how a class can update

* the view in its graphical display.

* @author Josh Feldman

*/

public interface ImageUpdater {

public Image getSelectedImage();

}

/*

* Created on Nov 4, 2006

*/

import java.awt.Image;

import java.awt.Toolkit;

import java.awt.event.KeyEvent;

import java.awt.image.BufferedImage;

import java.io.File;

import java.io.IOException;

import java.rmi.Naming;

import javax.imageio.ImageIO;

import javax.swing.ImageIcon;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

import javax.swing.JPasswordField;

import javax.swing.JTextField;

public class LoginFrame extends JFrame {

private String serverName = "//Broom:5001/IMServer";

private ServerInter serverInter = null;

// The icon to be used when this frame is minimized

private Image icon;

// The main image to be displayed on this frame

private Image robotImage;

private javax.swing.JPanel jContentPane = null;

private JPanel imagePanel = null;

private JLabel screenameLabel = null;

private JTextField screenameTextField = null;

private JPanel jPanel1 = null;

private JLabel passwordLabel = null;

private JPasswordField passwordTextField = null;

private JButton signonButton = null;

private JButton helpButton = null;

/**

* This is the default constructor

*/

public LoginFrame() {

initialize();

}

/**

* This method initializes this

*

* @return void

*/

private void initialize() {

this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

this.setResizable(false);

this.setSize(210, 368);

this.setContentPane(getJContentPane());

this.setTitle("Sign On");

try {

this.setIconImage(ImageIO.read(new File("images/robby3.jpg")));

} catch (IOException e) {

e.printStackTrace();

}

this.setLocationRelativeTo(null);

this.setVisible(true);

}

/**

* This method initializes jPanel

*

* @return javax.swing.JPanel

*/

private JPanel getImagePanel() {

if (imagePanel == null) {

imagePanel = new JPanel();

imagePanel.setBounds(7, 7, 190, 159);

imagePanel.setBorder(javax.swing.BorderFactory.createLineBorder(java.awt.Color.gray,0));

try {

BufferedImage bi =

ImageIO.read(

getClass().getClassLoader().getResourceAsStream("images/robby_big.bmp"));

Image scaled = bi.getScaledInstance(190, 169, BufferedImage.SCALE_FAST);

JLabel robotLabel = new JLabel(

new ImageIcon(scaled));

imagePanel.add(robotLabel);

} catch (IOException e) {

e.printStackTrace();

}

}

return imagePanel;

}

/**

* This method initializes jTextField

*

* @return javax.swing.JTextField

*/

private JTextField getScreenameTextField() {

if (screenameTextField == null) {

screenameTextField = new JTextField();

screenameTextField.setBounds(22, 208, 168, 20);

screenameTextField.addKeyListener(new java.awt.event.KeyAdapter() {

public void keyTyped(java.awt.event.KeyEvent e) {

if (!isAllowedCharacter(e.getKeyChar())) {

e.consume();

Toolkit.getDefaultToolkit().beep();

}

}

public void keyPressed(java.awt.event.KeyEvent e) {

int keycode = e.getKeyCode();

if(keycode == KeyEvent.VK_ENTER && signonButton.isEnabled()) {

signonButton.doClick();

passwordTextField.setText("");

}

else if (keycode == KeyEvent.VK_ESCAPE) {

dispose();

System.exit(0);

}

}

public void keyReleased(java.awt.event.KeyEvent e) {

String screename = screenameTextField.getText();

char [] password = passwordTextField.getPassword();

if (screename.equals("") ||

password.length <= 0) {

signonButton.setEnabled(false);

}

else if (!screename.equals("") &&

password.length > 0) {

signonButton.setEnabled(true);

}

clearPasswordArray(password);

}

});

}

return screenameTextField;

}

/**

* This method initializes jPanel1

*

* @return javax.swing.JPanel

*/

private JPanel getJPanel1() {

if (jPanel1 == null) {

jPanel1 = new JPanel();

jPanel1.setBounds(8, 173, 188, 1);

jPanel1.setBorder(javax.swing.BorderFactory.createLineBorder(java.awt.Color.gray,5));

}

return jPanel1;

}

/**

* This method initializes jPasswordField

*

* @return javax.swing.JPasswordField

*/

private JPasswordField getPasswordTextField() {

if (passwordTextField == null) {

passwordTextField = new JPasswordField();

passwordTextField.setBounds(20, 259, 170, 20);

passwordTextField.addKeyListener(new java.awt.event.KeyAdapter() {

public void keyTyped(java.awt.event.KeyEvent e) {

if (!isAllowedCharacter(e.getKeyChar())) {

e.consume();

Toolkit.getDefaultToolkit().beep();

}

}

public void keyPressed(java.awt.event.KeyEvent e) {

int keycode = e.getKeyCode();

if(keycode == KeyEvent.VK_ENTER && signonButton.isEnabled()) {

signonButton.doClick();

passwordTextField.setText("");

}

else if (keycode == KeyEvent.VK_ESCAPE) {

dispose();

System.exit(0);

}

}

public void keyReleased(java.awt.event.KeyEvent e) {

String screename = screenameTextField.getText();

char [] password = passwordTextField.getPassword();

if (screename.equals("") ||

password.length <= 0) {

signonButton.setEnabled(false);

}

else if (!screename.equals("") &&

password.length > 0) {

signonButton.setEnabled(true);

}

clearPasswordArray(password);

}

});

}

return passwordTextField;

}

/**

* This method initializes jContentPane

*

* @return javax.swing.JPanel

*/

private javax.swing.JPanel getJContentPane() {

if(jContentPane == null) {

passwordLabel = new JLabel();

screenameLabel = new JLabel();

jContentPane = new javax.swing.JPanel();

jContentPane.setLayout(null);

screenameLabel.setBounds(22, 182, 132, 20);

screenameLabel.setText("Screename");

screenameLabel.setEnabled(true);

screenameLabel.setFont(new java.awt.Font("Century Gothic", java.awt.Font.BOLD, 12));

passwordLabel.setBounds(21, 238, 135, 17);

passwordLabel.setText("Password");

jContentPane.add(getImagePanel(), null);

jContentPane.add(screenameLabel, null);

jContentPane.add(getScreenameTextField(), null);

jContentPane.add(getJPanel1(), null);

jContentPane.add(passwordLabel, null);

jContentPane.add(getPasswordTextField(), null);

jContentPane.add(getSignonButton(), null);

jContentPane.add(getJButton1(), null);

}

return jContentPane;

}

/**

* This method initializes jButton

*

* @return javax.swing.JButton

*/

private JButton getSignonButton() {

if (signonButton == null) {

signonButton = new JButton();

signonButton.setBounds(105, 294, 81, 26);

signonButton.setText("Sign On");

signonButton.setEnabled(false);

signonButton.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

if (!screenameTextField.getText().equals("") && !passwordTextField.getPassword().equals("")) {

try {

serverInter = (ServerInter) Naming.lookup(serverName);

String username = screenameTextField.getText();

String password = String.valueOf(passwordTextField.getPassword());

int connectionID = serverInter.connect(username, password);

System.out.println("authenticate successful");

dispose();

Buddylist buddyList = new Buddylist(username, connectionID);

serverInter.registerCallback(buddyList, connectionID);

return;

} catch (Exception e1) {

JOptionPane.showMessageDialog(LoginFrame.this, e1,

"Connection Error", JOptionPane.ERROR_MESSAGE);

passwordTextField.setText("");

signonButton.setEnabled(false);

e1.printStackTrace();

}

}

}

});

}

return signonButton;

}

/**

* This method initializes jButton1

*

* @return javax.swing.JButton

*/

private JButton getJButton1() {

if (helpButton == null) {

helpButton = new JButton();

helpButton.setBounds(21, 295, 63, 25);

helpButton.setText("Help");

helpButton.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

String helpMessage = "Question: Why can't I enter certain characters?\n\n" +

"Answer: Josh's IM only let's you enter valid characters\n" +

"for both your screename and your password. If a specific\n" +

"character isn't showing up in the text box, it's because\n" +

"the character is invalid.";

JOptionPane.showMessageDialog(

LoginFrame.this, helpMessage, "Help", JOptionPane.INFORMATION_MESSAGE);

}

});

}

return helpButton;

}

/**

* Returns true if the character corresponding

* to the specified keycode is acceptable for

* either screenames or passwords, false

* otherwise.

* @param keycode

* @return

*/

private boolean isAllowedCharacter(int keycode) {

if (keycode >= KeyEvent.VK_0 &&

keycode <= KeyEvent.VK_Z) {

System.out.println("allowed");

return true;

}

if (keycode == KeyEvent.VK_EXCLAMATION_MARK ||

keycode == KeyEvent.VK_AMPERSAND ||

keycode == KeyEvent.VK_NUMBER_SIGN ||

keycode == KeyEvent.VK_DOLLAR ||

keycode == KeyEvent.VK_ASTERISK ||

keycode == KeyEvent.VK_CIRCUMFLEX ||

keycode == KeyEvent.VK_UNDERSCORE ||

keycode == KeyEvent.VK_LEFT_PARENTHESIS ||

keycode == KeyEvent.VK_RIGHT_PARENTHESIS) {

System.out.println("allowed");

return true;

}

System.out.println(keycode);

System.out.println("not allowed");

return false;

}

/**

* Determines whether or not the given character is

* valid for the input textfields.

* @param c - A character to test for validity.

* @return - true if the given character is valid,

* false otherwise.

*/

private boolean isAllowedCharacter(char c) {

// Valid characters, separated by commas

// &, (, ), *, !, ^, Backspace, Delete

// Carriage return, _, english letters and numbers

if (c == 38 || c == 40 || c == 41 || c == 42 || c == 33 ||

c == 94 || c == 8 || c == 13 || c == 95 ||

(c >= 48 && c <= 57) ||

// Uppercase letters

(c >= 65 && c <= 90) ||

// Lowercase letters

(c >= 97 && c <= 122)) {

return true;

}

return false;

}

/**

* Used for security after each call to getPassword()

* on the password text box.

*/

private void clearPasswordArray(char [] password) {

if (password.length <= 0) {

return;

}

for (int i = 0; i < password.length; i++) {

password[i] = 0;

}

password = null;

}

public static void main(String[] args) {

new LoginFrame();

}

}

/*

* Created on Nov 4, 2006

*/

import java.io.IOException;

import java.net.MalformedURLException;

import java.rmi.Naming;

import java.rmi.RemoteException;

import java.rmi.registry.LocateRegistry;

import java.rmi.server.UnicastRemoteObject;

import java.util.HashSet;

import java.util.Iterator;

import java.util.Vector;

/**

* The Main server that will manage chat sessions and authentication

* between clients.

*/

public class Server extends UnicastRemoteObject implements ServerInter, Runnable {

private Database database = null;

private int maxNumConnections = 1000;

private HashSet availableConnectionIDs = new HashSet();

private Vector loggedInUsers = new Vector();

protected Server() throws RemoteException {

super();

try {

database = new Database();

generateConnectionIDs();

startServer();

} catch (IOException e) {

e.printStackTrace();

}

}

private void startServer() {

String serverName = "//Broom:5001/IMServer";

try {

LocateRegistry.createRegistry(5001);

Naming.rebind(serverName, this);

Thread t = new Thread(this);

t.start();

} catch (RemoteException e) {

e.printStackTrace();

} catch (MalformedURLException e) {

e.printStackTrace();

}

}

/**

* Simply used right now to keep the server thread

* running.

*/

public void run() {

while (true) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public static void main(String[] args) throws RemoteException {

new Server();

}

public int connect(String username, String password) throws RemoteException, ServerException {

System.out.println("in authenticate");

int connectionID;

try {

if (database.doesUserExist(username) &&

database.getPassword(username).equals(password)) {

connectionID = getConnectionID();

Connection connection = new Connection(username, connectionID);

loggedInUsers.add(connection);

fireBuddyLoggedOnEvent(username);

return connectionID;

}

else {

throw new ServerException("Username and/or password incorrect!");

}

} catch (RemoteException e) {

e.printStackTrace();

throw e;

} catch (ServerException e) {

e.printStackTrace();

throw new ServerException("Problem connecting to server");

}

}

public void disconnect(String username, int connectionID) throws RemoteException {

for (int i = 0; i < loggedInUsers.size(); i++) {

Connection c = (Connection) loggedInUsers.get(i);

if (c.getUsername().equals(username) && c.getConnectionID() == connectionID) {

// Put this connection id back in the pool of available

// ids

availableConnectionIDs.add(new Integer(connectionID));

loggedInUsers.remove(i);

fireBuddyLoggedOffEvent(username);

return;

}

}

}

/**

* Populates the list of available connections IDs.

*/

private void generateConnectionIDs() {

for (int i = 0; i < maxNumConnections; i++) {

availableConnectionIDs.add(new Integer(i));

}

}

/**

* Returns a unique connection id from a pool of

* available connection ids. After a call to this method,

* the returned id will be removed from the available

* list of ids.

*/

public int getConnectionID() throws RemoteException, ServerException {

Iterator iter = availableConnectionIDs.iterator();

if (iter.hasNext()) {

int connectionID = ((Integer) iter.next()).intValue();

iter.remove();

return connectionID;

}

else {

throw new ServerException("No Connection IDs available!");

}

}

/**

* Inform all logged in users that are buddies with user named screename

* that this user just logged in. This will allow the users to

* update their buddylists to reflect the new login.

* @param screenname

*/

private void fireBuddyLoggedOnEvent(String screenname) {

for (int i = 0; i < loggedInUsers.size(); i++) {

Connection c = (Connection) loggedInUsers.get(i);

if (database.areBuddies(screenname, c.getUsername())) {

try {

c.getCallback().buddySignedOn(screenname);

} catch (RemoteException e) {

e.printStackTrace();

}

}

}

}

/**

* Inform all logged in users that are buddies with user named screename

* that this user just logged in. This will allow the users to

* update their buddylists to reflect the new login.

* @param screenname

*/

private void fireBuddyLoggedOffEvent(String screename) {

for (int i = 0; i < loggedInUsers.size(); i++) {

Connection c = (Connection) loggedInUsers.get(i);

if (database.areBuddies(screename, c.getUsername())) {

try {

c.getCallback().buddySignedOff(screename);

} catch (RemoteException e) {

e.printStackTrace();

}

}

}

}

public void registerCallback(Buddylist buddylist, int connectionID) throws RemoteException {

Connection c = new Connection(connectionID);

if (!loggedInUsers.contains(c)) {

throw new RemoteException("Problem registering callback");

}

int index = loggedInUsers.indexOf(c);

Connection c2 = (Connection) loggedInUsers.get(index);

c2.setCallback(buddylist);

}

public String[] getBuddyList(String username) throws RemoteException {

return database.getUserBuddies(username);

}

public boolean isBuddyOnline(String username) throws RemoteException {

for (int i = 0; i < loggedInUsers.size(); i++) {

Connection c = (Connection) loggedInUsers.get(i);

if (c.getUsername().equals(username)) {

return true;

}

}

return false;

}

}

/*

* Created on Sep 30, 2006

*/

/**

* Class that indicates that an Exception has occured

* on the server side.

* @author Josh Feldman

*/

public class ServerException extends Exception {

public ServerException(String message) {

super(message);

}

}

/*

* Created on Nov 4, 2006

*/

import java.rmi.RemoteException;

public interface ServerInter extends java.rmi.Remote {

public void registerCallback(Buddylist buddylist, int connectionID) throws RemoteException;

/**

* Attempts to establish a "logged in" status with the server

* (i.e. the successful creation of an ID with the server.

* Returns a connection id for the authenticated client upon

* success.

*/

public int connect(String username, String password)

throws RemoteException, ServerException;

public void disconnect(String username, int connectionID) throws RemoteException;

public String[] getBuddyList(String username) throws RemoteException;

public boolean isBuddyOnline(String username) throws RemoteException;

}

import java.io.File;

import javax.swing.ImageIcon;

public class Utils {

public final static String jpeg = "jpeg";

public final static String jpg = "jpg";

public final static String gif = "gif";

public final static String tiff = "tiff";

public final static String tif = "tif";

public final static String png = "png";

/*

* Get the extension of a file.

*/

public static String getExtension(File f) {

String ext = null;

String s = f.getName();

int i = s.lastIndexOf('.');

if (i > 0 && i < s.length() - 1) {

ext = s.substring(i+1).toLowerCase();

}

return ext;

}

/** Returns an ImageIcon, or null if the path was invalid. */

protected static ImageIcon createImageIcon(String path) {

java.net.URL imgURL = Utils.class.getResource(path);

if (imgURL != null) {

return new ImageIcon(imgURL);

} else {

System.err.println("Couldn't find file: " + path);

return null;

}

}

}

java_sunya at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...
# 7
Forget it, I'm not going to read all that. The exception means that something is out of date between your remote interfaces, your stubs, and your servers.When you deleted all .class files and recompiled did you also restart the Registry? and all other JVMs?
ejpa at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...
# 8

I didn't really expect you to read the while thing ejp. Just figured I put it out there for everyone in case someone has an idea what the problem could be. The registry is implicitly started every time the server is run because I use LocateRegistry.createRegistry(5001). At any rate, I give up as far as these forums are concerned, but it's not your fault ejp- it's RMI's fault for being such a cryptic system. Thank you for taking all the time that you have to look at my problem.

java_sunya at 2007-7-15 5:23:37 > top of Java-index,Core,Core APIs...