Chat - Primefaces

Pessoal fiz um chat utilizando Socket, que tá funcionando legal exceto na hora de atualizar a mensagem recebida.

vejam só o código.

A página xhtml.


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Messenger</title>
    </h:head>
    <h:body>
        <h:form id="form">
            <p:panel id="panel" header="Chat">

                <p:inputTextarea id="mensagens" value="#{clienteSoccketBean.mensagensRecebidas}" rows="10" cols="50" /> <br/>
                <p:inputTextarea value="#{clienteSoccketBean.mensagemAEnviar}" rows="5" cols="50" />


                <p:commandButton value="Enviar" actionListener="#{clienteSoccketBean.enviarMensagem}" />

            </p:panel>
        </h:form>
    </h:body>
</html>

O ClienteSocketBean:


import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Random;
import java.util.Scanner;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

/**
 *
 * @author Daniel
 * 
 */
@ManagedBean
@SessionScoped
public class ClienteSoccketBean {

    private Socket conexao;
    private PrintStream saida;
    private String mensagensRecebidas = "";
    private String mensagemAEnviar;
    private Scanner entradaDoServidor = new Scanner("");

    public ClienteSoccketBean() {

        try {

            conexao = new Socket("127.0.0.1", 5555);

            saida = new PrintStream(conexao.getOutputStream());

            String meuNome = "Daniel " + new Random().nextInt();

            saida.println(meuNome.toUpperCase());

            entradaDoServidor = new Scanner(new InputStreamReader(conexao.getInputStream()));

            lerMensagem();

        } catch (Exception e) {
            System.out.println("" + e);
        }

    }

    public String getMensagemAEnviar() {
        return mensagemAEnviar;
    }

    public void setMensagemAEnviar(String mensagemAEnviar) {
        this.mensagemAEnviar = mensagemAEnviar;
    }

    public String getMensagensRecebidas() {
        return mensagensRecebidas;
    }

    public void setMensagensRecebidas(String mensagensRecebidas) {
        this.mensagensRecebidas = mensagensRecebidas;
    }

    public void lerMensagem() {

        new Thread() {

            public void run() {
                
                while (true) {
                    if(entradaDoServidor.hasNextLine()){
                        //Esse é o problema, como atualizar a Área de texto que recebe a variável 'mensagensRecebidas'?
                        mensagensRecebidas += "\n"+entradaDoServidor.nextLine();
                        //Sim... a variavel recebeu o valor direitinho 
                        //Se eu mandar imprimir sai tudo certo
                    }
                }
            }
        }.start();
    }

    public void enviarMensagem() {
        saida.println(mensagemAEnviar);
    }
}

O servidor (Consegui essa classe em algum fórum):

      

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintStream;

import java.net.ServerSocket;

import java.net.Socket;
import java.util.ArrayList;

import java.util.Enumeration;

import java.util.List;

import java.util.Vector;

public class ServidorSocket extends Thread {

    // Parte que controla as conexões por meio de threads.
    private static Vector CLIENTES;
    // socket deste cliente
    private Socket conexao;
    // nome deste cliente
    private String nomeCliente;
    // lista que armazena nome de CLIENTES
    private static List LISTA_DE_NOMES = new ArrayList();

    // construtor que recebe o socket deste cliente
    public ServidorSocket(Socket socket) {

        this.conexao = socket;

    }

    //testa se nomes são iguais, se for retorna true
    public boolean armazena(String newName) {

        //   System.out.println(LISTA_DE_NOMES);

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

            if (LISTA_DE_NOMES.get(i).equals(newName)) {
                return true;
            }

        }

        //adiciona na lista apenas se não existir

        LISTA_DE_NOMES.add(newName);

        return false;

    }

    //remove da lista os CLIENTES que já deixaram o chat
    public void remove(String oldName) {

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

            if (LISTA_DE_NOMES.get(i).equals(oldName)) {
                LISTA_DE_NOMES.remove(oldName);
            }

        }

    }

    public static void main(String args[]) {

        // instancia o vetor de CLIENTES conectados

        CLIENTES = new Vector();

        try {

            // cria um socket que fica escutando a porta 5555.

            ServerSocket server = new ServerSocket(5555);

            System.out.println("ServidorSocket rodando na porta 5555");

            

            // Loop principal.

            while (true) {

                // aguarda algum cliente se conectar.

                // A execução do servidor fica bloqueada na chamada do método accept da

                // classe ServerSocket até que algum cliente se conecte ao servidor.

                // O próprio método desbloqueia e retorna com um objeto da classe Socket

                Socket conexao = server.accept();

                // cria uma nova thread para tratar essa conexão

                Thread t = new ServidorSocket(conexao);

                t.start();

                // voltando ao loop, esperando mais alguém se conectar.

            }

        } catch (IOException e) {

            // caso ocorra alguma excessão de E/S, mostre qual foi.

            System.out.println("IOException: " + e);

        }

    }

    // execução da thread
    public void run() {

        try {

            // objetos que permitem controlar fluxo de comunicação que vem do cliente

            BufferedReader entrada = new BufferedReader(new InputStreamReader(this.conexao.getInputStream()));

            PrintStream saida = new PrintStream(this.conexao.getOutputStream());

            // recebe o nome do cliente
 
            this.nomeCliente = entrada.readLine();

            //chamada ao metodo que testa nomes iguais

            if (armazena(this.nomeCliente)) {

                saida.println("Este nome ja existe! Conecte novamente com outro Nome.");

                CLIENTES.add(saida);

                //fecha a conexao com este cliente

                this.conexao.close();

                return;

            } else {

                //mostra o nome do cliente conectado ao servidor

                System.out.println(this.nomeCliente + " : Conectado ao Servidor!");

            }

            //igual a null encerra a execução

            if (this.nomeCliente == null) {

                return;

            }

            //adiciona os dados de saida do cliente no objeto CLIENTES

            CLIENTES.add(saida);

            //recebe a mensagem do cliente

            String msg = entrada.readLine();

            // Verificar se linha é null (conexão encerrada)

            // Se não for nula, mostra a troca de mensagens entre os CLIENTES

            while (msg != null && !(msg.trim().equals(""))) {

                // reenvia a linha para todos os CLIENTES conectados


                sendToAll(saida, " escreveu: ", msg);

                // espera por uma nova linha.

                msg = entrada.readLine();

            }

            //se cliente enviar linha em branco, mostra a saida no servidor

            System.out.println(this.nomeCliente + " saiu do bate-papo!");

            //se cliente enviar linha em branco, servidor envia mensagem de saida do chat aos CLIENTES conectados

            sendToAll(saida, " saiu", " do bate-papo!");

            //remove nome da lista

            remove(this.nomeCliente);

            //exclui atributos setados ao cliente

            CLIENTES.remove(saida);

            //fecha a conexao com este cliente

            this.conexao.close();

        } catch (IOException e) {

            // Caso ocorra alguma excessão de E/S, mostre qual foi.

            System.out.println("Falha na Conexao... .. ." + " IOException: " + e);

        }

    }

    // enviar uma mensagem para todos, menos para o próprio
    public void sendToAll(PrintStream saida, String acao, String msg) throws IOException {

        Enumeration e = CLIENTES.elements();

        while (e.hasMoreElements()) {

            // obtém o fluxo de saída de um dos CLIENTES

            PrintStream chat = (PrintStream) e.nextElement();

            // envia para todos, menos para o próprio usuário

            if (chat != saida) {

                chat.println(this.nomeCliente + acao + msg);

            }

        }

    }
}

Para executar eu rodo primeiro o servidor e depois executo o projeto com a página .xhtml e o bean.

Recaptulando o problema é: [color=red] como atualizar a Área de texto que recebe a variável ‘mensagensRecebidas’? [/color]

Obrigado.

O jeito mais fácil? poll.

Obrigado pela resposta Rodrigo.

Mas…

Não sei porque não funcionou quando eu usei.

Tinha feito isso na página .xhtml:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Messenger</title>
    </h:head>
    <h:body>
        <h:form id="form">
            <p:panel id="panel" header="Chat">

                <p:poll interval="1" update="form:mensagens" />

                <p:inputTextarea id="mensagens" value="#{clienteSoccketBean.mensagensRecebidas}" rows="10" cols="50" /> <br/>
                <p:inputTextarea value="#{clienteSoccketBean.mensagemAEnviar}" rows="5" cols="50" />


                <p:commandButton value="Enviar" actionListener="#{clienteSoccketBean.enviarMensagem}" />

            </p:panel>
        </h:form>
    </h:body>
</html>

Ele realmente fica atualizando mas a mensagem não aparece.

Mais uma vez obrigado.

cadê o listener? você não precisa enviar alguma requisição pro servidor pra atualizar a lista?

Mesmo sem o listener ele fica atualizando, porque o método ‘lerMensagem()’ abaixo

public void lerMensagem() {  
  
        new Thread() {  
  
            public void run() {  
                  
                while (true) {  
                    if(entradaDoServidor.hasNextLine()){  
                        //Esse é o problema, como atualizar a Área de texto que recebe a variável 'mensagensRecebidas'?  
                        mensagensRecebidas += "\n"+entradaDoServidor.nextLine();  
                        //Sim... a variavel recebeu o valor direitinho   
                        //Se eu mandar imprimir sai tudo certo  
                    }  
                }  
            }  
        }.start();  
    }  

fica em um loop infinito atualizando a variável ‘mensagensRecebidas’.

bom, se você diz que ele está atualizando a tela corretamente, provavelmente o erro não está aí, está na parte onde você busca as mensagens.

Alguma Sugestão?

Experimente fazer um método que busca as mensagens, e deixar ele como listener do poll, assim você não precisa deixar uma thread ativa sempre buscando tudo, quando for o tempo do poll, ele verifica, se tiver mensagens ele adiciona e aí atualiza a tela.

Beleza vou tentar agora.

Rodrigo, deu certo cara muito obrigado.

o código ficou assim

.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Messenger</title>
    </h:head>
    <h:body>
        <h:form id="form">
            <p:panel id="panel" header="Chat">

                <p:poll interval="1" listener="#{clienteSoccketBean.lerMensagem}" update="form:mensagens" />

                <p:inputTextarea id="mensagens" value="#{clienteSoccketBean.mensagensRecebidas}" rows="10" cols="50" /> <br/>
                <p:inputTextarea value="#{clienteSoccketBean.mensagemAEnviar}" rows="5" cols="50" />


                <h:commandButton value="Enviar" action="#{clienteSoccketBean.enviarMensagem}" />

            </p:panel>
        </h:form>
    </h:body>
</html>

Bean:


import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Random;
import java.util.Scanner;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

/**
 *
 * @author Daniel
 * 
 */
@ManagedBean
@SessionScoped
public class ClienteSoccketBean {

    private Socket conexao;
    private PrintStream saida;
    private String mensagensRecebidas = "";
    private String mensagemAEnviar = "";
    private Scanner entradaDoServidor = new Scanner("");

    public ClienteSoccketBean() {
        try {

            conexao = new Socket("127.0.0.1", 5555);

            saida = new PrintStream(conexao.getOutputStream());

            String meuNome = "Daniel " + new Random().nextInt();

            saida.println(meuNome.toUpperCase());

            entradaDoServidor = new Scanner(new InputStreamReader(conexao.getInputStream()));

        } catch (Exception e) {
            System.out.println("" + e);
        }
    }

    public String getMensagemAEnviar() {
        return mensagemAEnviar;
    }

    public void setMensagemAEnviar(String mensagemAEnviar) {
        this.mensagemAEnviar = mensagemAEnviar;
    }

    public String getMensagensRecebidas() {
        return mensagensRecebidas;
    }

    public void setMensagensRecebidas(String mensagensRecebidas) {
        this.mensagensRecebidas = mensagensRecebidas;
    }

    public void lerMensagem() {
        try {
                if (entradaDoServidor.hasNextLine()) {
                    mensagensRecebidas = entradaDoServidor.nextLine();
                    return;
                }
            
        } catch (Exception e) {
        }
    }

    public void enviarMensagem() {

        saida.println(mensagemAEnviar);

    }
}

Mas surgiu outro problema, a mensagem só chega na outra tela se o botão enviar for clicado duas vezes.

Já tentei ajax=“false” no <p:commandButton e não deu certo.

Obrigado novamente.