Lab6 Misael Alexandre

aluno: Misael Alexandre
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 11/12/2008 (11)

Introdução

Este laboratório visa complementar e completar o Backend (parte do servidor) do Bolão Virtual desenvolvido no quinto laboratório através do desenvolvimento de um cliente com interface gráfica que se conecta com o servidor, envia requisições para o mesmo, recebe e mostra as devidas respostas. A dupla foi inicialmente feita com o aluno Tiago Porto. Como não houve tempo nem possibilidade de apresentar o projeto com a dupla, neste relatório será descrito o protocolo que a dupla adotou. O cliente que será apresentado neste laboratório foi feito em Java, usando a biblioteca de GUI Swing.

Desenvolvimento

O desenvolvimento deste laboratório pode ser dividido nas seguintes partes:

  • Escolha do protocolo adequado
  • Criação das telas (sem função lógica)
  • Criação da conexão com o servidor
  • Desenvolvimento da parte lógica das telas
  • Testes finais

Escolha do protocolo adequado

Antes de começar o desenvolvimento do Cliente propriamente dito, a dupla resolveu adotar um protocolo quando o quinto laboratório ainda estava sendo realizado. O protocolo adotado foi o mesmo sugerido na instrução do quinto laboratório, ou seja, baseado em Yaml. Tal protocolo foi escolhido graças a facilidade de utilização do mesmo, tanto em Ruby (já feito no quinto laboratório) como supostamente em Java, já que existe uma biblioteca chamada JYaml que ajuda a trabalhar com Yaml.

Assim, a descrição breve do protocolo utilizado é:

Criar usario

O pedido de criação do usuário era da forma:

INICIO
---
request: 
 operation: create_user 
 parameters: 
  - (name) 
  - (password)
FIM

Ao ser criado com sucesso, a resposta era:

INICIO
---
:response:
  :created: true
FIM

Ao haver erro na criação, a resposta era:

INICIO
---
:response:
  :created: false
FIM

Autenticar usario

O pedido de autenticação do usuário era da forma:

INICIO
---
request: 
 operation: autenticate_user 
 parameters: 
  - (name) 
  - (password)
FIM

Ao ser autenticado com sucesso, a resposta era:

INICIO
---
:response:
  :autenticate: true
FIM

Ao haver erro na autenticação, a resposta era:

INICIO
---
:response:
  :autenticate: false
FIM

A partir daqui, o protocolo das operações somente feitas por usuários logados foi:

Listar partidas atuais

O pedido de listar as partidas atuais era da forma:

INICIO
---
request: 
 operation: current_matches 
FIM

A resposta dada pelo servidor era então da forma:

INICIO
---
:response:
- :name: (name)
  :end_time: ("%d/%m/%Y %H:%M")
  :bet_count: (bet counter)
FIM

Listar partidas encerradas

O pedido de listar as partidas encerradas era da forma:

INICIO
---
request: 
 operation: finalized_matches 
FIM

A resposta dada pelo servidor era então da forma:

INICIO
---
:response:
- :name: (name)
  :end_time: ("%d/%m/%Y %H:%M"),
  :finalized: (finalized or not)
  :final_score: ("_ x _")
  :bet_count: (bet counter)
  :winners: (winner users)
FIM

Informações detalhadas de uma partida

O pedido de informações detalhadas de uma partida foi:

INICIO
---
request: 
 operation: match_information 
FIM

A resposta mandada pelo servidor era da forma:

INICIO
---
:response:
  :name: (name)
  :end_time: ("%d/%m/%Y %H:%M"),
  :finalized: (finalized or not)
  :final_score: ("_ x _")
  :bet_count: (bet counter)
  :holders: (holders)
  :winners: (winners)
FIM

Fazer aposta em uma partida

O pedido para fazer uma aposta em determinada partida era da forma:

INICIO
---
request: 
 operation: make_bet
 parameters:
  - (name of match)
  - (score)
FIM

Se a aposta era realizada com sucesso, a resposta era da forma:

INICIO
---
:response:
  :bet_made: true
FIM

Se havia erro na realização da aposta, a resposta era da forma:

INICIO
---
:response:
  :bet_made: false
FIM

Listar apostas pessoais

O pedido de listar apostas pessoais era da forma:

INICIO
---
request: 
 operation: own_bets_information
FIM

A resposta dada pelo servidor era então da forma:

INICIO
---
:response:
- :name: (name)
  :end_time: ("%d/%m/%Y %H:%M"),
  :finalized: (finalized or not)
  :final_score: ("_ x _")
  :bet_count: (bet counter)
  :holders: (holders)
  :winners: (winners)
FIM

Encerrar a conexão

O pedido para encerrar conexão foi da forma:

INICIO
---
request: 
 operation: close_connection
FIM

Se a conexão era encerrada com sucesso, a resposta era da forma:

INICIO
---
:response:
- :connection: close
FIM

Criação das telas (sem função lógica)

A partir daqui começa o trabalho do sexto laboratório em si. Inicialmente, a partir de uma conversa com alguns colegas sobre a melhor forma de agir para criar uma interface gráfica com Netbeans usando Swing, resolveu-se criar inicialmente todas as telas a serem utilizadas (sem função lógica) para depois criar o programa em si. Após aproximadamente uma hora de estudo de tutoriais sobre criação de telas com Netbeans, percebeu-se que esta é uma tarefa relativamente simples, já que o Netbeans disponibiliza uma interface muito prática de criação.
Inicialmente foi criado um projeto denominado BolaoVirtual. Nele, criou-se um package denominado telas. Neste pacote, foram criadas as diversas telas necessárias para todas as funções implementadas pelo protocolo. A execução desta parte do trabalho foi relativamente rápida se comparada com o restante, não havendo maiores problemas. Os poucos que houveram (ex: como colocar barra de rolagem em uma Tabela???) puderam ser rapidamente sanados por pesquisas no Google, já que há bastante material disponível sobre Swing+Netbeans.

A relação das telas, juntamente com as figuras do esboço de cada tela, podem ser vistas a seguir.

Tela de Login

Denominada LoginGUI.java, esta tela recebia o username e o password do usuário que queria fazer login. Também tinha um botão "Create User", que chamava outra tela para criar um usuário. O esboço de LoginGUI.java pode ser visto abaixo:

login.jpg

Tela de Criação de Usuário

Denominada Create_UserGUI.java, esta tela recebia o username e o password de uma pessoa que queria se cadastrar como usuário do Bolão Virtual. O esboço de Create_UserGUI.java pode ser visto abaixo:

createuser.jpg

Tela de usário logado

Denominada LogadoGUI.java, esta tela, além do fundo personalizado do Bolão Virtual, continha os menus das diversas funcionalidades do programa, as quais foram agrupadas em três conjuntos de funcionalidades:

  • Meu Menu: continha as funcionalidades de fazer aposta e visualizar apostas do usuário
  • Menu Geral: continha as funcionalidades de mostrar partidas atuais, partidas finalizadas e informações detalhadas de uma partida
  • Encerrar: continha a funcionalidade de encerrar conexão.

O esboço de LogadoGUI.java pode ser visto abaixo:

logado.jpg

Tela de Partidas Atuais

Denominada Current_MatchesGUI.java, esta tela listava as partidas atuais com informações básicas das mesmas. O esboço de Current_MatchesGUI.java pode ser visto abaixo:

currentmatches.jpg

Tela de Partidas Finalizadas

Denominada Finalized_MatchesGUI.java, esta tela listava as partidas finalizadas com algumas informações a mais que a tela de partidas atuais. O esboço de Finalized_MatchesGUI.java pode ser visto abaixo:

finalizedmathes.jpg

Tela de Entrada do Nome da Partida

Denominada Name_MatchGUI.java, esta tela recebia o nome da partida da qual o usuário desejava obter informações detalhadas. O esboço de Name_MatchGUI.java pode ser visto abaixo:

namematch.jpg

Tela de Informações de uma Partida

Denominada Match_InformationGUI.java, esta tela listava as informações da partida cujo nome foi inserido em Name_MatchGUI.java. O esboço de Match_InformationGUI.java pode ser visto abaixo:

matchinformation.jpg

Tela de Fazer Aposta

Denominada Make_BetGUI.java, esta tela recebia o nome da partida e o score que o usuário desejava apostar. O esboço de Make_BetGUI.java pode ser visto abaixo:

make_bet.jpg

Tela de Listagem de Apostas

Denominada My_BetsGUI.java, esta tela listava as partidas que o usuário havia apostado. O esboço de My_BetsGUI.java pode ser visto abaixo:

mybets.jpg

Criação da conexão com o servidor

Após a confecção das telas, teve início a criação da parte lógica do programa. Inicialmente, tentou-se estabelecer a conexão com o servidor. Seguindo os passos dados pelo tutorial apresentado na página de instruções do laboratório, criou-se a conexão com o servidor no método main. Entretanto, aqui apareceu a primeira grande dificuldade na elaboração deste laboratório. Havia a necessidade, em outras telas, de usar-se o Socket e os streams criados. Inicialmente, tentou-se passar os streams como parâmetro, da mesma maneira que foi feito no quinto laboratório. Horas e horas de tentativas usando tal método se mostraram em vão. Por fim, através de uma pesquisa no Google (que por sinal não foi fácil de achar), teve-se o "insight" de fazer as conexão do servidor em um método na primeira tela chamada. E quanto ao Socket e aos streams, os mesmos foram colocados como atributos estáticos, de forma a poderem ser chamados de outras classes e serem imutáveis.
Assim, foram criados os métodos listenSocket e closeSocket na classe LoginGUI.java. O código dos métodos e dos atributos relacionados pode ser visto abaixo.

static Socket echoSocket = null;
    static PrintWriter out = null;
    static BufferedReader in = null;
 
    public void listenSocket(){
 
        try {
            echoSocket = new Socket("localhost", 4344);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(
                                        echoSocket.getInputStream()));
 
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: localhost.");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for "
                               + "the connection to: localhost.");
            System.exit(1);
        }
 
    BufferedReader stdIn = new BufferedReader(
                                   new InputStreamReader(System.in));
 
        String userInput;
 
    }
 
    public static void closeSocket() throws IOException{
        in.close();
        out.close();
        echoSocket.close();
 
    }

Desenvolvimento da parte lógica das telas

Esta foi a parte realmente trabalhosa do laboratório, assim como a mais importante. Para cada uma das funcionalidades, foi desenvolvido um código que mandava uma requisição para o servidor, recebia a resposta e interpretava. Também houveram dúvidas nesta parte, mas nada tão trabalhoso de ser sanado quanto a criação da conexão com o servidor.

Antes de entrar na parte do código propriamente dita, foi necessário um estudo da biblioteca JYaml. Diferentemente de Ruby, na qual o texto em Yaml é mapeado para um hash, ao usar JYaml o mapeamento é OBJETO-TEXTO EM YAML. Assim, fez-se necessário criar classes cujas instâncias fossem objetos que pudessem ser mapeados pela biblioteca em um texto em Yaml e vice-versa.

BREVE DESCRIÇÃO DE JYAML

Em JYaml, existem dois tipos de entidades: sequences e mappings.

Sequences são seqüências de expressões e normalmente são mapeadas por Arrays. Como exemplo, no retorno do request: current_matches, é retornado uma seqüência de jogos, que são listadas por - seguido do ítem da seqüencia.

INICIO
---
:response:
- :name: (name)
  :end_time: ("%d/%m/%Y %H:%M")
  :bet_count: (bet counter)
- :name: (name2)
  :end_time: ("%d/%m/%Y %H:%M")
  :bet_count: (bet counter2)
FIM

Mappings são coleções de chave-valor, na qual sempre tem-se a chave, seguida de ":", seguida do valor. Mappings são normalmente mapeadas por Hashes. Como exemplo, temos a resposta mandada pelo servidor para o request: match_information.

INICIO
---
:response:
  :name: (name)
  :end_time: ("%d/%m/%Y %H:%M"),
  :finalized: (finalized or not)
  :final_score: ("_ x _")
  :bet_count: (bet counter)
  :holders: (holders)
  :winners: (winners)
FIM

Assim, nem sempre a resposta mandada pelo servidor era para ser mapeada por um Hash, e nem sempre para um Array. Desta maneira, resolveu-se criar três classes para mapeamento de requests e responses:

  • Response, para quando a resposta fosse um Hash
  • Response2, para quando a resposta fosse um Array
  • Request, já que o request era sempre um Mapping.

Importante salientar que na documentação de JYaml dizia que tais classes deveria ter construtores nulos, ou seja, construtores sem parâmetros. O código das classes Request, Response e Response2 pode ser visto abaixo.

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
 
package aplication;
 
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
 
/**
 *
 * @author M
 */
public class Request {
 
    public Request() {
 
    }
 
    public HashMap request;
 
    public HashMap getRequest() {
        return request;
    }
 
    public void setRequest(HashMap request) {
        this.request = request;
    }
 
}
package aplication;
 
import java.util.HashMap;
 
public class Response {
 
    public Response() {
 
    }
 
    public HashMap response;
 
    public HashMap getResponse() {
        return response;
    }
 
    public void setResponse(HashMap response) {
        this.response = response;
    }
 
}
package aplication;
 
import java.util.ArrayList;
 
public class Response2 {
 
    public Response2() {
 
    }
 
    public ArrayList response;
 
    public ArrayList getResponse() {
        return response;
    }
 
    public void setResponse(ArrayList response) {
        this.response = response;
    }
 
}

Por fim, através do estudo dos métodos da classe Yaml (inclusa na biblioteca), escolheu-se dois métodos que foram usados em toda a aplicação:

  • dump, que transformava o objeto criado em um texto (String) em formato Yaml
  • loadType, que transformava o texto lido do servidor (já inicialmente tratado) em um objeto tratável

Através destas considerações, pode-se passar para a parte de implementação das funcionalidades.

Criação da funcionalidade de criar usuário

Inicialmente, foi feito que, ao se clicar no botão "Create User" da tela de Login, fosse aberta uma outra tela (instância de Create_UserGUI.java) na qual os dados de cadastro do usuário. Ao apertar o botão "Save", era formado um texto em formato Yaml (delimitado pelas palavras INICIO e FIM), o qual era mandado pelo Stream de saída. Então, do Stream de entrada, era lida a resposta do servidor e a mesma era interpretada (transformada em objeto), o qual era manipulado a fim de se mostrar uma resposta ao usuário. Tanto na formação do Yaml de saída como na interpretação do Yaml de entrada, foram feitos pequenos ajustes para que o texto ficasse no formato Yaml padrão, como colocar (na saída) e retirar (na entrada) as palavras INICIO e FIM. O código colocado no método jButton1ActionPerformed (método executado ao clicar no botão Save) pode ser visto abaixo.

String username = jTextField1.getText();
    String password = jTextField2.getText();
 
    ArrayList arr = new ArrayList();
 
    HashMap arr2=new HashMap();
 
    arr.add(username);
    arr.add(password);
 
    arr2.put("operation","create_user");
    arr2.put("parameters",arr);
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    out=LoginGUI.out;
    out.println(s);
 
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":created:", "created:");
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
    Response resposta = Yaml.loadType(complete_return,Response.class);
 
    if((Boolean)resposta.getResponse().get("created")==true){
        JOptionPane.showMessageDialog(this,
        "User created successfully",
        "User created",
        JOptionPane.PLAIN_MESSAGE);
    }
    else{
        JOptionPane.showMessageDialog(this,
        "User not created",
        "User not created",
        JOptionPane.PLAIN_MESSAGE);
    }
 
   this.dispose();

Criação da funcionalidade de login

Criado de maneira bastante parecida com a criação de usuário, recebia a resposta do servidor e, de acordo com a mesma, chamava ou não a tela de usuário logado. O código do método ok_buttonActionPerformed (chamado ao clicar no botão Ok da tela de login) pode ser visto abaixo.

String username = username_text.getText();
    String password = password_text.getText();
 
    ArrayList arr = new ArrayList();
 
    HashMap arr2=new HashMap();
 
    arr.add(username);
    arr.add(password);
 
    arr2.put("operation","autenticate_user");
    arr2.put("parameters",arr);
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    out=LoginGUI.out;
    out.println(s);
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":autenticate:", "autenticate:");
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
    Response resposta = Yaml.loadType(complete_return,Response.class);
 
    if((Boolean)resposta.getResponse().get("autenticate")==true){
        LogadoGUI c=new LogadoGUI();
        c.setVisible(true);
        this.dispose();
    }
    else{
        username_text.setText("");
        password_text.setText("");
        JOptionPane.showMessageDialog(this,
        "Username or password wrong",
        "Login Error",
        JOptionPane.PLAIN_MESSAGE);
    }

Criação da funcionalidade de mostrar partidas atuais

Também muito parecida com as funcionalidades anteriores, mas com duas diferenças significativas. Primeiro, como a resposta dada pelo servidor era um conjunto de partidas (sequence), a classe de mapeamento da resposta não foi Response (para HashMap), mas Response2 (para ArrayList). Através do objeto "resposta" criado, criava-se uma instância de Current_MatchesGUI.java e a tabela da mesma era populada com os dados do objeto "resposta". Assim, o código do método jMenuItem3ActionPerformed (chamado ao clicar-se no menu "Partidas atuais" da tela de usuário logado) pode ser visto abaixo.

HashMap arr2=new HashMap();
 
    arr2.put("operation","current_matches");
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    LoginGUI.out.println(s);
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":name:", "name:");
                line=line.replaceAll(":end_time:", "end_time:");
                line=line.replaceAll(":bet_count:", "bet_count:");
                if(!(line.equals("response: ")||line.equals("--- "))){
                    line="  "+line;
                }
 
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
    complete_return=complete_return+"\n";
 
    Response2 resposta = Yaml.loadType(complete_return,Response2.class);
 
    if(!resposta.getResponse().isEmpty()){
        Current_MatchesGUI b=new Current_MatchesGUI();
        DefaultTableModel modelo = (DefaultTableModel)b.getJTable1().getModel();
        modelo.setNumRows(0);
        int tam=resposta.getResponse().size();
        for(int i=0;i<tam;i++){
           HashMap item=(HashMap)resposta.getResponse().get(i);
           String name=(String) item.get("name");
           String bet_count=(String) item.get("bet_count");
           String end_time=(String) item.get("end_time");
 
           modelo.addRow(new Object [] {name, end_time, bet_count});
        }
 
        b.setVisible(true);
    }
    else{
        JOptionPane.showMessageDialog(this,
        "There are no matches happening",
        "No Matches",
        JOptionPane.PLAIN_MESSAGE);
    }

Criação da funcionalidade de mostrar partidas finalizadas

Feita totalmente de maneira análoga a funcionalidade anterior, seu código pode ser visto abaixo.

HashMap arr2=new HashMap();
 
    arr2.put("operation","finalized_matches");
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    LoginGUI.out.println(s);
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":name:", "name:");
                line=line.replaceAll(":end_time:", "end_time:");
                line=line.replaceAll(":bet_count:", "bet_count:");
                line=line.replaceAll(":finalized:", "finalized:");
                line=line.replaceAll(":winners:", "winners:");
                line=line.replaceAll(":final_score:", "final_score:");
                if(!(line.equals("response: ")||line.equals("--- "))){
                    line="  "+line;
                }
 
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
    complete_return=complete_return+"\n";
 
    Response2 resposta = Yaml.loadType(complete_return,Response2.class);
 
    if(!resposta.getResponse().isEmpty()){
        Finalized_MatchesGUI b=new Finalized_MatchesGUI();
        DefaultTableModel modelo = (DefaultTableModel)b.getJTable1().getModel();
        modelo.setNumRows(0);
        int tam=resposta.getResponse().size();
        for(int i=0;i<tam;i++){
           HashMap item=(HashMap)resposta.getResponse().get(i);
           String name=(String) item.get("name");
           String bet_count=(String) item.get("bet_count");
           String end_time=(String) item.get("end_time");
           String finalized=(String) item.get("finalized");
           String winners=(String) item.get("winners");
           String final_score=(String) item.get("final_score");
 
           modelo.addRow(new Object [] {name, end_time, finalized, final_score, bet_count, winners});
        }
 
        b.setVisible(true);
    }
    else{
        JOptionPane.showMessageDialog(this,
        "There are no finalized matches",
        "No Finalized Matches",
        JOptionPane.PLAIN_MESSAGE);
    }

Criação da funcionalidade de mostrar informações detalhadas de uma partida

Também criada de maneira parecida com as funcionalidades anteriores, sua diferença das outras funcionalidades já mostradas de usuário logado é que na sua requisição é mandado uma parâmetro (nome da partida). Assim, ao se clicar no menu "Informações de Partida" da tela de usuário logado, chamava-se uma tela na qual era digitado o nome da partida e então, ao clicar-se no botão Show desta tela, era populada uma tabela com a informação retornada pelo servidor, de maneira análoga às funcionalidades anteriores. O código e requisição e leitura do retorndo do servidor pode ser visto abaixo.

String name_match=jTextField1.getText();
 
    HashMap arr2=new HashMap();
 
    ArrayList arr = new ArrayList();
 
    arr.add(name_match);
 
    arr2.put("operation","match_information");
    arr2.put("parameters", arr);
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    LoginGUI.out.println(s);
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":name:", "name:");
                line=line.replaceAll(":end_time:", "end_time:");
                line=line.replaceAll(":bet_count:", "bet_count:");
                line=line.replaceAll(":finalized:", "finalized:");
                line=line.replaceAll(":winners:", "winners:");
                line=line.replaceAll(":holders:", "holders:");
                line=line.replaceAll(":final_score:", "final_score:");
                if(!(line.equals("response: ")||line.equals("--- "))){
                    line="  "+line;
                }
 
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
    complete_return=complete_return+"\n";
 
    Response resposta = Yaml.loadType(complete_return,Response.class);
 
    if(!resposta.getResponse().isEmpty()){
       Match_InformationGUI b=new Match_InformationGUI();
       DefaultTableModel modelo = (DefaultTableModel)b.getJTable1().getModel();
       modelo.setNumRows(0);
       HashMap item=(HashMap)resposta.getResponse();
       String name=(String) item.get("name");
       String bet_count=(String) item.get("bet_count");
       String end_time=(String) item.get("end_time");
       String finalized=(String) item.get("finalized");
       String winners=(String) item.get("winners");
       String final_score=(String) item.get("final_score");
       String holders=(String) item.get("holders");
       modelo.addRow(new Object [] {name, end_time, finalized, final_score, bet_count, winners, holders});
       b.setVisible(true);
    }
    else{
        JOptionPane.showMessageDialog(this,
        "There is no match with this name",
        "No Match",
        JOptionPane.PLAIN_MESSAGE);
    }
 
    this.dispose();

Criação da funcionalidade de fazer uma aposta

Também criada de maneira parecida com as anteriores, chamava uma tela na qual se entrava com a partida e o score que se desejava apostar. Era então mandada uma requisição para o servidor e, dependendo da resposta, era exibida uma mensagem de criação ou não da aposta. O código do método jButton1ActionPerformed (chamado ao submeter os dados do nome da partida e do score) pode ser visto abaixo.

String name_match=jTextField1.getText();
    String score=jTextField2.getText();
 
    HashMap arr2=new HashMap();
 
    ArrayList arr = new ArrayList();
 
    arr.add(name_match);
    arr.add(score);
 
    arr2.put("operation","make_bet");
    arr2.put("parameters", arr);
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    LoginGUI.out.println(s);
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":bet_made:", "bet_made:");
                if(!(line.equals("response: ")||line.equals("--- "))){
                    line="  "+line;
                }
 
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
   complete_return=complete_return+"\n";
 
    Response resposta = Yaml.loadType(complete_return,Response.class);
 
    if((Boolean)resposta.getResponse().get("bet_made")==true){
       JOptionPane.showMessageDialog(this,
        "Bet made sucessfully",
        "Bet made",
        JOptionPane.PLAIN_MESSAGE);
    }
    else{
        JOptionPane.showMessageDialog(this,
        "Bet not made",
        "Bet not made",
        JOptionPane.PLAIN_MESSAGE);
    }
 
    this.dispose();

Criação da funcionalidade de mostrar as partidas que o usuário apostou

Também feita de maneira parecida com as anteriores, seu código pode ser visto abaixo.

HashMap arr2=new HashMap();
 
    arr2.put("operation","own_bets_information");
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    LoginGUI.out.println(s);
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":name:", "name:");
                line=line.replaceAll(":end_time:", "end_time:");
                line=line.replaceAll(":bet_count:", "bet_count:");
                line=line.replaceAll(":finalized:", "finalized:");
                line=line.replaceAll(":winners:", "winners:");
                line=line.replaceAll(":holders:", "holders:");
                line=line.replaceAll(":final_score:", "final_score:");
                if(!(line.equals("response: ")||line.equals("--- "))){
                    line="  "+line;
                }
 
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
    Response2 resposta = Yaml.loadType(complete_return,Response2.class);
 
    if(!resposta.getResponse().isEmpty()){
        My_BetsGUI b=new My_BetsGUI();
        DefaultTableModel modelo = (DefaultTableModel)b.getJTable1().getModel();
        modelo.setNumRows(0);
        int tam=resposta.getResponse().size();
        for(int i=0;i<tam;i++){
           HashMap item=(HashMap)resposta.getResponse().get(i);
           String name=(String) item.get("name");
           String bet_count=(String) item.get("bet_count");
           String end_time=(String) item.get("end_time");
           String finalized=(String) item.get("finalized");
           String winners=(String) item.get("winners");
           String holders=(String) item.get("holders");
           String final_score=(String) item.get("final_score");
 
           modelo.addRow(new Object [] {name, end_time, finalized, final_score,bet_count, holders, winners});
        }
 
        b.setVisible(true);
    }
    else{
        JOptionPane.showMessageDialog(this,
        "You have no bets",
        "No bets",
        JOptionPane.PLAIN_MESSAGE);
    }

Criação da funcionalidade de encerrar conexão

Ao clicar-se no menu de encerrar conexão, é chamado o método jMenuItem6ActionPerformed da classe LogadoGUI.java. Neste método, manda-se a requisição de encerrar conexão ao servidor e, sendo retornada uma resposta satisfatória, é chamado o método estático closeSocket da classe LoginGUI.java, o qual encerra os atributos estáticos echoSocket, in e out desta classe, responsáveis pela conexão. A janela de usuário logado é por fim fechada. O código do método pode ser visto abaixo.

HashMap arr2=new HashMap();
 
    arr2.put("operation","close_connection");
 
    Request req = new Request();
    req.setRequest(arr2);
 
    String s=Yaml.dump(req, true);
    s="INICIO\n"+s+"FIM";
 
    LoginGUI.out.println(s);
    String line="";
    String complete_return="";
    try {
        line = LoginGUI.in.readLine();
    } catch (IOException ex) {
        System.out.println("Deu erro");
    }
 
    while(!(line.equals("FIM"))){
        try {
            if (!line.equals("INICIO")){
                line=line.replaceAll(":response:", "response:");
                line=line.replaceAll(":connection:", "connection:");
                if(!(line.equals("response: ")||line.equals("--- "))){
                    line="  "+line;
                }
 
                complete_return=complete_return+"\n"+line;
            }
            line=LoginGUI.in.readLine();
        } catch (IOException ex) {
            System.out.println("Deu erro");
        }
    }
 
    complete_return=complete_return+"\n";
 
    Response resposta = Yaml.loadType(complete_return,Response.class);
 
    if(resposta.getResponse().get("connection").equals("close")){
 
       try {
            LoginGUI.closeSocket();
            this.dispose();
       } catch (IOException ex) {
            System.out.println("Erro ao fechar conexao");                                          
       } 
 
    }
    else{
        JOptionPane.showMessageDialog(this,
        "It is not possible to close the connection",
        "Error closing connection",
        JOptionPane.PLAIN_MESSAGE);
    }

Por fim, no método main, colocou-se uma criação de uma instância da janela de login, uma chamada ao método que estabelece a conexão (listenSocket) e por fim tornar esta tela visível. Daí pra frente o programa funciona como já descrito.

Testes finais

Por fim, foi feito um teste para mostrar o bom funcionamento do programa.

Com o Netbeans, inicialmente colocou-se o servidor para rodar. Logo após, rodou-se o aplicativo criado. Inicialmente foi-se para a tela de criação de usuário. Um usuário com o username "bernardo" e o password "professor" foi criado.

criaruser.jpg

O sistema retorna uma mensagem que o usuário foi criado com sucesso.

usercreatedsuccess.jpg

Em seguida, tentou-se fazer o login com o usuário criado.

login2

Ao se clicar no botão Ok, abre-se a tela de usuário logado.

logged.jpg

Em seguida, faz-se a requisição de mostrar as partidas atuais, sendo o resultado mostrado abaixo.

current_matches.jpg

Faz-se também a requisição de mostrar as partidas finalizadas.

finalized_matches.jpg

Para listar informações de uma partida, tentou-se inicialmente um nome de partida inválida. O sistema retorna uma notificação de que a partida não existe.

nome_partida_invalida.jpg

Logo após tenta-se um nome correto. O resultado satisfatório pode ser visto abaixo.

nome_partida_valido.jpg

Tentou-se agora mostrar as apostas do usuário "bernardo". Como esperado, foi mostrado uma mensagem que o usuário não tinha feito nenhuma aposta ainda.

semapostas.jpg

Agora tentou-se fazer uma aposta. O resultado foi satisfatório foi obtido.

makebet.jpg

Novamente tentou-se listar as apostas do usuário. Desta vez, foi mostrado a aposta já realizada.

comapostas.jpg

Por fim, tentou-se encerrar a conexão, sendo a janela fechada em seguida.

encerrar.jpg

Todos os testes ocorreram satisfatoriamente, como previsto.

O código do projeto pode ser baixado no link abaixo

Laboratório 6

Conclusão

Como já comentado, a grande dificuldade da execução deste laboratório não foi a criação da parte gráfica em si, mas como fazer uma conexão com o servidor que pudesse ser utilizada por toda a aplicação (algumas horas de tentativa) e como utilizar corretamente a biblioteca JYaml. A criação de telas, graças a ajuda do Netbeans, foi quase trivial.
Muito diferente, pelo que se pôde conversar com o aluno que inicialmente era a dupla para este laboratório, foi fazer a interface gráfica em Ruby. O aluno que fez em Ruby, não tendo uma ferramenta de fácil criação de telas como a utilizada neste laboratório, gastou horas para aprender a biblioteca de GUI de Ruby e muito pouco tempo na manipulação de textos em Yaml e conexão com o servidor (ambos já estavam meio que aprendidos graças ao quinto laboratório).
Para a execução deste laboratório, ao contrário do quinto laboratório (já voltado para isto), não foram utilizados muitos dos conhecimentos novos aprendidos em sala de aula. Algo muito importante, que praticamente foi a chave para a criação da conexão com o servidor, foi o conceito de variáveis estáticas. Outro conhecimento de Orientação a Objetos bastante utilizado na execução deste laboratório foi o tratamento de excessões (IOException) cada vez que um stream era acessado. O conceito em si de objeto (básico) também foi necessário para se entender o mapeamento entre texto em Yaml e um objeto de uma classe (Request, Response ou Response2).

Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License