Java RMI - start chat

I have created interfaces and implementations for Chat Server, Client, and Message. I compiled all the Java files. Then I compiled implementation classes using rmic to create stubs. I started the rmi registry. I started the server implementation and it works as expected; confirming that he registered with RMI and he returns a list of active clients. I am now stuck as when I try to run a client implementation if it is below the command

start java ChatClientImpl

the command field appears within a second and disappears. I do not know how to make the client work.

Does anyone have any suggestions on how to get the client to work and work?

Here is the complete code for the application.

Chat client interface

   package p;
    /**
     * Defines the remote interface to be used by chat clients. A client
     * implementation must support all of the methods below. 
     */
    public interface ChatClient extends java.rmi.Remote {
        /**
         * Process a newly received message. 
         * 
         * @param inMessage
         * @throws java.rmi.RemoteException
         */
        public void processMessage(MessageImpl inMessage) throws    
        java.rmi.RemoteException;
        /**
         * Simple empty method which can be called by the server 
         * and other clients to verify that the client is still 
         * operating. 
         * 
         * @throws java.rmi.RemoteException
         */
        public void ping() throws java.rmi.RemoteException;
        /**
         * Returns the name of the client. 
         */
        public String getName() throws java.rmi.RemoteException;
    }

Chat client implementation

package p;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

/**
 * The client implements the ChatClient and the LocalClient interfaces.
 * 
 * A chat client is responsible for relaying on a message it has received from the 
 * user or from a different peer to all other peers that it knows about. 
 * 
 * The client should try to reduce the number of pointless relays of the message. 
 * Minimally if it has already received the message, it should not pass the message
 * on further. 
 */
public class ChatClientImpl extends UnicastRemoteObject implements ChatClient{

    private static final long serialVersionUID = 3056117484716596895L;

    private ChatServer listServer = null;

    private ArrayList<ChatClient> knownClients = null; 

    private String clientName = null; 

    private ArrayList<GUID> processedMessages = null; 

    private JChatClient gui = null; 

    public ChatClientImpl(String inServerName,                        
                          String inClientName) throws Exception{
        // set the client name
        this.clientName = inClientName; 

            // connect to chat server through RMI
        this.listServer = (ChatServer) Naming.lookup(inServerName);

        // create the empty list of processed messages
        this.processedMessages = new ArrayList<GUID>(); 

        // register with the ChatServer
        this.listServer.register(this);

        // request a set of clients.
        ChatClient[] newClients = this.listServer.getClients();

        // add the new set to our known set.
        this.knownClients = new ArrayList<ChatClient>(); 
        this.addClients(newClients);
    }

    protected void setGUI(JChatClient inGUI){
        this.gui = inGUI; 
    }

    /**
     * Add a list of clients to our known clients. 
     * 
     * @param inClients
     */
    private void addClients(ChatClient[] inClients){
        for(int i = 0; i < inClients.length; i++){
            if(!this.knownClients.contains(inClients[i])){
                this.knownClients.add(inClients[i]);
            }
            else{
                System.err.println("already know that peer - will not add.");
            }
        }
    }

    public void handleMessage(String inString){
        // create a message instance
        MessageImpl msg = new MessageImpl(this,inString);

        // broadcast to all clients
        Iterator<ChatClient> i = this.knownClients.iterator();
        while(i.hasNext()){
            ChatClient knownClient = i.next();
            try {
                knownClient.processMessage(msg);    
            } catch (RemoteException e) {
                // looks like the client isn't there any longer. 
                i.remove();
            }
        }       
    }

    /**
     * Process a newly received message. 
     * 
     * @param inMessage
     * @throws java.rmi.RemoteException
     */
    public void processMessage(MessageImpl inMessage) throws java.rmi.RemoteException{
        System.out.println("message received");

        // check the message to see if we have processed it before - discard if so
        if(this.processedMessages.contains(inMessage.id)) return;
        else{
            System.err.println(inMessage.id);
            this.processedMessages.add(inMessage.id);
            System.err.println("have now processed " + this.processedMessages.size());
        }

        // if we have not displayed it, then notify our viewer
        this.gui.updateDisplay(inMessage);;

        // get all new known clients from the message
        HashSet<ChatClient> recipients = inMessage.getRecipients();

        // add the names of any new recipients to known list
        for (ChatClient chatClient : recipients) {
            if(knownClients.contains(chatClient)){
                System.out.println("already know about that client");
            }
            else{
                System.out.println("discovered a new recipient");
                this.knownClients.add(chatClient);
            }
        }

        // pass the message on to any clients who have not already seen the message
        Iterator<ChatClient> iter = this.knownClients.iterator();
        while(iter.hasNext()){
            ChatClient chatClient = iter.next(); 
            if(recipients.contains(chatClient)){
                System.err.println("aready sent to that client");
            }
            else{
                System.out.println("sending to a new client");
                try {
                    chatClient.processMessage(inMessage);   
                } catch (RemoteException e) {
                    // looks like the client isn't there any longer. 
                    iter.remove();
                }
            }
        }
    }

    /**
     * Simple empty method which can be called by the server 
     * and other clients to verify that the client is still 
     * operating. 
     * 
     * @throws java.rmi.RemoteException
     */
    public void ping() throws java.rmi.RemoteException{
        /* doesn't have to do anything */
    }

    /**
     * Get the client name. 
     */
    public String getName() throws java.rmi.RemoteException{
        return this.clientName; 
    }
}

Chat server interface

package p;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ChatServer extends Remote {

    /**
     * Register the ChatClient instance with the ChatServer. 
     * @param inClient
     */
    public abstract void register(ChatClient inClient) throws RemoteException;

    /**
     * Request a set of clients from the chat server. 
     */
    public abstract ChatClient[] getClients() throws RemoteException;
}

Chat Server Implementation

package p;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * A chat server allows chat clients to locate other chat clients. 
 * The chat server is not used during message broadcast. It is 
 * only used when the chat clients initially connect to the chat session.
 * 
 * Communication with the server (as with all other modules) will be 
 * conducted with RMI rather than a custom TCP or UDP protocol. 
 * 
 * Each client only communicates with a small set of peers. The 
 * ChatServer is responsible for deciding upon the set of peers.   
 * 
public class ChatServerImpl extends UnicastRemoteObject implements ChatServer, Runnable{

    private static final long serialVersionUID = 1344922080333520010L;
    private static final int RETURN_SET_SIZE = 3;
    private static final int PING_SLEEP_DURATION = 60000;

    /**
     * Hashtable of all registered remote clients. 
     */
    private List<ChatClient> clients = null; 

    /**
     * Chat server is started once on a server. Students will have to 
     * start an instance themselves. If we were to have a number of 
     * students working together, one server would be started central.
     */
    public ChatServerImpl() throws RemoteException{
        this.clients = new ArrayList<ChatClient>(); 
    }

    /* (non-Javadoc)
     * @see p.ChatServer#register(p.ChatClient)
     */
    @Override
    public void register(ChatClient inClient) throws RemoteException{
        if(!this.clients.contains(inClient)) this.clients.add(inClient);
    }

    /* (non-Javadoc)
     * @see p.ChatServer#getClients()
     */
    @Override
    public synchronized ChatClient[] getClients() throws RemoteException{
        if(this.clients.size() > RETURN_SET_SIZE){
            List<ChatClient> clientSample = randomSample2(this.clients, RETURN_SET_SIZE);
            return (ChatClient[]) this.clients.toArray( new ChatClient[ this.clients.size() ] );
        }
        else return (ChatClient[]) this.clients.toArray( new ChatClient[ this.clients.size() ] );
    }

    /**
     * Generate the random subset. Based on an implementation of D. Knuth 
     * algorithm. 
     * 
     * @param <T>
     * @param items
     * @param m
     * @return
     */
    private static <T> List<T> randomSample2(List<T> items, int m){
        Random rnd = new Random();
        for(int i=0;i<items.size();i++){
            int pos = i + rnd.nextInt(items.size() - i);
            T tmp = items.get(pos);
            items.set(pos, items.get(i));
            items.set(i, tmp);
        }
        return items.subList(0, m);
    }   

    /**
     * Run the server main thread. The server will periodically 
     * iterate through all registered clients to find out if they 
     * are still alive. Any dead clients will be removed from the 
     * client list. 
     */
    public void run(){
        while(true){
            try {Thread.sleep(PING_SLEEP_DURATION);} catch (Exception e) {}
            System.out.println("Performing Update");
            // we don't want to lock the list, so we will make a copy of it
            // for the checking phase. 
            ArrayList<ChatClient> copy = new ArrayList<ChatClient>(this.clients);
            System.out.println("Checking " + copy.size() + " clients");
            for (ChatClient chatClient : copy) {
                try {
                    chatClient.ping();  
                } catch (RemoteException e) {
                    System.out.println("Removing a client.");
                    // client is no longer accessible. 
                    // We will remove the client from the main list
                    this.clients.remove(chatClient);
                }                
            }
        }
    }

    /**
     * Start the chat server. 
     * @param args
     */
    public static void main(String[] args) throws Exception{
        // create an instance of the chat server
        ChatServer server = new ChatServerImpl();

        // register the instance with the RMIRegistry
        Naming.rebind("rmi://localhost/chat", server);
        System.out.println("Registration with RMI complete.");

        // create the server thread and start it    
        Thread th = new Thread(((ChatServerImpl)server));
        th.start();
        System.out.println("Server thread now running.");
    }
}

Message interface

package p;

import java.util.HashSet;

public interface Message {

    /**
     * Add a chat recipient to the list of receivers
     * @param inClient
     */
    public abstract void addRecipient(ChatClient inClient);

    /**
     * Get the set of clients that have seen this message
     * @return
     */
    public abstract HashSet<ChatClient> getRecipients();

    /**
     * Get the message content.  
     * @return
     */
    public abstract String getContent();


    public abstract String getSource();
}

package p;

import java.util.HashSet;

/**
 * Must have some unique identifier. Peer + number.  
 */
public class MessageImpl implements java.io.Serializable, Message {

    /**
     * Generated versionaID
     */
    private static final long serialVersionUID = 8914588083609635659L;

    /** 
     * The globally unique identifier for this message 
     */
    public final GUID id;

    /** 
     * All remote clients that this message has passed through,
     * including source 
     */
    public final HashSet<ChatClient> passedThrough = new HashSet<ChatClient>();

    /**
     * The content of the message. 
     */
    private String content = null;

    /**
     * The client who created the object
     */
    private ChatClient client = null; 

    /** 
     * Create a new Message instance. 
     */
    public MessageImpl(ChatClient s, String inContent) {
        this.id = new GUID(s);
        this.client = s; 
        this.passedThrough.add(s);
        this.content = inContent; 
    }

    /* (non-Javadoc)
     * @see p.MessageInterface#addRecipient(p.ChatClient)
     */
    @Override
    public void addRecipient(ChatClient inClient){
        this.passedThrough.add(inClient);
    }

    /* (non-Javadoc)
     * @see p.MessageInterface#getRecipients()
     */
    @Override
    public HashSet<ChatClient> getRecipients(){
        return this.passedThrough;
    }

    /* (non-Javadoc)
     * @see p.MessageInterface#getContent()
     */
    @Override
    public String getContent(){
        return this.content; 
    }

    /**
     * Get the name of the source of the message
     * @return
     */
    public String getSource(){
        try {
            return this.client.getName();   
        } catch (Exception e) {
            return "client uncreachable";
        }
    }
}

    /** 
     * Class used to generate globally unique identifiers for each message.
     * 
     * The GUID is based on the client plus an increment for each new
     * message instance. 
     */
    class GUID implements java.io.Serializable {
        /**
         * Generated versionaID
         */
        static final long serialVersionUID = 4928185858035458591L;

        /**
         * Reference to the client -- needed to generate a number. 
        */
        public final ChatClient source;

        /**
         * Next number that should be used - defined statically 
         */
        private static int nextID;

        /**
         * The message number to be used in the current GUID
         */
        private int id = 0;

        /**
         * Generate a GUID for use by a client. 
         * @param s
         */
        public GUID(ChatClient s) {
            this.source = s;
            synchronized(GUID.class) {
              this.id = nextID++;
            }
        }

          public final int hashCode() {
              return source.hashCode() + id;
          }

          public final boolean equals(Object that) {
              if (!(that instanceof GUID)) return false;
              GUID m2 = (GUID)that;
              return id == m2.id && source.equals(m2.source);
        }
   }

GUI

package p;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.rmi.RemoteException;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class JChatClient extends JFrame implements java.awt.event.ActionListener{

    private ChatClientImpl model = null;

    /**
     * Field for users to enter text. 
     */
    protected JTextField inputField = null;

    /**
     * Text area where user and system text is printed out. 
     */
    private JTextPane historyPane = null;   

    /**
     * 
     * @param name
     * @param host
     * @param mname
     * @throws RemoteException
     */
    public JChatClient(ChatClient inClient, String inUserName) throws RemoteException {
        super("Chat: " + inUserName);
        this.model = (ChatClientImpl) inClient;
        this.model.setGUI(this);

        super.setLayout(new BorderLayout());

        // create the main output panel    
        this.historyPane = new JTextPane();
        this.historyPane.setAutoscrolls(true);
        this.historyPane.setEditable(false);         
        this.historyPane.setPreferredSize(new Dimension(100,100));

        // create a scrollpane to put the output panel in. This will then be added to the parent container.
        JScrollPane historyView = new JScrollPane(historyPane);        
        historyView.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        historyView.setAutoscrolls(true); 
        super.add(historyView, BorderLayout.CENTER);

        // create and add the input field
        this.inputField = new JTextField("", 20);
        this.inputField.addActionListener(this);
        JPanel bottom = new JPanel();
        bottom.setLayout(new BoxLayout(bottom,BoxLayout.X_AXIS));
        bottom.add(inputField);
        super.add(bottom, BorderLayout.SOUTH);          

        this.centerAndSetVisible();
    }

    /**
     * Updates the display with details of a new message. 
     * @param inMessage
     * @throws IOException
     */
    public void updateDisplay(MessageImpl inMessage) {
        this.printText("\n" + inMessage.getSource() + ": " + inMessage.getContent(),Color.BLACK);
    }

    /**
     * Called when the 'send' button has been hit. 
     * 
     * Must be extended to update all clients with information on the text that 
     * was entered by the user. 
     */
    public void actionPerformed(ActionEvent e) {
        String msg = inputField.getText();
        if (msg.length() > 0) {
            // Clear the chat input field
            inputField.setText("");

            // update (directly or indirectly) any peers of the message update.
            this.model.handleMessage(msg);
        }
    }

    /**
     * Utility method for doing the work of printing a message in a 
     * given colour to the output window. 
     * 
     * @param inText
     * @param inColor
     */
    private void printText(String inText, Color inColor){
        StyledDocument doc = historyPane.getStyledDocument();
        SimpleAttributeSet attr = new SimpleAttributeSet();
        StyleConstants.setForeground(attr, inColor);

        try {
            doc.insertString(doc.getLength(),inText,attr);      
            historyPane.setCaretPosition(doc.getLength()); 
        } catch (Exception ex) {
            ex.printStackTrace(); 
        }       
    }           

    /**
     * Put everything in the right place. 
     */
    private void centerAndSetVisible(){
        this.pack(); 
        this.setPreferredSize(new Dimension(300, 300));
        Dimension labelSize = this.getPreferredSize();
        Dimension sd = Toolkit.getDefaultToolkit().getScreenSize();
        int x = ( sd.width - labelSize.width )/2;
        int y = ( sd.height - labelSize.height )/2;
        this.setLocation(x, y);
        setSize(new Dimension(labelSize.width, labelSize.height));
        this.setVisible(true);
        this.setAlwaysOnTop(false); 
     }    

    public static void main(String[] args) throws Exception{
        // find address of chat name server
        ChatClientImpl client = new ChatClientImpl(args[0], args[1]);
        new JChatClient(client,args[1]);
    }       
}
+4
2
start java ChatClientImpl

start. , - . . JChatClientImpl.

0
, . rmi- :
start java JChatClient rmi://localhost/chat User1

. .

-1

Source: https://habr.com/ru/post/1584025/


All Articles