Projeto Final - Messenger

Integrantes:

  • Ricardo Itiro Sabota Tominaga
  • Marcus Leandro

Ano/sem: 2008/2o.

Introdução

O projeto consistiu no desenvolvimento de um software similar ao famoso "MSN messenger" utilizando-se, para isso, a linguagem Java. A semelhança com o "MSN messenger" se dá devido ao aspecto estético e de utilização. No entanto, o funcionamento é diferente, uma vez que, cada usuário corresponde a um cliente e um servidor simultâneamente. Assim, pode-se requisitar ou receber requisição de conexão com o mesmo programa. Deve-se salientar que não há servidor intermediário que administra as conexões, ou seja, gera-se uma conexão ponto-a-ponto.

O programa e o seu manual de instruções pode ser obtido em [http://code.google.com/p/chatlan/]
[http://code.google.com/p/chatlan/downloads/list].

Funcionamento

Nesta seção será detalhado o funcionamento do programa em termos de código fonte (classes e objetos) e execução (telas).

Classes e objetos

As classes presentes são:

  • ChatConnection
  • ChatWindow
  • ChatWindowBuilder
  • ClasseExecucao
  • ConnectionWindow
  • GenericClient
  • GenericServer
  • P2PConnection

ClasseExecucao

Responsável por criar o servidor (entenda-se a "thread" do servidor") e a janela de conexão.

new GenericServer(5000);
    new ConnectionWindow();

ConnectionWindow

Cria uma tela de conexão (JFrame) com um textfield e um botão. A função do botão é instanciar um cliente.

public class ConectorActionListener implements ActionListener{
 
        public void actionPerformed(ActionEvent e) {
            new GenericClient(field.getText(), 5000);
        }    
    }

GenericClient

Cria uma conexão (socket) e instancia um objeto ChatConnection.

public GenericClient(String host,int port){
        try{
            clientSocket = new Socket(host,port);
        }catch(Exception e){e.printStackTrace();}
        new ChatConnection(clientSocket);        
    }

ChatConnection e P2PConnection

ChatConnection é subclasse da classe abstrata P2PConnection e implementa o método abstrato useConnection instanciando uma ChatWindow. A classe abstrata P2PConnection cria os canais de entrada e saída (BufferedReader/PrintWriter) responsáveis por receber e enviar informações.

entrada = new BufferedReader(new InputStreamReader(con.getInputStream()));
    saida = new PrintWriter(con.getOutputStream());

ChatWindow

Recebe como parâmetros os canais de entrada e saída e os IPs local e remoto. Cria o frame de chat com o ChatWindowBuilder e seta esse frame como "visible".

ChatWindowBuilder chatBuilder = new ChatWindowBuilder("Messenger Maroto", entrada, saida, localIp, hostIp);
        chatBuilder.CreateAll();
        frame = chatBuilder.getFrame();
        frame.setVisible(true);

ChatWindowBuilder

Builder do frame da tela de chat. Instancia um frame, três panels, um botão e dois textareas com barra de rolagem e os monta ao frame final. Possui um método que realiza toda a montagem.

public void CreateAll(){
        CreateIncomingTextArea();
        CreateOutgoingTextArea();
        CreateSendButton();
        BuildFrame();
    }

GenericServer

Cria uma thread que "ouve" requisições de conexão. Quando recebe uma requisição, cria uma conexão e instancia uma ChatConnection.

public class EsperaConexao implements Runnable{
        public void run() {
            try{
                while(true){
                    Socket client = serverSocket.accept();
                    new ChatConnection(client);
                }    
            }catch(Exception e){e.printStackTrace();}
        }     
    }

Telas

Em seguida, são mostradas as telas do programa.

Tela de Conexão

conexão.jpg

Fig.1 Tela de conexão

Na tela de conexão há um textfield no qual é inserido o IP do usuário a quem se quer conectar. Logo em seguida, clica-se no botão para cria a nova conexão. Pode-se conectar com vários usuários ao mesmo tempo, sendo que cada nova conexão corresponde a uma nova tela de conversação.

Tela de Chat

conversa.jpg

Fig.2 Tela de Conversação

Na tela de conversação há dois textareas, um para envio de texto e outro para recebimento. No de recebimento, aparacem tanto as informações recebidas como as enviadas. Para se enviar texto clica-se no botão de envio.

Padrões de projeto utilizados

Os padrões de projeto utilizados foram o Template e o Builder.

Template

O padrão template foi utilizado na classe P2PConnection, uma vez que ela permite a criação de diversas utilizações para os canais de comunicação através da implementação do método abstrato useConection. No caso, foi criada apenas a subclasse ChatConnection, mas poder-se-ia utilizar a classe P2PConnection para criar um servidor que registra os usuários ativos e os repassa para os outros, de forma que todos os usuários teriam ciência daqueles que estão ativos.

public abstract class P2PConnection {
   (...)
 
    public P2PConnection(Socket con){
        (...)
        useConnection();
    }
 
    public abstract void useConnection();
}

Builder

O padrão builder foi utilizado na criação da tela de conversação, uma vez que ela é um objeto complexo. Nesse caso, o padrão serviu para melhorar a visibilidade na criação da tela, uma vez que, há muito código envolvido.

public class ChatWindowBuilder {    
   (...)
 
    public ChatWindowBuilder(...) {
       (...)
    }
 
    public void CreateIncomingTextArea(){
        (...)
    }
 
    public void CreateOutgoingTextArea(){
        (...)
    }
 
    public void CreateSendButton(){
       (...)
    }
 
    public void BuildFrame(){
       (...)
    }
 
    public void CreateAll(){
       (...)
    }
 
    public JFrame getFrame(){
        return frame;
    }
 
    (...)
 
}

Diagrama de Classes

Abaixo segue o diagram UML para que se possa visualizar as classes do projeto e ver como elas se relacionam.

Diagrama UML

uml.jpg

Fig.3 Diagrama UML

Funções extras

Para melhorar o funcionamento do projeto, implementamos algumas funções extras com o intuito de melhorar a vida do utilitário.

A primeira delas é o envio da mensagem apertanto a tecla enter através do JTextArea, cabe salientar que só apertar enter faz com que enviemos um mensagem já escrita, mas quando se aperta shift+enter pulamos de linha no JTextArea destinado a enviar as mensagens, assim como é no msn.

Para implementar tal ttarefa tive que adionar ao JTextArea um KeyListener e em seguida implementar uma classe interna que extendesse KeyListner chamada EnterListener cujo código colocarei encontra-se abaixo.

public class EnterListener implements KeyListener{          
        private boolean auxiliar = false; // me auxiliará pra eu detectar o shift+enter no TextArea
         public void keyPressed(KeyEvent e) {             
            if(e.getKeyCode() == KeyEvent.VK_SHIFT){
                auxiliar = true;
            }
        }        
 
        public void keyTyped(KeyEvent e) {}
 
        public void keyReleased(KeyEvent e) {
            if (auxiliar == false && (e.getKeyCode() == KeyEvent.VK_ENTER) ){                               
                String[] semEnter = outgoing.getText().split("\\n");
                for(String s:semEnter){
                    saida.println(s);                   
                    saida.flush();
                    String[] str = localIp.split("/");
                    incoming.append(str[1]+" (me): " + s + "\n");                     
                }         
                outgoing.setText("");
                outgoing.requestFocus();
            }
            else if(e.getKeyCode() == KeyEvent.VK_SHIFT){
                auxiliar = false;
            }
            else if(auxiliar == true && (e.getKeyCode() == KeyEvent.VK_ENTER) ){
                outgoing.append("\n");
            }
        }               
     }

Para eu detectar a diferença do shift+enter e do enter puro eu utilizei uma variável de instancia para me auxiliar, setando ela inicialmente como falso (private boolean auxiliar = false), toda vez e enquanto o shift está sendo precionado essa variável recebe o valor de true, assim, sei qd eu tenho um enter sem ter um shift junto e quando eu tenho um enter com um shift junto, basta eu verificar essa variável.

Cabe salientar que o motivo dessa calsse ser uma classe interna se deve ao fato de eu querer utilizar as variável da classe externa a ela, caso contrário teria que ficar passando variáveis ao construtor dessa classe, o que aumentaria o aclopamento e tornando assim menos eficiente o código.

Como segunda implementação extra foi adicionar som. toda vez que o uma das pessaos na conversa fechar a janela, quando a outra lhe mandar uma mensagem, reabriremos a janela anterior, minimizaremos essa janela e tocará um som para despertar a atenção dessa pessoa que tinha fechado sua janela. Para me auxiliar nesse processo utilizei a classe Player de javazoom.jl.payer.Player, cujo download se encontra no link [http://www.javazoom.net/javalayer/javalayer.html]

a implementação dessa funcionalidade se encontra abaixo:

public class IncomingReader implements Runnable{
 
        public void run() {
            String mensagem;
 
            try{                
                while( (mensagem = entrada.readLine()) != null ){                    
                    if(!frame.isShowing() || frame.getExtendedState() == JFrame.ICONIFIED ){ // se o usuário fechar a janela ou ela estive minimizada, ao se enviar                       
                       frame.setExtendedState( JFrame.ICONIFIED ); // evitar que a janela a ser aberta sobreponha as outras.                           
                       try{// também tocara uma musiquinha.
                            File file = new File("sons\\type.mp3");
                            InputStream in = (InputStream) (new  FileInputStream(file));
                            Player plr = new Player(in);
                            plr.play();                            
                            frame.setVisible(true);
                        }catch(Exception e){e.printStackTrace();}
                    } 
                    System.out.println("ler " + mensagem);
                    String[] str = hostIp.split("/");          
 
                    incoming.append(str[1]+" (your friend):   " + mensagem + "\n");
                }
            }catch(Exception e){e.printStackTrace();}            
        }    
    } 
}

A classe em que se encontra tal parte de código é a IncomingReader que extend Runnable e é classe interna de ChatWindow, a razão dela ser classe interna é a mesma da explica anteriormente.

Por fim, não posso nem dizer que isso seja uma funcionalidade extra, mas comentarei que foi adionado uma imagem para substituir o simbolo da java que fica no canto superior esquerdo de toda frame, coloquei a imegem simbolo do msn e para tal utilizei o seguinte comando: frame.setIconImage(imagem);

Conclusão

Enfim, como podemos ver, temos uma aplicação que utiliza conexão ponto a ponto e o envio de informações através dela, que no nosso caso são linhas de texto.

De todas as tarefas realizadas, tanto na minha opinião como na do meu parceiro, essa foi a que mais nos engrandeceu nesse curso. Nos viramos muito sozinhos para aprender inúmeras coisas. Pra se ter idéia, para que implementasse a parte do envio da mensagem através de enter e pular linha com shift+enter, foi uma pesquisa realizada que demorou em torno de 3 horas, mas foi proveitoso no que se diz reipeito ao aprendizado, pois não só aprendi como aprendi a aprender sozinho.

Também pode-se salientar, que com essa aplicação, pudemos utilizar do que foi aprendido no curso de ces-22 e observamos que apesar de meses de estudo aquilo ainda não era suficiente para realizar um projeto maior.

Observamos a grande utilidade de se saber padrões de projetos, conhecendo-os podemos conjecturar códigos mais bem estruturados e "limpos", sem "gambiarras" e mais legíveis. Pode-se dizer inclusive, que o conhecimento de alguns deles, nos faz ter soluções para implementações que sem eles possivelmente não saberiamos como fazer.

Enfim, padrões de projetos são conceitos de implementações para determinadas situações, que nos facilitam para determinados problemas específicos aos quais cada um deles se encaixam.

Estatísticas

  • 8 arq ".java"
  • 302 linhas de código
  • 60 linhas em branco
Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License