Lab5 Fabio Imada

aluno: Fabio Eigi Imada
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 22/12/2008 (2)

Introdução

A idéia deste laboratório era criar um servidor de um bolão que fosse capaz de interagir com o cliente através de um protocolo simples. Dessa forma seriam trabalhados os conceitos de Threading e IO, além de trabalhar os padrões de projeto Builder e Decorator.

Desenvolvimento

O desenvolvimento do laboratório foi feito nas seguinte etapas, conforme as instruções do lab:

  • Desenvolvimento dos Decorators
  • Modificação dos testes para envolverem as modificações criadas
  • Criação do builder para encapsular o processo de criação de listas decoradas
  • Desenvolvimento do main, que cria as listas decoradas com os builders e cria uma instância do RequestDealer (classe que encapsula o tratamento das requisições e responde adequadamente).
  • Criação da classe Server que abre um servidor TCP em localhost na porta 4344

A criação dos Decorators foi feita conforme as instruções e o seu funcionamento foi comprovado com os testes fornecidos pelo professor:

Melhoria nos matchs

Como as melhorias envolviam inserção de novas funcionalidades aos métodos das Matchs, foram criados os seguintes Decorators para as Matchs, que tornavam os métodos make_bet e finalize mais robustos uma vez que estes métodos em particular modifam o estado do objeto:

MatchPersistanceDecorator
Faz com que as modificações sejam salvas no momento em que ocorrem.
MatchThreadSafeDecorator
Protege a instância de modifições simultâneas que possam tornar seu estado insonsistente.

Exemplo da MatchPersistanceDecorator:

class MatchPersistanceDecorator < Decorator
    def initialize(match,list)
      super(match)
      @match_list = list
    end
    def make_bet(*args)
      bet = @decorated.make_bet(*args)
      save
      bet
    end
    def finalize(*args)
      match = @decorated.finalize(*args)
      save
      match
    end
    def save
      @match_list.save
    end
  end

Melhoria nas MatchLists

Como foi feito no MatchListLoggerDecorator, os Decorators dos Matchs também tinham que ser aplicados às instâncias que estavam anteriormente nas listas.
Então foram criadas MatchListDecorators que herdavam das respectivas ListDecorators.

Exemplo da MatchListThreadSafetyDecorator

class MatchListThreadSafeDecorator < ListThreadSafeDecorator
    def initialize(decorated)
      super
      old_match_decorator = self.decorated.item_decorator
      self.decorated.item_decorator = lambda do |match|
        MatchThreadSafeDecorator.new( old_match_decorator.call(match) )
      end
    end
  end

Criação dos Builders

Foi criado um "builder abstrato" contendo apenas as assinaturas dos métodos e o método add_all que era igual para ambos os builders propostos, para deixar explícita a semelhança entre os ListBuilders.

Classe ListBuilder (teria sido um nome mais apropriado, mas foi chamado apenas de builder)

class Builder
  attr_accessor :persistance_file
  def add_all(persistance_file = nil)
    if persistance_file
      @persistance_file = persistance_file
    end
    add_persistance
    add_logging
    add_thread_safe
  end
end

Classe MatchListBuilder

class MatchListBuilder < Builder
  include Decorators
  def initialize
    @persistance_file = "matchs.yml" unless @persistance_file
    @added_persistence = false
    @match_list = MatchList.new
  end
  def match_list
      @match_list.load_list if @added_persistence
      @match_list
  end
  def add_persistance
    @added_persistence = true
    @match_list = MatchListPersistanceDecorator.new(@match_list,@persistance_file)
  end
  def add_logging
    @match_list = MatchListLoggerDecorator.new(@match_list)
  end
  def add_thread_safe
    @match_list = MatchListThreadSafeDecorator.new(@match_list)
  end
end

Desenvolvimento do main

Conforme foi proposto, foi criado o método main que recebia como parâmetros os ios de entrada e saída e que criava as listas decoradas, além de receber solicitações de modo sincronizado, ThreadSafety:

def main(input=$stdin, output=$stdout)
  @input = input
  @output = output
  al_builder = UserListBuilder.new
  al_builder.add_all("admins.yml")
  @admin_list = al_builder.user_list
  ul_builder = UserListBuilder.new
  ul_builder.add_all("users.yml")
  @user_list = ul_builder.user_list
  ml_builder = MatchListBuilder.new
  ml_builder.add_all("matchs.yml")
  @match_list = ml_builder.match_list
  reader = RequestDealer.new(@admin_list,@user_list,@match_list,@input,@output)
  $stdout.puts "Recebendo de #{@input.to_s}"
  loop do
    reader.receive
    break if @input.closed?
  end
end

A classe RequestDealer recebe, analiza e efetua as solicitações pelo método receive.

def receive
    #lê a entrada linha a linha até acharmos um "INICIO"
    loop do
      string = @in.gets
      break if string && string.include?("INICIO")
    end
    yaml = @in.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
    $stdout.puts "Recebido:\n" + yaml
    requisicao = YAML.load(yaml) # aqui já temos o hash com a requisicao processada
    #... processa requisicão e escreve saída
    if requisicao['request']['parameters']
      __send__(requisicao['request']['operation'],requisicao['request']['parameters'])
    else
      __send__(requisicao['request']['operation'])
    end
  end

Cada requisição chama o método de mesmo nome do parâmetro 'operation', como por exemplo:

def create_user(parameters)
    name = parameters[0]
    password = parameters[1]
    @user_list.add(name, password)
    simple_response("user created")
  end

Foi percebido posteriormente que seria mais claro se parameters fosse um dicionário ao invés de uma lista, evitando erros futuramente e facilitando a documentação.
Dessa forma, ao invés de:

name = parameters[0]

Teríamos algo como:
name = parameters[:name]

Foram executados alguns testes com input "entrada.data" e output "saida.data".
Os resultados estão aqui e na pasta test_results.

Criação do Servidor

OBS.: Para testar o servidor foi utilizado o telnet do Windows, que apesar de não estar disponível diretamente no prompt de comando, ainda existe, mocado no meio das pastas do Windows.

A criação do servidor foi de fácil implementação após ler a documentação indicada e compreender a idéia:

def server_multithread
  server = TCPServer.new(4344)  # 4344 é a porta de conexão de rede usada
                                 # utilize de preferência um número acima de
                                 # 2000 (e menor que 64000).
  loop do
    while ( io_socket = server.accept ) #aguarda até que conexão seja recebida, retornando um TCPSocket com a conexão.
      Thread.new do # dispare uma nova thread e utilize io_socket para efetuar a entrada e saída do método main, de dentro desta thread
        main(io_socket,io_socket)#io_socket deve ser utilizado tanto para saída quanto para entrada, portanto é passado como parâmetro in e parâmetro out de main.
        io_socket.close
      end
    end
  end
end
 
server_multithread

Foi criado ainda um arquivo load.rb para carregar as listas com dados para poderem ser testados (descaradamente copiado dos testes):

ul_builder = UserListBuilder.new
ul_builder.add_all
ul = ul_builder.user_list
 
jose = ul.add("jose", "123456")
joao = ul.add("joao", "123456")
maria = ul.add("maria", "123456")
 
ml_builder = MatchListBuilder.new
ml_builder.add_all
m_list = ml_builder.match_list
 
m1 = m_list.add "atletico x cruzeiro", Time.now + 100000
m2 = m_list.add "santos x palmeiras", Time.now + 100000
m1.make_bet "5x0", jose
m1.make_bet "5x0", joao
m1.make_bet "0x1", maria
m2.make_bet " 3 x 0", jose
m2.make_bet "2x0", joao
m1.finalize("5x0")

Conclusão

O laboratório foi interessante para aprofundar o entendimento do padrão Decorator e da utilização do Builder.
Além disso, foi o laboratório que conteve mais conteúdo envolvido, e por isso mostrou-se um pouco trabalhoso e seu desenvolvimento foi bastante "quebrado", para consultar o material sobre Decorators, ThreadSafety, Builders, YAML, Sockets e Modules (este último eu ainda estou com algumas dúvidas e acabei não utilizando onde eu imaginei que seria útil, no caso dos Builders).

código do projeto

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