Lab5 Thiago Brandão

aluno: Thiago Brandão Damasceno
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 05/12/2008 (10)

Introdução

A prática consistiu no desenvolvimento da parte do servidor de uma aplicação cliente/servidor em Ruby, onde foram utilizados os conceitos de multithread, IO da linguagem, além dos padrões Decorator e Builder.

Desenvolvimento do Backend

Melhoria nos Decorators de Thread Safety

Devido aos problemas de corrida nos métodos make_bet e finalize de Matchlist, foi necessário estender a classe ListThreadSafeDecorator para MatchListThreadSafeDecorator, onde cada item da lista de match passa a ter os métodos sincronizados, isto é, :

#...
def make_bet(score,user)     
      synchronize do
        @decorated.make_bet(score,user)  
      end
    end
 
    def finalize(final_score)
       synchronize do
         @decorated.finalize(final_score)
       end 
    end
#...

Para adicionar o item de decoração na lista de Match, foi utilizado código semelhante à decoração de MatchListLoggerDecorator.

Melhoria nos Decorators de persistência

De maneira semelhante, os métodos make_bet e finalize devem ser modificados de modo que salvem em disco o conteúdo de uma aposta/finalização de uma partida automaticamente, sem ter que chamar explicitamente o método save de MatchList. Para tanto, os métodos foram modificados de foram a chamarem o save.

def initialize(obj,item_decorator)
      super(item_decorator)  #esta linha chama o construtor de Decorator
      @obj = obj  #esta linha guarda a referência de MatchListPersistenceDecorator
    end
 
    def make_bet(score,user) 
      bet = @decorated.make_bet(score,user)
      @obj.save                 #save automatico
      bet
    end
 
    def finalize(final_score)
      final = @decorated.finalize(final_score)
      @obj.save
      final
    end

Assim, de maneira semelhante a anterior, o MatchListPersistenceDecorator deve estender ListPersistenceDecorator e passar a ter como item de decoração a nova classe criada anteriormente.

Criação dos Builders

A criação dos Builders segue o mesmo padrão, exceto pelo fato de se adicionar uma variável booleana para garantir a chamada de load_list caso a persistência tenha sido adicionada na lista.

#exemplo para MatchList, o UserList é igual
def initialize
    @match_list = MatchList.new
    @persistance_added = false #foi adicionada persistência?
  end
 
  def add_persistance(file) #gravar em um arquivo definido pelo administrador
    @match_list = MatchListPersistanceDecorator.new(@match_list,file)
    @persistance_added = true #sim
  end
 
  def add_logging
    @match_list = MatchListLoggerDecorator.new(@match_list)
  end
 
  def add_thread_safety
    @match_list = MatchListThreadSafeDecorator.new(@match_list)
  end
 
  def add_all(file)
    add_persistance(file)
    add_logging
    add_thread_safety #garante sincronia em todas as operações
  end
 
  def match_list
    if @persistance_added  #chama load_list de cada lista caso a persistência
      @match_list.load_list #tenha sido adicionada
    end
    @match_list
  end

Criação do protocolo de comunicação

Para a criação do protocolo, foi escolhido como pacote de serialização o Yaml, por ser de fácil utilização. O protocolo consistirá de hashes com campos request (no caso do cliente) e response (no caso do servidor)). A especificação do protocolo encontra-se abaixo:

#Lado do cliente
 
 #Criação de usuário
     {
          :request => 
            {
              :operation => :create_user,
              :parameters => [user, password]
            }
       }
 
  #Autenticação  
        {
          :request => 
            {
              :operation => :authenticate_user,
              :parameters => [user, password]
            }
       }
 
 #Listar partidas atuais
        {
          :request => 
            {
              :operation => :current_matches
            }
       }
 
#Listar partidas finalizadas
        {
          :request => 
            {
              :operation => :finalized_matches
            }
       }
 
#Listar informações de uma determinada partida
        {
          :request => 
            {
              :operation => :match_info,
              :parameters => match
            }
       }
 
#Listar informações das apostas do usuário logado
        {
          :request => 
            {
              :operation => :bet_info
            }
       }
 
#Fazer uma nova aposta
        {
          :request => 
            {
              :operation => :make_bet,
              :parameters => [score,match_name]
            }
       }
 
#Fechar conexão 
        {
          :request => 
            {
              :operation => :close
            }
       }
 
#Lado do servidor
 
# Responde à requisição "criar usuário"
        {
          :response => error
 
       }
 
# Responde à requisição "autenticar usuário"    
        {
          :response => error
          :flag => flag
 
       }
 
# Responde à requisição "Listar partidas atuais"
        {
          :response => 
            {
              :name => 
              :end_time => 
              :bet_count => 
            }
 
       }
 
 #Responde à requisição "Listar partidas finalizadas"  
        {
          :response =>
            {
              :name => 
              :end_time =>
              :final_score => 
              :winners => 
 
       }
 
 #Responde à requisição "Listar informações de uma partida"
        {
          :response => 
            {
 
              :name => 
              :end_time => 
              :finalized => 
              :final_score => 
              :bets => 
              :bet_count => 
              :winners => 
            }
       }
 
  #ou responde com o seguinte hash:
          {
            :response => "No matches found"
 
          }
 
 # Responde à requisição "Listar informações de apostas"   
        {
 
          :response =>
            {
               :winning_bets => 
               :current_bets => 
               :finalized_bets => 
 
            }
 
        }
 
   #  Responde à requisição "Fazer nova aposta"
      {
 
        :response => error
 
      }
 
 #Responde à requisição "Fechar conexão"
        {
 
          :response => "Connection closed"
 
        }

A parte do processamento de requisições foi toda incluída em uma classe chamada RequestReader que lê o tipo do pedido a partir de hashes com chaves do tipo :request produzido pela classe RequstGenerator no cliente e chama o método correspondente (a escolha é feita através de um case-when). Esse método então envia uma resposta através do campo :response, que indica o status da operação realizada.
Por fim, a função main (em main.rb) irá utilizar um objeto de RequestReader para fazer a leitura e processamento das requisições do cliente dentro de um loop.

Criação do servidor Multithread

O servidor irá ser simplesmente esperar requisições via io_socket (definido como server.accept), chamando o método main dentro de uma Thread, pela porta 4344. Abaixo seguem os resultados preliminares (conexões múltiplas) :

fig5.png

OBS.: O PROJETO SERÁ POSTADO NA PÁGINA DO RELATÓRIO DO LAB 6 (Cliente mais servidor)

Conclusão

A prática permitiu que os conhecimentos de OOP e de sintaxe da linguagem Ruby pudessem ser mais trabalhados e amadurecidos. Além disso, foi uma ótima oportunidade para aplicar mais alguns padrões de projeto novos, além de se familiarizar com o conceito de threads, sockets e serialização de estruturas de dados. O desenvolvimento da parte do servidor poderia ser mais livre (visto que os códigos de testes limitavam a implementação pessoal do aluno).

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