Falha ao criar uma thread por socket

Ola pessoal, não sei se aqui é o melhor lugar para postar minha dúvida, mais acho que ela é um pouco básica.

Vamos la, então pessoal, estou construindo um bate-papo com Swing só pra aprender um pouco mais sobre threads mesmo, nada de comercial, logo não liguem para algumas coisinhas feias que estou fazendo como não implementar runnable e outras coisas. Li um pouco sobre serialização e ja estudei um pouco sobre threads no livro pra certificação, mas ainda estou com algumas duvidas no meu projeto como:

  1. estou tentando fazer a comunicação com o servidor passando um objeto no fluxo (ObjectOutputStream) acho que ate ai tudo bem.
  2. Estou tentando gerar uma nova thread para cada socket recebido pelo servidor
  3. para cada conexao no servidor eu mantenho uma lista dos conectados
  4. Para cada thread criada gostaria de uma mensagem enviada por um usuario seja enviada para todos que estao conectados.

OBS: algumas funcionalidades não estao implementadas, gostaria mesmo é que me ajudem a identificar o erro.

Desde já agradeço pessoal

Vou postar todo o codigo, vai ficar um pouco grande.

[code]public class Usuario implements Serializable {

private InetAddress adress;
private String nickName;
private boolean online;
private String ultimaMensagem;

public Usuario() {
}

public Usuario(String nickName, InetAddress adress) {
    this.nickName = nickName;
    this.online = true;
    this.adress = adress;
}

public String getUltimaMensagem() {
    return ultimaMensagem;
}

public void setUltimaMensagem(String msg) {
    this.ultimaMensagem = msg;
}

public String getNickName() {
    return nickName;
}

public void setNickName(String name) {
    this.nickName = name;
}

public boolean isOnline() {
    return online;
}

public void setOnline(boolean online) {
    this.online = online;
}

public InetAddress getAdress() {
    return adress;
}

public void setAdress(InetAddress adress) {
    this.adress = adress;
}

private void readObject(ObjectInputStream in) {
    try {
        in.defaultReadObject();
        this.nickName = in.readUTF();
        this.online = in.readBoolean();
        this.ultimaMensagem = in.readUTF();
        this.adress = (InetAddress)in.readObject();
    } catch (Exception e) {
        throw new ValidationException("Falha in Usuario.readObject", e);
    }
}
private void writeObject(ObjectOutputStream out) {
    try {
        out.defaultWriteObject();
        out.writeUTF(this.nickName);
        out.writeBoolean(this.online);
        out.writeUTF(this.ultimaMensagem);
        out.writeObject(this.adress);
    } catch (Exception e) {
        throw new ValidationException("Falha in Usuario.writeObject", e);
    }
}
@Override
public String toString() {
    return "Usuario{" + "name=" + nickName + ", ipAdress=???" + ", online=" + online + ", ultimaMensagem=" + ultimaMensagem + '}';
}

}
[/code]

[code]public class EntrarUI extends javax.swing.JFrame {

private DataInputStream in;
private DataOutputStream out;
private Socket clientSocket;
private Usuario user;

/**
 * Creates new form EntrarUI
 */
public EntrarUI() {
    initComponents();
    //centralizar
    this.setLocationRelativeTo(null);
}

                 

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
    entrar();
}                                        


private void entrar() {
    if (txtNomeApelido.getText().isEmpty() || txtNomeApelido.getText() == null) {
        throw new ValidationException("Informe seu Nome/Apelido para acessar o bate-papo.", null);
    }
    
    if (txtIpServidor.getText().isEmpty() || txtIpServidor.getText() == null) {
        throw new ValidationException("Informe o ip do servidor para acessar a sala de bate-papo.", null);
    }
    
    if (txtPorta.getText().isEmpty() || txtPorta.getText() == null) {
        throw new ValidationException("Informe a porta do servidor para acessar a sala de bate-papo.", null);
    }
    
    String nickName = txtNomeApelido.getText();
    String servidor = txtIpServidor.getText();
    int porta = Integer.parseInt(txtPorta.getText());
    
    if (estabelecerConexao(servidor, porta)) {
        this.user = new Usuario(nickName, clientSocket.getLocalAddress());
        new MessengerClientUI(user, clientSocket).setVisible(true);
        this.dispose();
    }
}


private boolean estabelecerConexao(String server, int port) {
    try {
        this.clientSocket = new Socket(server, port);
        return clientSocket != null ? true : false;
    } catch (Exception e) {
        throw new ValidationException("Falha ao tentar obter conexão com o servidor.", e);
    }
}

/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    /*
     * Set the Nimbus look and feel
     */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /*
     * If Nimbus (introduced in Java SE 6) is not available, stay with the
     * default look and feel. For details see
     * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
     */
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(EntrarUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(EntrarUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(EntrarUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(EntrarUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /*
     * Create and display the form
     */
    java.awt.EventQueue.invokeLater(new Runnable() {
        
        public void run() {
            new EntrarUI().setVisible(true);
        }
    });
}

}
[/code]



public class MessengerClientUI extends javax.swing.JFrame {

    private Usuario user;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private Socket clientSocket;

    public MessengerClientUI(Usuario user, Socket socket) {
        this();
        try {
            System.out.println("criou out");
            this.clientSocket = socket;
            this.user = user;
            this.out = new ObjectOutputStream(socket.getOutputStream());
        } catch (IOException ex) {
            throw new ValidationException("Falha ao iniciar fluxo de saida.", ex);
        }
    }

    public MessengerClientUI() {
        initComponents();
        //centralizar
        this.setLocationRelativeTo(null);
    }

                          

    private void btnLimparActionPerformed(java.awt.event.ActionEvent evt) {                                          
        limpar();
    }                                         

    private void btnEnviarActionPerformed(java.awt.event.ActionEvent evt) {                                          
        enviarMensagem();
    }                                         

    private void atualizarBateBapo() {
        try {
            ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
            Usuario userNewMessage = (Usuario) in.readObject();
            insertNewMessage(user);
        } catch (Exception e) {
            throw new ValidationException("Falha ao atualizar mensagens enviada pelos usuarios ", e);
        }
    }

    private void insertNewMessage(Usuario user) {
        //se não tiver nada escrito retorna uma String vazia, caso contrario o texto
        String batePapoAntigo = txtaBatePapo.getText() != null ? txtaBatePapo.getText() : "";
        String novaMensagem = user.getUltimaMensagem();
        String mensagemAtualizada = batePapoAntigo
                + "\n\n<<" + user.getNickName() + ">>"
                + "\nMensagem: " + user.getUltimaMensagem();
        txtaBatePapo.setText(mensagemAtualizada);
    }

    private void enviarMensagem() {
        if (txtaMensagem.getText().isEmpty() || txtaMensagem.getText() == null) {
            throw new ValidationException("Informe pelomenos um caractere");
        }

        user.setUltimaMensagem(txtaMensagem.getText().trim());

        try {
            out.writeObject(user);
            out.flush();
        } catch (IOException ex) {
            throw new ValidationException("Falha ao enviar dados do cliente ao servidor.", ex);
        }
    }

    private void limpar() {
    }

    public static void main(String args[]) {

        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /*
         * If Nimbus (introduced in Java SE 6) is not available, stay with the
         * default look and feel. For details see
         * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MessengerClientUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MessengerClientUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MessengerClientUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MessengerClientUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new MessengerClientUI().setVisible(true);
            }
        });
    }                 
}

public class Server {
    
    private static List<Usuario> usuarios = new ArrayList<>();
    
    public Server() {
    }
    
    public static void main(String[] args) {
        ServerSocket server = null;
        try {
            server = new ServerSocket(ConfigServer.PORT);
            System.out.println("server on\n\n\n");
        } catch (Exception e) {
            throw new ValidationException("Falha ao iniciar o servidor", e);
        }
        while (true) {
            try {
                Socket client = server.accept();
                atualizaListaUsuarios(client);
                ConnectionManagerServer manager = new ConnectionManagerServer(client, usuarios);
            } catch (Exception e) {
                throw new ValidationException("Falha ao obter cliente no servidor", e);
            }
            
        }
        
    }

    /**
     * Atualiza a lista de clientes conectados ao bate-bapo
     *
     * @param Socket
     */
    private static void atualizaListaUsuarios(Socket client) {
        try {
            ObjectInputStream inServer = new ObjectInputStream(client.getInputStream());
            Usuario user = (Usuario) inServer.readObject();
            System.out.println(user.toString());
            usuarios.add(user);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ValidationException("Falha ao obter dados do cliente #"
                    + client.getInetAddress().getHostAddress(), e);
        }
        
    }
}

Aqui está a danada

package util;

import client.model.Usuario;
import java.io.*;
import java.net.Socket;
import java.util.List;

public class ConnectionManagerServer extends Thread {

    private List<Usuario> usuarios;
    private Socket client;
    private ObjectInputStream in;
    private ObjectOutputStream out;

    /**
     * Este construtor serve como um Eco das mensagens, ou seja, ele recebe e
     * repassa
     *
     * @param aSocket
     */
    public ConnectionManagerServer(Socket aSocket, List<Usuario> usuarios) {
        try {
            this.client = aSocket;
            this.usuarios = usuarios;
            
            in = new ObjectInputStream(client.getInputStream());
            out = new ObjectOutputStream(client.getOutputStream());
            
            this.start();
        } catch (IOException e) {
            throw new ValidationException("IOException do client #"
                    +client.getInetAddress().getHostAddress());
        }
    }

    public void run() {
        try {
            Usuario user = (Usuario)in.readObject();
            
        } catch (Exception e) {
            throw new ValidationException("Falha ao ECOAR objeto em run()", e);
        }
    }

    private void sendToAll() {
        for (Usuario user : usuarios) {
            try {
                ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
                out.writeObject(user);
            } catch (Exception e) {
                throw new ValidationException("Falha ao iniciar fluxo de xaida para as mensagens do servidor.", e);
            }
        }
    }
}

Se alguem precisa eu posso enviar projeto(netbeans)

  1. ObjectOutputStream com sockets não é uma boa coisa a ser usada (a menos que a conexão seja usada apenas por um breve período e com poucos dados a serem enviados - é que ele inerentemente acaba tendo um fenômeno semelhante a um “memory leak”)
  2. Se você associou um ObjectOutputStream a um socket, ele deve permanecer associado até você fechar o socket. O que você escreveu abaixo:
                ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());  
                out.writeObject(user);

aparentemente vai dar problemas :frowning:

[quote=entanglement]1) ObjectOutputStream com sockets não é uma boa coisa a ser usada (a menos que a conexão seja usada apenas por um breve período e com poucos dados a serem enviados - é que ele inerentemente acaba tendo um fenômeno semelhante a um “memory leak”)
2) Se você associou um ObjectOutputStream a um socket, ele deve permanecer associado até você fechar o socket. O que você escreveu abaixo:

                ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());  
                out.writeObject(user);

aparentemente vai dar problemas :frowning:
[/quote]
hHmm certo,
mas minha aplicação é para uma pequena troca de informação, entre no máx 10 computadores numa rede internna, nada mais do que isso, estou tento problema na criação dessas threads e na notificação de todas as threads em execução, ou seja, quando mando uma msg todos devem visualiza-la