Lab5 Alexandre

aluno: Alexandre Couto Albizzati
ano/sem: 2008/2o.

Introdução

Neste 5º laboratorio, foi desenvolvido, a partir de um código inicial fornecido, um servidor multi-thread para uma aplicação do tipo "bolão virtual".
Esta atividade foi util para uma consolidação do conteúdo de todo o curso, já que abrangeu muitas idéias de OO e a complexidade do código foi bem maior do que no caso das atividades anteriores.

Desenvolvimento

Inicialmente, foram implementadas as melhorias no Decorators, da maneira como as intruções sugeriam. Com essas melhorias nós tornamos a nossa aplicação de fato, thread safe. Os testes foram realizados e após um certo trabalho, elas passaram. Abaixo segue o código das classes acrescentadas:

require 'decorators/decorator' 
require 'monitor' 
 
module Decorators 
 
  class MatchPersistanceDecorator < Decorator 
 
    include MonitorMixin 
 
    attr_writer :match_list 
 
    def make_bet(score, user) 
      variavel = @decorated.make_bet(score,user) 
      @match_list.save 
      variavel 
    end 
 
    def finalize(final_score) 
      variavel = @decorated.finalize(final_score) 
      @match_list.save 
      variavel 
    end 
 
  end 
end
require 'yaml' 
require 'decorators/list_persistance_decorator' 
 
module Decorators 
  class MatchListPersistanceDecorator < ListPersistanceDecorator 
 
   def initialize(decorated, filename) 
      old_match_decorator = decorated.item_decorator 
      decorated.item_decorator = lambda do |match| 
        MatchPersistanceDecorator.new( old_match_decorator.call(match) ) 
      end 
      super 
   end     
 
    def add(*args) 
      obj = @decorated.add(*args) 
      obj.match_list=self 
      save 
      obj 
    end 
 
  def each(&block) 
   @decorated.items.sort_by {|m| m.end_time }.each(&block) 
  end     
 
  def find_by_name(name) 
    var = super                         # chama find_by_name, que retorna um match 
    var.match_list =  self          # grava na variável match_list a match list desse match 
    var                                      # retorna o valor que find_by_name deveria retornar 
  end 
 
end 
end
require 'monitor' 
require 'decorators/decorator' 
module Decorators 
  class MatchThreadSafeDecorator < Decorator 
    include MonitorMixin 
 
    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 
 
  end 
end
require 'decorators/decorator' 
require 'monitor' 
module Decorators 
  class MatchListThreadSafeDecorator < Decorator 
     include MonitorMixin 
 
    def initialize(decorated) 
      old_match_decorator = decorated.item_decorator 
      decorated.item_decorator = lambda do |match| 
        MatchThreadSafeDecorator.new( old_match_decorator.call(match) ) 
      end 
      super 
    end 
 
    def add(name, end_time) 
      synchronize do 
        @decorated.add(name, end_time) 
      end 
    end 
 
  end 
end

Seguindo o roteiro do lab, foram criados em seguida os builders para UserList e MatchList, já com as decorações. Ambas as classes criadas são mostradas abaixo:

require 'decorators/decorator' 
require 'decorators/list_thread_safe_decorator' 
require 'decorators/user_list_logger_decorator' 
require 'decorators/list_persistance_decorator' 
 
class UserListBuilder 
 
   include Decorators 
  attr_accessor :filename   
  def initialize(filename="userlist.yml") 
    @filename = filename 
    @user_list = UserList.new 
    @added_persistance = false 
  end 
 
  def add_persistance 
      @user_list = ListPersistanceDecorator.new(@user_list,@filename) 
      @added_persistance = true 
  end 
 
  def add_logging 
       @user_list =UserListLoggerDecorator.new(@user_list) 
  end 
 
  def add_thread_safety 
        @user_list =ListThreadSafeDecorator.new(@user_list) 
  end 
 
    def add_all 
      add_persistance 
      add_logging 
      add_thread_safety 
    end 
 
    def user_list 
    @user_list.load_list if @added_persistance 
    @user_list 
  end 
end
require 'match_list' 
require 'initialize' 
require 'match' 
 
class MatchListBuilder 
 
  include Decorators 
  attr_accessor :filename 
 
  def initialize(filename = "matchlist.yml") 
    @filename = filename 
    @match_list = MatchList.new 
    @added_persistance = false 
  end 
 
  def add_persistance 
      @match_list  = MatchListPersistanceDecorator.new(@match_list ,@filename) 
      @added_persistance = true 
  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 
      add_persistance 
      add_logging 
      add_thread_safety 
    end 
 
def match_list 
    @match_list .load_list if @added_persistance 
    @match_list 
  end 
end

Após a conclusão dos builders, partimos para o desenvolvimento do protocolo em dupla (minha dupla foi o Luty). Criou-se então a classe main que iria ter uma chamada em loop dentro dela para uma outra classe chamada RequestReader. Esta última classe é o "coração" da aplicação, sendo ela a responsável por reconhecer todas as entradas do servidor. Adotou-se o yaml para ser o nosso padrão de "conversa" com o servidor. As operações que deveriam ser implementadas seriam: create_user, login_user, list_current_matches, list_finalized_matches, show_match, show_my_bets, make_bet e close_connection. As operações que eu não consegui implementar foram as relacionadas às "bets" e a "show_match". Todas as outras funcionaram corretamente.
Eu tive uma demora muito grande, porque não estava conseguindo achar um bug. Eu estava conseguindo criar e logar o usuário. Conseguia também finalizar a conexão, porém o programa simplesmente parecia estar ignorando as minhas tentativas de listar os matches com: "list_current_matches" e "list_finalizes_matches". Depois de muito tempo sem que eu conseguisse encontrar o erro (um dia inteiro e mais uma madrugada!!), tentei muitas coisas e finalmente descobri que o que estava faltando era um "include Printers" na classe RequestReader, que eu não tinha me atentado que estava faltando.
Finalmente, depois de terminar a classe RequestReader, pôde-se "enxugar" main de suas declarações, colocá-las na classe Server e criar de fato o servidor multi Thread. Abaixo são mostrados os códigos finais para as main, server, e para a classe RequestReader.
O arquivo main.rb

require 'initialize' 
require 'request_reader' 
 
def main(inn=$stdin, out= $stdout) 
      request = RequestReader.new(inn) 
      loop do 
          # Gera a partir de RequestReader o prompt do servidor 
            request.ler_request 
      end 
 end 
#main() #executa o main

O arquivo server.rb
require 'socket' 
require 'initialize' 
require 'user_list_builder' 
require 'main' 
require 'logger' 
$logger = Logger.new("bolao.log") 
$logger.level = Logger::INFO 
 
class String 
  def to_score 
    self.strip.gsub(/\s+/, '').downcase 
  end 
end 
# Cria os builders e os decora 
      user_list_builder = UserListBuilder.new 
      match_list_builder = MatchListBuilder.new 
      user_list_builder.add_all 
      match_list_builder.add_all 
#  Cria as listas de match e de usuario usando os builders decorados 
      $match_list = match_list_builder.match_list 
      $user_list =user_list_builder.user_list 
 
# Crias algumas partidas para o banco de dados 
$match_list.add("palmeiras x santos", Time.now + 40000) 
$match_list.add("corinthians x portuguesa", Time.now + 400000) 
$match_list.add("vitoria x cruzeiro", Time.now + 200000) 
$match_list.add("gremio x coritiba", Time.now - 300000) 
match = $match_list.add("palmeiras x corinthians", Time.now - 600000) 
match.finalize("3x 1") 
$match_list.find_by_name("palmeiras x santos").finalize("5 x 0") 
#puts "Match_list Construida"          
 
server = TCPServer.new(4344)  # Cria o servidor na porta 4344 
 
while ( io_socket = server.accept ) #aguarda até que a conexão seja recebida 
  # dispara uma nova thread  
 Thread.new do  
    main(io_socket, io_socket) 
  end   
end

A classe RequestReader
require 'yaml' 
require 'printers/yaml_match_printer' 
require 'printers/yaml_match_list_printer' 
require 'printers/yaml_bet_printer' 
 
class RequestReader 
include Printers 
 
  def initialize(io) 
      @io = io 
      @current_user = nil 
      @saida = $stdout 
    end 
    attr_accessor :saida 
    attr_accessor :current_user 
 
#lê a entrada linha a linha até acharmos um "INICIO" 
  def ler_request 
    puts "prompt do servidor >>" 
    loop do 
      string = @io.gets 
      break if string && string.include?("INICIO") 
    end 
    yaml =@io.gets("\nFIM") #depois do INICIO, lê tudo até a palavra FIM 
    yaml = yaml.chomp("FIM") #tira o FIM do final do string que foi lido do io 
    requisicao = YAML.load(yaml) # aqui já temos o hash com a requisicao processada 
 
    case (requisicao["request"])["operation"] 
 
      when "create_user" 
        nome_user, swordfish = (requisicao["request"])["parameters"] 
        $user_list.add(nome_user,swordfish) 
        @current_user = $user_list.authenticate(nome_user, swordfish) 
        puts "Usuario Criado" 
 
      when "login_user" 
        nome_user, swordfish = (requisicao["request"])["parameters"] 
        @current_user = $user_list.authenticate(nome_user, swordfish) 
        if (@current_user == nil) then 
             puts "Usuario Invalido" 
             @io.close 
        end 
        puts "Usuario Logado" 
 
      when "list_current_matches" 
        @io.close if (@current_user == nil)   # Segurança: soh realiza operaçao se esta logado 
        imprime = YamlMatchListPrinter.new($match_list, @io) 
        imprime.print_current_matches 
        puts "Partidas Atuais Listadas" 
 
      when "list_finalized_matches" 
        @io.close if (@current_user == nil)   # Segurança: soh realiza operaçao se esta logado 
        imprime = YamlMatchListPrinter.new($match_list, @io) 
        imprime.print_finalized_matches 
        puts "Partidas Finalizadas Listadas" 
 
      when "show_match" 
        @io.close if (@current_user == nil)   # Segurança: soh realiza operaçao se esta logado 
        match_name =  (requisicao["request"])["parameters"] 
        match = $match_list.find_by_name(match_name) 
        imprime = YamlMatchPrinter.new(match, @io) 
        imprime.print_match 
        puts "Partidas Mostrada" 
 
      when  "show_my_bets" 
        @io.close if (@current_user == nil)   # Segurança: soh realiza operaçao se esta logado 
        imprime = YamlBetPrinter.new(@current_user, @io) 
        imprime.print_user_bets 
        puts "Aposta Mostrada" 
 
      when  "make_bet" 
        @io.close if (@current_user == nil)   # Segurança: soh realiza operaçao se esta logado 
        @saida.puts "testando" 
        @username = requisicao["request"]["parameters"][0] 
        @swordfish = requisicao["request"]["parameters"][1] 
        @matchname = requisicao["request"]["parameters"][2] 
        @score = requisicao["request"]["parameters"][3].to_score 
 
        @match = $match_list.find_by_name(@matchname) 
        puts "#{@match}" 
        @user = $user_list.find_by_name(@username) 
        @user.make_bet(@score, @match) 
        puts "Aposta Realizada" 
 
      when  "close_connection" 
        username = (requisicao["request"])["parameters"] 
        $logger.info "User #{username} has logged out." 
        @io.close 
        puts "Conexão Encerrada" 
 
      else  
          @saida.puts("Operacao invalida.") 
      end 
   end 
   end

Abaixo são mostrados os resultados do servidor já funcional.
Dois usuários (Joao e Aninha) logam e ficam ao mesmo tempo realizando operações no servidor. O arquivo de logger também foi mostrado.
O prompt do servidor é mostrado abaixo:
servidor.jpg
As telas dos usuários, Joao e Aninha:
usuarios.jpg
O arquivo de log, mostrando as ações dos usuários:
logger.jpg

Conclusão

Esta sem dúvida foi a atividade mais difícil do curso. Todavia, foi uma atividade importante pois englobou praticamente todos os conceitos estudados por nos neste curso de orientação a objetos. Pudemos aplicar aqui, num código relativamente extenso, conceitos de padrões de projeto, e orientação a objetos além de "encararmos" a dificuldade natural de se "lutar" contra os bugs. A maior dificuldade para mim nessa atividade não foi a orientação a objetos em si, mas, como eu não estava ainda totalmente acostumado com ruby, senti muita dificuldade de debugar, principamente por sentir falta da declaração dos tipos.
Enfim, embora sofri, mas o que deu pra fazer está ai! :)

p.s. Desculpa pela informalidade deste lab final!

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