Java RMI - Starting a Chat Client - java

I have created interfaces and implementations for a Chat Server, Client and Message system. I have compiled all the Java files. I have then compiled my Implementation classes using rmic to create stubs. I have started the rmi registry. I have started the Server Implementation and it is running as expected; confirming that it has registered with RMI and it is returning a list of active clients.
I am now stuck as when i try to run the Client Implementation witht he below command
start java ChatClientImpl
a command box appears for a split second and disappears. I have no idea to get the client to run.
Would anyone have any suggestions as to how to get the client up and running?
Here is the full 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's
* 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's 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();
}
Message Implementation
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]);
}
}

start java ChatClientImpl
The way to debug this is to remove the start part. When you do that you will see an exception about no main method. You're starting the wrong class. The main method is in JChatClientImpl.

Finally realised what I was doing wrong. After starting the rmi registry and starting the server I tried running the below from the command line:
start java JChatClient rmi://localhost/chat User1
Chat app is now working. Thanks for all the suggestions.

Related

how to add a list of all the clients connected on a chat application in Java?

I found this Client-Server chat code online but would like to modified it to show the list of clients connected to to each client this way users know who is connected and who has left the chat.
client class:
package edu.lmu.cs.networking;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
*
* #author PUTA
* The client follows the Chat Protocol which is as follows.
* When the server sends "SUBMITNAME" the client replies with the
* desired screen name. The server will keep sending "SUBMITNAME"
* requests as long as the client submits screen names that are
* already in use. When the server sends a line beginning
* with "NAMEACCEPTED" the client is now allowed to start
* sending the server arbitrary strings to be broadcast to all
* chatters connected to the server. When the server sends a
* line beginning with "MESSAGE " then all characters following
* this string should be displayed in its message area.
*/
public class ChatClient {
BufferedReader in;
PrintWriter out;
JFrame frame = new JFrame("InstantMessenger");
JTextField textField = new JTextField(40);
JTextArea messageArea = new JTextArea(8, 40);
/**
* Constructs the client by laying out the GUI and registering a
* listener with the textfield so that pressing Return in the
* listener sends the textfield contents to the server. Note
* however that the textfield is initially NOT editable, and
* only becomes editable AFTER the client receives the NAMEACCEPTED
* message from the server.
*/
public ChatClient() {
// Layout GUI
textField.setEditable(false);
messageArea.setEditable(false);
frame.getContentPane().add(textField, "North");
frame.getContentPane().add(new JScrollPane(messageArea), "Center");
frame.pack();
// Add Listeners
textField.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
public void actionPerformed(ActionEvent e) {
out.println(textField.getText());
textField.setText("");
}
});
}
/**
* Prompt for and return the address of the server.
*/
private String getServerAddress() {
return JOptionPane.showInputDialog(
frame,
"Enter IP Address of the Server:",
"Welcome to the Chatter",
JOptionPane.QUESTION_MESSAGE);
}
/**
* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
/**
* Connects to the server then enters the processing loop.
*/
private void run() throws IOException {
// Make connection and initialize streams
String serverAddress = getServerAddress();
Socket socket = new Socket(serverAddress, 9001);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(getName());
} else if (line.startsWith("NAMEACCEPTED")) {
textField.setEditable(true);
} else if (line.startsWith("MESSAGE")) {
messageArea.append(line.substring(8) + "\n");
}
}
}
/**
* Runs the client as an application with a closeable frame.
*/
public static void main(String[] args) throws Exception {
ChatClient client = new ChatClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}
server class:
package edu.lmu.cs.networking;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
/**
*
*
*/
public class ChatServer {
/**
* The port that the server listens on.
*/
private static final int PORT = 9001;
/**
* The set of all names of clients in the chat room. Maintained
* so that we can check that new clients are not registering name
* already in use.
*/
private static HashSet<String> names = new HashSet<String>();
/**
* The set of all the print writers for all the clients. This
* set is kept so we can easily broadcast messages.
*/
private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>();
/**
* The appplication main method, which just listens on a port and
* spawns handler threads.
*/
public static void main(String[] args) throws Exception {
System.out.println("The chat server is running.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
/**
* A handler thread class. Handlers are spawned from the listening
* loop and are responsible for a dealing with a single client
* and broadcasting its messages.
*/
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run() {
try {
// Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Request a name from this client. Keep requesting until
// a name is submitted that is not already used. Note that
// checking for the existence of a name and adding the name
// must be done while locking the set of names.
while (true) {
out.println("SUBMITNAME");
name = in.readLine();
if (name == null) {
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}
// Now that a successful name has been chosen, add the
// socket's print writer to the set of all writers so
// this client can receive broadcast messages.
out.println("NAMEACCEPTED");
writers.add(out);
// Accept messages from this client and broadcast them.
// Ignore other clients that cannot be broadcasted to.
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
} finally {
// This client is going down! Remove its name and its print
// writer from the sets, and close its socket.
if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}
My approach will be, to introduce a command like :WHO. When client send :WHO to the server, the server will respond list of user's connected.
Something like (on the server class) :
String input = in.readLine();
if (input == null) {
return;
} else if(input.equals(":WHO")) {
out.println(names.toString()); // ... or format it nicer.
} else {
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
This way, the client can ask "who's login/online" any time. Or you can send list of names when the client first established a connection, and send only names who joins or parts the chat.

Sending to All vs Sending to One Person in a Server/Client program

I have two programs that I'll post below, one is a Server and the other is a Client. The ChatServer runs once and many clients can run ChatClient from separate computers and talk together in their GUI's. The the client program comes with two buttons, one that's simulates the sending of a message to a single user ; "Send Message To User", and one that just generally sends a message ; "Send Message To All". Although now the Clients on the server seem to be able to selectively send messages to an individual by giving the recipient name, all the clients can see that message, which is not exact what I am aiming for. What I am going for is having the "Send Message To User" button click to send a message to the named user without all other users seeing the message.
The Server Program Code
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
/**
* A multithreaded chat room server. When a client connects the
* server requests a screen name by sending the client the
* text "SUBMITNAME", and keeps requesting a name until
* a unique one is received. After a client submits a unique
* name, the server acknowledges with "NAMEACCEPTED". Then
* all messages from that client will be broadcast to all other
* clients that have submitted a unique screen name. The
* broadcast messages are prefixed with "MESSAGE ".
*
*/
public class ChatServer {
/**
* The port that the server listens on.
*/
private static final int PORT = 9001;
/**
* The set of all names of clients in the chat room. Maintained
* so that we can check that new clients are not registering name
* already in use.
*/
private static ArrayList<String> names = new ArrayList<String>();
/**
* The set of all the print writers for all the clients. This
* set is kept so we can easily broadcast messages.
*/
private static ArrayList<PrintWriter> writers = new ArrayList<PrintWriter>();
/**
* The appplication main method, which just listens on a port and
* spawns handler threads.
*/
public static void main(String[] args) throws Exception {
System.out.println("The chat server is running.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
/**
* A handler thread class. Handlers are spawned from the listening
* loop and are responsible for a dealing with a single client
* and broadcasting its messages.
*/
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run() {
try {
// Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Request a name from this client. Keep requesting until
// a name is submitted that is not already used. Note that
// checking for the existence of a name and adding the name
// must be done while locking the set of names.
while (true) {
out.println("SUBMITNAME");
name = in.readLine();
if (name == null) {
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}
// Now that a successful name has been chosen, add the
// socket's print writer to the set of all writers so
// this client can receive broadcast messages.
out.println("NAMEACCEPTED");
writers.add(out);
// Accept messages from this client and broadcast them.
// Ignore other clients that cannot be broadcasted to.
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
} finally {
// This client is going down! Remove its name and its print
// writer from the sets, and close its socket.
if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}
The Client Program Code
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.*;
/**
* A simple Swing-based client for the chat server. Graphically
* it is a frame with a text field for entering messages and a
* textarea to see the whole dialog.
*
* The client follows the Chat Protocol which is as follows.
* When the server sends "SUBMITNAME" the client replies with the
* desired screen name. The server will keep sending "SUBMITNAME"
* requests as long as the client submits screen names that are
* already in use. When the server sends a line beginning
* with "NAMEACCEPTED" the client is now allowed to start
* sending the server arbitrary strings to be broadcast to all
* chatters connected to the server. When the server sends a
* line beginning with "MESSAGE " then all characters following
* this string should be displayed in its message area.
*/
public class ChatClient {
BufferedReader in;
PrintWriter out;
JFrame frame = new JFrame("Chatter");
JTextField textField = new JTextField(40);
JTextArea messageArea = new JTextArea(8, 40);
JButton sendall = new JButton("Send Message to All");
JButton senduser = new JButton("Send Message to User");
String otherGuy = "";
/**
* Constructs the client by laying out the GUI and registering a
* listener with the textfield so that pressing Return in the
* listener sends the textfield contents to the server. Note
* however that the textfield is initially NOT editable, and
* only becomes editable AFTER the client receives the NAMEACCEPTED
* message from the server.
*/
public ChatClient() {
// Layout GUI
textField.setEditable(false);
messageArea.setEditable(false);
frame.getContentPane().add(textField, "South");
frame.getContentPane().add(new JScrollPane(messageArea), "Center");
frame.getContentPane().add(sendall, "West");
frame.getContentPane().add(senduser, "East");
frame.pack();
// Add Listeners
// textField.addActionListener(new ActionListener() {
// /**
// * Responds to pressing the enter key in the textfield by sending
// * the contents of the text field to the server. Then clear
// * the text area in preparation for the next message.
// */
// public void actionPerformed(ActionEvent e) {
// out.println(textField.getText());
// textField.setText("");
// }
// });
sendall.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
public void actionPerformed(ActionEvent e) {
out.println(textField.getText());
textField.setText("");
}
});
senduser.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
// = sendTo();
public void actionPerformed(ActionEvent e) {
otherGuy = sendTo();
out.println(textField.getText() + " -> " + otherGuy);
textField.setText("");
}
});
}
/**
* Prompt for and return the address of the server.
*/
private String getServerAddress() {
return JOptionPane.showInputDialog(
frame,
"Enter IP Address of the Server:",
"Welcome to the Chatter",
JOptionPane.QUESTION_MESSAGE);
}
/**
* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
private String sendTo() {
return JOptionPane.showInputDialog(
frame,
"Who do you want to send a message to?",
"",
JOptionPane.PLAIN_MESSAGE);
}
/**
* Connects to the server then enters the processing loop.
*/
private void run() throws IOException {
// Make connection and initialize streams
String serverAddress = getServerAddress();
Socket socket = new Socket(serverAddress, 9001);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(getName() + otherGuy);
} else if (line.startsWith("NAMEACCEPTED")) {
textField.setEditable(true);
} else if (line.startsWith("MESSAGE")) {
messageArea.append(line.substring(8) + "\n");
}
}
}
/**
* Runs the client as an application with a closeable frame.
*/
public static void main(String[] args) throws Exception {
ChatClient client = new ChatClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}
Now I have tried thing like having various input output streams and trying to connect those, but no luck. Tried fiddling with having the names arraylist directing the messages to one client versus all but that did not work out either. Do you guys have any idea how I what I would need to do to go about doing this?
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
}
This specifies that right when the server receives data from a client, send it to all output streams right away. There is no filtering system. You need a way to specify which stream you want to send to if you only want the message going to 1 client.
Current protocol:
Client sends - server receives - server sends to all connected outputstreams
Protocol needed:
Client sends - server receives - server see who message is too - server sends message to specified outputstream(s).

FTP Upload in JAVA

I want to use FTP to upload a file with a Java application, but it's not working.
The problem is that if I do the FTPUploader class in my main class, it works perfectly, but if I do like it's in here, it doesn't work. Can you guys help me out?
My code is:
Main Thread:
package restrictedareamanager;
import java.io.File;
import java.io.IOException;
public class RestrictedAreaManager {
File path = new File ("C:\\Área Restrita");
File lista[] = path.listFiles();
public void processa () throws IOException {
String titulo, subtitulo, nomeArq, aux;
for (File s : this.lista) {
aux = s.getName();
//String work
titulo = aux.substring (0, aux.indexOf ("-"));
aux = aux.substring (aux.indexOf ("-")+1);
subtitulo = aux.substring (0, aux.indexOf ("-"));
aux = aux.substring (aux.indexOf ("-")+1);
nomeArq = aux.substring (0);
//Create new file to be copied
final File dest = new File (path + "\\" + nomeArq);
//Copy File
FileCopier copiador = new FileCopier();
copiador.copiaArquivo(s, dest);
//Send file via FTP
FTPUploader ftp = new FTPUploader("**********", "********", "*********", titulo, subtitulo, dest);
ftp.execute();
}
}
public static void main(String[] args) throws IOException {
RestrictedAreaManager ram = new RestrictedAreaManager();
ram.processa();
}
}
FTPUploader class:
package restrictedareamanager;
import java.awt.HeadlessException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
public class FTPUploader extends SwingWorker <Object,Object> {
private String servidor;
private String usuario;
private String senha;
private String titulo;
private String subtitulo;
private File dest;
public FTPUploader (String servidor, String usuario, String senha, String titulo, String subtitulo, File dest) {
this.servidor = servidor;
this.usuario = usuario;
this.senha = senha;
this.titulo = titulo;
this.subtitulo = subtitulo;
this.dest = dest;
}
#Override
protected Object doInBackground() throws Exception {
FTPClient ftp = new FTPClient ();
try {
ftp.connect("servidor");
//verifica se conectou com sucesso!
if( FTPReply.isPositiveCompletion( ftp.getReplyCode() ) ) {
ftp.login ("usuario", "senha");
} else {
//erro ao se conectar
ftp.disconnect();
JOptionPane.showMessageDialog(null, "Ocorreu um erro ao se conectar com o servidor FTP", "Erro", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
ftp.changeWorkingDirectory("/download");
ftp.changeWorkingDirectory(this.titulo);
ftp.changeWorkingDirectory (this.subtitulo);
ftp.storeFile (dest.getName(), new FileInputStream (dest.getPath().toString()));
System.out.println ("Done");
ftp.logout();
ftp.disconnect();
} catch( IOException | HeadlessException e ) {
JOptionPane.showMessageDialog(null, "Ocorreu um erro ao enviar o arquivo.", "Erro", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
return null;
}
}
You may want to check into JSch library - it handles all the ugliness of FTP and sFTP connection in java.
http://www.jcraft.com/jsch/
Here's a sample implementation (from live production code):
package com.somecompany.productfeed;
import net.snakedoc.jutils.ConfigException;
import org.apache.log4j.Logger;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
public class SFTP {
// setup logger
private static final Logger LOG = Logger.getLogger(SFTP.class.getName());
private JSch jsch;
private ChannelSftp sftpChannel;
private Session session;
private String username;
private String password;
private String host;
private int port;
// singleton
private volatile static SFTP instance = null;
/**
* Private constructor used in Singleton pattern.
* All variables populated via properties file upon
* object instantiation. Since we only need FTP to one server,
* object variables are immutable after instantiation.
*
* #throws ConfigException
* #throws NumberFormatException
*/
private SFTP() throws ConfigException, NumberFormatException {
this.jsch = new JSch();
this.username = Config.getInstance().getConfig("SFTP_USER");
this.password = Config.getInstance().getConfig("SFTP_PASS");
this.host = Config.getInstance().getConfig("SFTP_HOST");
this.port = Integer.parseInt(Config.getInstance().getConfig("SFTP_PORT"));
}
/**
* Create or Return SFTP Object using Singleton pattern.
*
* #return Singleton of SFTP Object.
* #throws NumberFormatException
* #throws ConfigException
*/
public static SFTP getInstance() throws NumberFormatException, ConfigException {
if (SFTP.instance == null) {
synchronized (SFTP.class) {
if (SFTP.instance == null) {
SFTP.instance = new SFTP();
}
}
}
return SFTP.instance;
}
/**
* If connection is not already open/connected, open connection.
*
* #throws JSchException
*/
public void openConnection() throws JSchException {
LOG.info("Opening SFTP Connection");
if (null == getSession() || ! getSession().isConnected()) {
setSession(jsch.getSession(this.username, this.host, this.port));
getSession().setConfig("StrictHostKeyChecking", "no");
getSession().setPassword(this.password);
getSession().connect();
Channel channel = getSession().openChannel("sftp");
channel.connect();
setSftpChannel((ChannelSftp) channel);
} else {
LOG.info("SFTP Connection already open");
}
LOG.debug("Success");
}
/**
* Closes connection.
*/
public void closeConnection() {
LOG.info("Closing SFTP connection");
getSftpChannel().exit();
getSession().disconnect();
LOG.debug("SFTP Connection closed");
}
/**
* Checks if SFTP Connection is open.
*
* #return TRUE if connection is open, FALSE if connection is closed
*/
public boolean isOpen() {
if (getSession().isConnected()) {
return true;
} else {
return false;
}
}
/**
* Gets SFTP Channel
*
* #return SFTP Channel (<code>ChannelSftp</code>)
*/
private ChannelSftp getSftpChannel() {
return this.sftpChannel;
}
/**
* Returns current <code>Session</code>
*
* #return <code>Session</code>
*/
private Session getSession() {
return this.session;
}
/**
* Sets SFTP Channel (<code>ChannelSftp</code>)
*
* #param sftpChannel Channel to set this object's <code>ChannelSftp</code>
*/
private void setSftpChannel(ChannelSftp sftpChannel) {
this.sftpChannel = sftpChannel;
}
/**
* Sets current <code>Session</code>
*
* #param session Sets this object's <code>Session</code>
*/
private void setSession(Session session) {
this.session = session;
}
/**
* Pushes local file to remote location
*
* #param local String representation of filepath + filename (ex: /some_local_directory/somefile.txt)
* #param remote String representation of filepath + filename (ex: /some_remote_directory/somefile.txt)
* #throws SftpException
*/
public void push(String local, String remote) throws SftpException {
LOG.info("Sending file: " + local + " to remote: " + remote);
getSftpChannel().put(local, remote);
LOG.debug("Success");
}
/**
* Gets remote file to local location
*
* #param remote String representation of filepath + filename (ex: /some_remote_directory/somefile.txt)
* #param local String representation of filepath + filename (ex: /some_local_directory/somefile.txt)
* #throws SftpException
*/
public void get(String remote, String local) throws SftpException {
LOG.info("Retrieving file: " + remote + " saving to: " + local);
getSftpChannel().get(remote, local);
LOG.debug("Success");
}
}

Retrieving email attachment filename with mime4j

I'm trying to use mime4j to parse emails, all is working fine, however I'm not able to get the file name of the attachment. Unfortunately the BodyDescriptor doesn't include this information in the content disposition, or content type fields.
I have read that the MaximalBodyDescriptor will include the filename, however I don't know how to tell the parser to return a MaximalBodyDescriptor object.
My handler is implementing the ContentHandler interface. I can't see an alternate interface which would work.
Any advice appreciated.
Here is a helper class that we use successfully to parse e-mails with their attachments.
In this approach attach.getFileName() has the filename.
package com.bitplan.smartCRM;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.dom.Body;
import org.apache.james.mime4j.dom.Entity;
import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.dom.MessageBuilder;
import org.apache.james.mime4j.dom.MessageServiceFactory;
import org.apache.james.mime4j.dom.Multipart;
import org.apache.james.mime4j.dom.SingleBody;
import org.apache.james.mime4j.dom.TextBody;
import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.message.MessageImpl;
import org.apache.james.mime4j.stream.Field;
import com.bitplan.restinterface.Configuration;
/**
* EMail Helper class
*
* #author wf
* #author Denis Lunev <den#mozgoweb.com>
* #see http
* ://www.mozgoweb.com/posts/how-to-parse-mime-message-using-mime4j-library
* /
*/
public class EMailHelper implements ClipboardOwner {
public static boolean debug = true;
public static Logger LOGGER = Logger
.getLogger("com.bitplan.common.EMailHelper");
private StringBuffer txtBody;
private StringBuffer htmlBody;
private ArrayList<Entity> attachments;
/**
* get a String from an input Stream
*
* #param inputStream
* #return
* #throws IOException
*/
public String fromInputStream(InputStream inputStream) throws IOException {
String result = IOUtils.toString(inputStream);
// System.out.println(result);
return result;
}
/**
* get the full Mail from a message
*
* #param message
* #return
* #throws MessagingException
* #throws IOException
*/
public String fullMail(javax.mail.Message message) throws MessagingException,
IOException {
StringBuffer sBuf = new StringBuffer();
#SuppressWarnings("unchecked")
Enumeration<javax.mail.Header> headers = message.getAllHeaders();
while (headers.hasMoreElements()) {
javax.mail.Header header = headers.nextElement();
sBuf.append(header.getName() + ": " + header.getValue() + "\n");
}
sBuf.append(fromInputStream(message.getInputStream()));
return sBuf.toString();
}
/**
* Authentication
*/
public static class Authentication {
enum AuthenticationType {
pop3, smtp
};
String host;
String user;
String password;
AuthenticationType authenticationType;
Transport mTransport;
/**
* create an Authentication from the configuration
*
* #param configuration
* #param pAuthType
*/
public Authentication(Configuration configuration,
AuthenticationType pAuthType) {
authenticationType = pAuthType;
String prefix = pAuthType.name() + ".";
// use prefix e.g. pop3.host / smtp.host
host = (String) configuration.toMap().get(prefix + "host");
user = (String) configuration.toMap().get(prefix + "user");
password = (String) configuration.toMap().get(prefix + "password");
}
/**
* authenticate for sending / receiving e-mail
*
* #throws MessagingException
*/
public Transport authenticate() throws MessagingException {
Properties lProps = new Properties();
Session session = Session.getDefaultInstance(lProps);
switch (authenticationType) {
case pop3:
Store store = session.getStore("pop3");
store.connect(host, user, password);
store.close();
return null;
case smtp:
// http://javamail.kenai.com/nonav/javadocs/com/sun/mail/smtp/package-summary.html
mTransport = session.getTransport("smtp");
mTransport.connect(host, user, password);
return mTransport;
}
return null;
}
}
/**
* send the given e-mail
*
* #param email
* #throws MessagingException
*/
public void send(EMail email, Configuration configuration)
throws MessagingException {
Authentication lAuth = new Authentication(configuration,
Authentication.AuthenticationType.pop3);
Properties lProps = System.getProperties();
lProps.put("mail.smtp.host", lAuth.host);
// WF 2004-09-18: make sure full qualified domain name is used for localhost
// the default InetAddress.getLocalHost().getHostName() might not work ...
// lProps.put("mail.smtp.localhost",java.net.InetAddress.getLocalHost().getCanonicalHostName());
Session lSession = Session.getInstance(lProps);
MimeMessage lMsg = new MimeMessage(lSession);
lMsg.setFrom(new InternetAddress(email.getFromAdr()));
lMsg.setRecipients(javax.mail.Message.RecipientType.TO,
InternetAddress.parse(email.getToAdr()));
if (email.getCC() != null)
lMsg.setRecipients(javax.mail.Message.RecipientType.CC,
InternetAddress.parse(email.getCC()));
/*
* if (bcc()!=null) lMsg.setRecipients(Message.RecipientType.BCC,
* InternetAddress.parse(bcc())); lMsg.setHeader("X-Mailer", "JavaMail");
* lMsg.setSentDate(new Date()); lMsg.setSubject(subject());
* lMsg.setText(content()); lMsg.saveChanges(); Transport
* lTransport=lAuth.authenticate(); if (lTransport!=null)
* lTransport.sendMessage(lMsg,lMsg.getAllRecipients()); } else {
* Transport.send(lMsg); }
*/
}
/**
* retrieve pop3 mail from the given host
*
* #param pop3host
* #param user
* #param password
* #throws Exception
*/
public List<EMail> retrievePop3Mail(EMailManager eMailmanager,
Configuration configuration) throws Exception {
List<EMail> result = new ArrayList<EMail>();
Properties lProps = new Properties();
Session session = Session.getDefaultInstance(lProps);
Store store = session.getStore("pop3");
File attachmentDirectory = (File) configuration.toMap().get(
"attachmentDirectory");
// get a pop3 authentication
Authentication auth = new Authentication(configuration,
Authentication.AuthenticationType.pop3);
store.connect(auth.host, auth.user, auth.password);
Folder remoteInbox = store.getFolder("INBOX");
remoteInbox.open(Folder.READ_WRITE);
javax.mail.Message message[] = remoteInbox.getMessages();
if (message.length > 0) {
// get all messages
LOGGER.log(Level.INFO, "Getting " + message.length
+ " messages from POP3 Server '" + store.getURLName() + "'");
for (int i = 0; i < message.length; i++) {
if (!message[i].isSet(Flags.Flag.DELETED)) {
EMail email = eMailmanager.create();
String mailInput = this.fullMail(message[i]);
// System.out.print(mailInput);
ByteArrayInputStream mailStream = new ByteArrayInputStream(
mailInput.getBytes());
this.parseMessage(email, mailStream, attachmentDirectory);
result.add(email);
message[i].setFlag(Flags.Flag.DELETED, true);
}
} // for
} // if
remoteInbox.close(true);
store.close();
return result;
}
/**
* parse the Message into the given EMail
*
* #param email
* #param fileName
* #param attachmentDirectory
*
* #throws Exception
*/
public void parseMessage(EMail email, String fileName,
String attachmentDirectory) throws Exception {
parseMessage(email, new File(fileName), new File(attachmentDirectory));
}
/**
* strip the brackets
*
* #param addressList
* #return
*/
public String stripBrackets(MailboxList addressList) {
String result = null;
if (addressList != null) {
result = addressList.toString();
if (result.startsWith("[") && result.endsWith("]")) {
result = result.substring(1, result.length() - 1);
}
}
return result;
}
/**
* parse the Message from the given file into the given e-mail using the given
* attachmentDirectory
*
* #param email
* #param file
* #param attachmentDirectory
* #throws Exception
*/
public void parseMessage(EMail email, File file, File attachmentDirectory)
throws Exception {
if (!file.canRead() || (!file.isFile()))
throw new IllegalArgumentException(file.getCanonicalPath()
+ " is not a readable file");
// Get stream from file
FileInputStream fis = new FileInputStream(file);
parseMessage(email, fis, attachmentDirectory);
}
/**
* parse the Message from the given file into the given e-mail using the given
* attachmentDirectory
*
* #param email
* #param emailInputStream
* #param attachmentDirectory
* #throws Exception
*/
public void parseMessage(EMail email, InputStream eMailInputStream,
File attachmentDirectory) throws Exception {
Message mimeMsg = null;
if (!attachmentDirectory.isDirectory())
throw new IllegalArgumentException(attachmentDirectory.getCanonicalPath()
+ " is not a directory");
txtBody = new StringBuffer();
htmlBody = new StringBuffer();
attachments = new ArrayList<Entity>();
Exception ex = null;
try {
// Create message with stream from file
// If you want to parse String, you can use:
// Message mimeMsg = new Message(new
// ByteArrayInputStream(mimeSource.getBytes()));
MessageServiceFactory factory = MessageServiceFactory.newInstance();
MessageBuilder msgBuilder = factory.newMessageBuilder();
try {
mimeMsg = msgBuilder.parseMessage(eMailInputStream);
} catch (Throwable th) {
LOGGER.log(Level.SEVERE,th.getClass().getName());
LOGGER.log(Level.SEVERE,th.getMessage());
}
if (mimeMsg == null) {
LOGGER.log(Level.SEVERE, "could not read mime msg:\n",
this.fromInputStream(eMailInputStream));
return;
}
// Get some standard headers
if (mimeMsg.getTo() != null)
email.setToAdr(stripBrackets(mimeMsg.getTo().flatten()));
email.setFromAdr(stripBrackets(mimeMsg.getFrom()));
email.setSubject(mimeMsg.getSubject());
email.setSendDate(mimeMsg.getDate());
email.setEMailId(mimeMsg.getMessageId());
LOGGER.log(Level.INFO, "To: " + email.getToAdr());
LOGGER.log(Level.INFO, "From: " + email.getFromAdr());
LOGGER.log(Level.INFO, "Subject: " + mimeMsg.getSubject());
// Get custom header by name
Field priorityFld = mimeMsg.getHeader().getField("X-Priority");
// If header doesn't found it returns null
if (priorityFld != null) {
// Print header value
LOGGER.log(Level.FINEST, "Priority: " + priorityFld.getBody());
}
// If message contains many parts - parse all parts
if (mimeMsg.isMultipart()) {
Multipart multipart = (Multipart) mimeMsg.getBody();
parseBodyParts(multipart);
// fix mime4j 0.7.2 behaviour to have no separate text/plain part
if (txtBody.length() == 0) {
txtBody.append(multipart.getPreamble());
}
} else {
// If it's single part message, just get text body
String text = getTxtPart(mimeMsg);
txtBody.append(text);
}
email.setContent(txtBody.toString());
// Print text and HTML bodies
if (debug) {
LOGGER.log(Level.FINEST, "Text body: " + txtBody.toString());
LOGGER.log(Level.FINEST, "Html body: " + htmlBody.toString());
}
// loop over attachments
for (Entity attach : attachments) {
writeAttachment(attach, attachmentDirectory);
}
} catch (Exception cex) {
ex = cex;
} finally {
if (eMailInputStream != null) {
try {
eMailInputStream.close();
} catch (IOException ioex2) {
ioex2.printStackTrace();
}
}
}
if (ex != null) {
throw ex;
}
}
/**
* write the given Attachment
*
* #param attach
* #param attachmentDirectory
* #throws IOException
*/
public void writeAttachment(Entity attach, File attachmentDirectory)
throws IOException {
String attName = attach.getFilename();
// Create file with specified name
if (attName == null) {
LOGGER.log(Level.WARNING, "attachment has no file name using 'attachment"
+ attach.hashCode() + "' instead");
attName = "attachment" + attach.hashCode();
}
FileOutputStream fos = new FileOutputStream(new File(attachmentDirectory,
attName));
try {
writeBody(fos, attach.getBody());
} finally {
fos.close();
}
}
/**
* write the given body to the given fileoutput stream
*
* #param fos
* #param body
* #throws IOException
*/
public void writeBody(FileOutputStream fos, Body body) throws IOException {
if (body instanceof SingleBody) {
((SingleBody) body).writeTo(fos);
} else if (body instanceof MessageImpl) {
writeBody(fos, ((MessageImpl) body).getBody());
} else {
LOGGER.log(Level.WARNING, "can't handle body of type "
+ body.getClass().getSimpleName());
}
}
/**
* This method classifies bodyPart as text, html or attached file
*
* #param multipart
* #throws IOException
*/
private void parseBodyParts(Multipart multipart) throws IOException {
// loop over the parts
for (Entity part : multipart.getBodyParts()) {
String mimeType = part.getMimeType();
if (mimeType.equals("text/plain")) {
String txt = getTxtPart(part);
txtBody.append(txt);
} else if (mimeType.equals("text/html")) {
String html = getTxtPart(part);
htmlBody.append(html);
} else if (part.getDispositionType() != null
&& !part.getDispositionType().equals("")) {
// If DispositionType is null or empty, it means that it's multipart,
// not attached file
attachments.add(part);
}
// If current part contains other, parse it again by recursion
if (part.isMultipart()) {
parseBodyParts((Multipart) part.getBody());
}
}
}
/**
*
* #param part
* #return
* #throws IOException
*/
private String getTxtPart(Entity part) throws IOException {
// Get content from body
TextBody tb = (TextBody) part.getBody();
return this.fromInputStream(tb.getInputStream());
}
/**
* Place a String on the clipboard, and make this class the owner of the
* Clipboard's contents.
*
* #param aString
*/
public void setClipboardContents(String aString) {
StringSelection stringSelection = new StringSelection(aString);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, this);
}
/**
* get text from the clipboard
*
* #return
* #throws Exception
*/
public String getClipboardText() throws Exception {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
String text = (String) clipboard.getData(DataFlavor.stringFlavor);
return text;
}
/**
* get Mail from clipboard
*
* #param email
* #param attachmentDirectory
* #return
* #throws Exception
*/
public boolean getMailFromClipboard(EMail email, File attachmentDirectory)
throws Exception {
String mailText = getClipboardText();
if (mailText == null)
return false;
this.parseMessage(email,
new ByteArrayInputStream(mailText.getBytes("UTF-8")),
attachmentDirectory);
return true;
}
/*
* (non-Javadoc)
*
* #see
* java.awt.datatransfer.ClipboardOwner#lostOwnership(java.awt.datatransfer
* .Clipboard, java.awt.datatransfer.Transferable)
*/
#Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}
}
I recommand you to use Token streams.
It is quite straight forward with it.
You can locate attachment by using headers of your multipart section :
Content-Disposition:attachment; filename="toto.txt"
You must be carefull when parsing a header with it ... It can be mail headers or multipart section header....

RMI chat program in java - how to send mesage from client to client (not through the server)?

I cannot get the client to send a message to another client without going through the server. It is an obligatory school project that should be implemented like that. I would appreciate it if someone could help me.
Server Interface:
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
public interface ChatServerInt extends Remote {
public abstract void register(ChatClientInt inClient) throws RemoteException;
public abstract ChatClientInt[] getClients() throws RemoteException;
public void disconnect(ChatClientInt client) throws RemoteException;
}
Server Implementation:
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;
public class ChatServerImp extends UnicastRemoteObject implements ChatServerInt, Runnable {
private ChatClientInt[] clientList;
int counter = 0;
private ArrayList<String> connectedClients;
/**
* List of all registered remote clients.
*/
private List<ChatClientInt> clients = null;
/**
* Construct an instance of the chat server.
*/
public ChatServerImp() throws RemoteException {
// initialise the list of client objects.
clients = new ArrayList<ChatClientInt>();
connectedClients = new ArrayList<String>();
clientList = new ChatClientInt[16];
}
/**
* Register a chat client.
*/
public void register(ChatClientInt inClient) throws RemoteException {
// perform registration.
synchronized(clients)
{
clients.add(inClient);
clientList[counter++] = inClient;
inClient = new ChatClientImp();
for(int i = 0 ; i < clients.size();i++) {
System.out.println(inClient.getName()+ "has joined\n");
}
}
}
/**After registering, each client will request the list of connected users.
* Get a list of chat clients.
*/
public synchronized ChatClientInt[] getClients() throws RemoteException {
// generate and return the list
return clientList;
}
public void disconnect(ChatClientInt client) throws RemoteException {
for(int i = 0; i < clients.size(); i++) {
System.out.println(client.getName() + "" + "has joined \n");
}
clients.remove(client);
}
/**
* Generate a random subset. Based on an implementation of D. Knuth's
* #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's main thread. The server should 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) {
//System.out.println("waiting for client connection....\n");
// sleep for a while
try {
Thread.sleep(5000);
// iterate through all the clients we know. if we can't communicate with the client
// then eliminate it else if we can communicate with the client then do nothing.
for(int i =0; i < clients.size(); i++) {
try {
if(clients.get(i).getName()==null) {
clients.remove(clients.get(i).getName());
System.out.println("Disconnected clients:\n" + clients.get(i).getName());
}
}
catch (RemoteException e) {e.printStackTrace();}
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Start the chat server.
* #param args
*/
public static void main(String[] args) throws Exception {
//ChatServerInt server;
try {
String serverName = "rmi://localhost/ChatServer";
// create an instance of the chat server
//server = (ChatServerImp) new ChatServerImp();
//Launch the registry - saves invoking it manually
java.rmi.registry.LocateRegistry.createRegistry(1099);
// register the instance with the RMIRegistry
Naming.rebind(serverName, new ChatServerImp());
//Naming.rebind(serverName, server);
// create the server thread and start it
//Thread t = new Thread(server).start();
System.out.println("Server running.....\n");
}
catch(RemoteException ex) {
System.out.println("Error binding the server to rmi");
}
}
}
Message Interface:
import java.util.HashSet;
public interface MessageInt {
/**
* Add a chat recipient to the list of receivers
* #param inClient
*/
public abstract void addRecipient(ChatClientInt inClient);
/**
* Get the set of clients that have seen this message
* #return
*/
public abstract HashSet<ChatClientInt> getRecipients();
/**
* Get the message content.
* #return
*/
public abstract String getContent();
/**
* Get an ID for a sender of the message
* #return
*/
public abstract String getSource();
}
Message Implementation:
import java.io.Serializable;
import java.util.HashSet;
public class MessageImp implements MessageInt, Serializable {
/**
*
*/
private static final long serialVersionUID = -2686034785384409672L;
HashSet<ChatClientInt> clientSet = new HashSet<ChatClientInt>();
String messageContent;
String messageSource;
public MessageImp(String inUser , String inMsg) {
messageSource = inUser;
messageContent = inMsg;
}
/**
* Add a chat recipient to the list of receivers
* #param inClient
*/
public void addRecipient(ChatClientInt inClient) {
synchronized(inClient) {
clientSet.add(inClient);
}
}
/**
* Get the set of clients that have seen this message
* #return
*/
public HashSet<ChatClientInt> getRecipients() {
return clientSet;
}
/**
* Get the message content.
* #return
*/
public String getContent() {
return messageContent;
}
/**
* Get an ID for a sender of the message
* #return
*/
public String getSource() {
return messageSource;
}
}
Client Interface:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface ChatClientInt extends Remote {
/**
* Process a newly received message.
*
* #param inMessage
*/
public void processMessage(MessageInt inMessage)throws RemoteException;
/**
* Returns the name of the client.
*/
public String getName() throws RemoteException;
public boolean sendMessage(MessageInt inMessage) throws RemoteException;
}
Client Implementation:
import java.io.Serializable;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
public class ChatClientImp extends UnicastRemoteObject implements ChatClientInt, Runnable {
/**
*
*/
private static final long serialVersionUID = 74130345076834009L;
private ArrayList <String> eventHistory = new ArrayList<String>();
private HashSet<ChatClientInt> clientSet = new HashSet<ChatClientInt>();
private List<ChatClientInt> clients;// = new ArrayList<ChatClientInt>();
ChatClientInt[] listFromServer = new ChatClientInt[4];
String clientName;
String strMessage;
MessageInt messageObj;
ChatServerInt serverObj;
public ChatClientImp() throws RemoteException {
super();
clients = new ArrayList<ChatClientInt>();
}
public void processMessage(MessageInt inMessage) throws RemoteException {
System.out.println("message:" + inMessage.getRecipients().toString() + inMessage.getSource() + inMessage.getContent() + "\n");
}
public String getName() throws RemoteException {
return clientName;
}
public synchronized boolean sendMessage(MessageInt inMessage) throws RemoteException {
boolean success = false;
for(int i = 0; i < clients.size(); i++) {
clients.get(i).processMessage(inMessage);
inMessage.addRecipient(clients.get(i));
success = true;
}
return success;
}
public void displayMessage(String displayName, String displayMsg) {
Iterator<ChatClientInt> it = clientSet.iterator();
while(it.hasNext()) {
System.out.println(displayName + displayMsg + "\n");
}
}
public void run() {
Scanner scanner = new Scanner(System.in);
String userName = "";
try {
this.serverObj =(ChatServerInt) new ChatServerImp();
} catch (RemoteException e) {
e.printStackTrace();
}
// Keep requesting until a name is submitted that is not already used.
// checking for the existence of a name and adding the name
// is done while locking the set of names.
System.out.println("Please Enter a username\n");
while(true) {
userName = scanner.nextLine();
if(userName==null) {
return;
}
clientName = userName;
try {
serverObj.register(ChatClientImp.this);
listFromServer = serverObj.getClients();
clientSet.add(this);
} catch (RemoteException e) {
e.printStackTrace();
}
System.out.println("Hi" + " " + clientName + " " + "enter your message\n");
String msg = scanner.nextLine();
if(msg.equals("exit")) {
try {
serverObj.disconnect(this);
System.exit(0);
}
catch (RemoteException e) {
e.printStackTrace();
}
}//end if
if((listFromServer.length) > 1) {
System.out.println("list from client is" + " " + listFromServer.length + "going to try to send message.....\n");
try {
this.messageObj = new MessageImp(userName, msg);
boolean result = this.sendMessage(messageObj);
if(result) {
System.out.print("sending result is:" + result + "\n");
} else {
System.out.println("sending was not successful\n");
}
} catch (RemoteException e1) {
System.out.println("Error tryiing to send message from the Run()\n");
}
} else {
System.out.println("There is no one else logged on\n");
}
}
}
public static void main(String[] args) throws RemoteException {
String serverUrl= "rmi://localhost/ChatServer";
try {
ChatServerInt server = (ChatServerInt)Naming.lookup(serverUrl);
Thread thread = new Thread(new ChatClientImp());
thread.start();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
The only way you can do that with RMI is if the clients are also RMI servers and send stubs to each other somehow.

Categories