• Sonuç bulunamadı

5. SONUÇLAR VE ÖNERĠLER

5.2. Öneriler

realizadas (relacionadas ao atributo exprCheck).

Outro método da classe Maquina é o método run. A chamada para avaliação de vértices é feita dentro deste método. Ele faz a veriĄcação dos vértices mínimos existentes na máquina, e faz a execução para um dos vértices escolhidos. A cada redução de um vértice, o método run é chamado, tornando esta uma tarefa cíclica, até que não existam mais vértices mínimos na máquina.

O método que contém toda a semântica de execução da máquina é o método executar, que recebe como parâmetro um vértice, e veriĄca o tipo do mesmo. A partir da deĄnição da instância do vértice (S! ¯E, S? ¯X, +, etc), é aplicado um tipo de redução

especíĄca.

4.2 A Máquina de Redução Estendida

Com a inserção dos métodos de tratamento de falhas, aplicamos algumas mudanças na gramática e na máquina para o suporte a estas alterações.

No módulo de especiĄcação, assim como na descrição abstrata da máquina, alte- ramos a estrutura da gramática de PEWS, da seguinte forma:

• Criação de novos tokens, padrões léxicos da gramática, sendo estes: TEMPORESPOSTA (usado para especiĄcar o tempo de resposta como método de detecção de falhas) e STRING (otimização realizada para separar a deĄnição de variáveis e strings). Além disso, outros tokens simples foram acrescentados para delimitar pilha de chamadas (símbolos ‘@’), delimitar pilhas de subcomposições (símbolos ‘{’ e ‘}’) e separar tanto serviços alternativos quanto composições alternativas (símbolo ‘%’);

• Inserção de novas regras na gramática, a Ąm de que a mesma dê suporte à cons- trução de composições utilizando o tempo de resposta como método de detecção de falhas, e dê suporte aos mecanismos de recuperação propostos (Retry, Rebinding e Restructure). Essas estruturas sintáticas serão mostradas com mais detalhes no Apêndice A deste documento;

• Inserção de novas chamadas semânticas para novas construções criadas na gramá- tica, cuja estrutura será mostrada no Apêndice A deste documento;

No módulo de transformação, algumas mudanças foram automáticas, pois, no mo- mento da compilação da nova gramática, as classes PewsTokens e PewsParser são geradas novamente, dando suporte às novas construções criadas. As grandes mudanças ocorreram no pacote pews.grafos. Na classe Grafo, novos construtores foram inseridos, para dar

suporte à criação dos grafos relacionados com os mecanismos de recuperação. Além das classes já existentes no pacote, foram acrescentadas as seguintes classes (todas estendem a super-classe Vertice):

• PilhaDeChamadas Ű Nova classe de vértice criada para dar suporte às pilhas de chamadas de serviços alternativos (dá suporte ao Rebinding). Estende a classe vértice, sendo seu atributo principal pilhaDeChamadas, que guarda todos os vértices de chamada da pilha (vértices do tipo S! ¯E);

• PilhaSubcomposicoes Ű Nova classe de vértice criada para dar suporte às pilhas de subcomposições (dá suporte ao Restructure). Esta classe também estende a classe Vertice. O atributo principal desta classe é subcomposicoes, que guarda todos os vértices das subcomposições alternativas separadamente;

• Sync Ű Este é um tipo especial de vértice que foi criado para resolver um problema relacionado ao paralelismo. No momento em que vamos criar uma pilha de subcom- posições, selecionamos todos os vértices mínimos e criamos um caminho alternativo a partir destes vértices. Ocorre que, no momento em que um paralelismo é a primeira construção de uma subcomposição alternativa, poderíamos erradamente considerar cada um dos dois ou mais vértices do paralelismo como um caminho alternativo. Sendo assim, criamos este vértice para que ele seja colocado no início e no Ąnal de um paralelismo. Esta classe não possui atributos, sendo criada apenas para facilitar a criação das pilhas de subcomposições. Na Ągura 22, temos o exemplo de como o vértice Sync ajuda na formação dos caminhos alternativos;

Figura 22 Ű a) Formação do grafo sem o Sync; b) Formação do grafo com o Sync;

Com estas inserções, mais classes estenderam a classe Vertice. Sendo assim, a hierarquia de classes que extendem a classe vértice é mostrada na Ągura 23.

O módulo de execução foi o que sofreu as maiores mudanças. Criamos um novo pacote, denominado pews.modulosTratamento. A nossa implementação permite que um desenvolvedor possa utilizar a mesma estrutura da máquina e implementar seu métodos de

4.2. A Máquina de Redução Estendida 73

Figura 23 Ű Hierarquia de classes do pacote pews.grafos na Máquina Estendida detecção. Para isso, o mesmo terá que implementar apenas as classes de detecção de falhas e nomear o método. Essa identiĄcação deve ser acrescentada na entidade tipoModulo, que pertence também ao pacote pews.modulosTratamento. Como estamos trabalhando com o tempo de resposta, criamos o pacote pews.modulosTratamento.TempoResposta, que comporta as classes de implementação deste método de detecção, onde o mesmo possui as seguintes classes:

• TempoResposta Ű Classe que é utilizada na execução de serviços quando o desig- ner da composição especiĄca que o mecanismo de detecção utilizado na composição será o tempo de resposta. Esta classe contém as informações padrões de todas as chamadas de serviços, ou seja, o par (TE,NM T). Com a utilização do tempo de res- posta, todo o processamento das chamadas de serviços é realizado por esta classe. A execução da mesma é simples: é criada uma thread de uma chamada de serviço, sendo a mesma executada e o tempo em que essa tarefa é realizada é guardado numa variável. Logo após, um laço inĄnito é disparado, sendo as condições de pa- rada do mesmo a execução e envio do sinal de término do serviço ou a detecção do estouro do limite de tempo máximo. Os atributos dessa classe são mostrados abaixo: private int tempoEspera;

private int numMaximoChamadas;

private BufferedWriter arquivo_saida;

• ThreadChamada Ű Esta classe realiza a chamada a um serviço propriamente dita. Nesta classe (assim também como na classe Chamada) está descrito o código para

chamada de cada serviço, assim como os artifícios para receber as respostas do mesmo.

Por Ąm, as maiores alterações foram realizadas na classe Maquina, que pertence ao pacote pews.maquina. Em relação aos atributos, além dos já existentes, foram acres- centados os seguintes:

• tipoTratamentoFalhas Ű Atributo criado pela inserção dos métodos de tratamento de falhas. Ele é uma entidade do tipo enum que, até o presente momento, pode assumir os valores NENHUM (caso a máquina seja usada sem detecção de falhas) ou TEMPORESPOSTA (caso a máquina seja especiĄcada com o uso do tempo de resposta para detecção de falhas). A construção da máquina foi feita de forma a dar suporte a outros métodos de detecção, sendo que estes devem ser implementados e criado outro valor a ser acrescentado neste atributo (por exemplo, QoS);

• moduloTratamentoFalhas Ű Atributo criado pela inserção dos métodos de tra- tamento de falhas. É um elemento do tipo Object. O objetivo deste atributo é ser setado com algum módulo de tratamento de falhas (se houver) que deve ser criado pelo usuário. No nosso caso, o único módulo opcional de detecção de falhas é o TEMPORESPOSTA;

• geradorFiguras e caracteristicas Ű Atributos auxiliares, usado pela classe gera- dora das Ąguras, citada no Apêndice C deste documento;

• exclusoes Ű Parâmetro auxiliar que indica o número de vértices excluídos, ou seja, aqueles cuja avaliação foi dispensada, como por exemplo, subcomposições alterna- tivas descartadas de uma pilha;

• arquivo_saida Ű Referência para o arquivo de saída, onde são escritas as informa- ções textuais de execução da máquina;

Quando o último passo da recursão é realizado e a máquina de redução é cri- ada, todo o grafo deve estar construído, juntamente com as informações sobre arestas de controle e dependência. Atualmente, existem dois métodos construtores para a classe Maquina, sendo eles:

public Maquina (Token grafo) {...}

public Maquina (Token grafo, Token Te, Token Nmt) { ... }

O primeiro construtor é usado quando a composição é feita sem o uso de meca- nismos de detecção de falhas, ou seja, o construtor original da máquina. O corpo desse

4.2. A Máquina de Redução Estendida 75 método simplesmente inicializa todos os atributos da classe, e o atributo tipoModulo re- cebe o valor NENHUM, indicando que nenhum método de detecção de falhas será utilizado. O segundo construtor é usado quando o designer da composição vai utilizar o tempo de res- posta como método de detecção. O corpo desse método, em geral, é igual ao primeiro, com a inicialização dos atributos padrões. A diferença é que o atributo tipoModulo receberá o valor TEMPORESPOSTA e os outros valores de entrada do método serão usados no construtor TempoResposta, cuja instância será atribuida ao atributo moduloTratamentoFalhas.

As maiores alterações desta classe foram realizadas no método executar. Este método contém toda a lógica de redução conceitual dos vértices. Este método recebe como parâmetro um vértice e, a partir da identiĄcação de seu tipo, aplica a avaliação correspondente. Foram acrescentadas três novas avaliações, relacionadas aos três novos tipos de vértices:

• Sync Ű Sua avaliação consiste apenas em excluí-lo, juntamente com suas arestas, quando o mesmo for um vértice mínimo;

• Pilha de Chamadas Ű A sequência da lógica da avaliação deste vértice consiste em, primeiramente, analisar se a pilha de chamadas é vazia ou não. Vejamos os dois casos abaixo:

Ű Se a pilha não é vazia, tenta-se executar o serviço do topo da pilha. Se ele

é executado, todos os outros serviços da pilha, assim como a própria pilha, são excluídos. Se ele falha, o mesmo é retirado da pilha e faz-se novamente a avaliação se a pilha é vazia ou não;

Ű Se a pilha é vazia, é veriĄcado se a mesma faz parte de uma subcomposição

alternativa, ou seja, de um caminho alternativo que está sendo executado por uma pilha de subcomposições. A semântica do caso em que isso é verdadeiro faz parte das pilhas de subcomposições. Se isso não é verdade, a composição chega a uma situação onde não há mais soluções a serem tomadas.

• Pilha de Subcomposições Ű A mesma lógica de redução que é usada na pilha de chamadas também é aplicada na pilha de subcomposições. Primeiramente, é observado se a pilha não é vazia. Caso ela não seja, tenta-se executar a composição do topo da pilha. Se ela é executada com sucesso, todas as outras subcomposições são excluídas, assim como o próprio vértice pilha. Se ocorre uma falha, toda a subcomposição é retirada do ambiente, tentando-se executar uma outra composição, se houver. Caso a pilha esteja vazia, veriĄcamos se o vértice pilha de subcomposições não faz parte de um caminho alternativo de outra pilha de subcomposições;

A Ąm de aproveitar todo o código de redução deĄnido pelo método executar, realizamos a avaliação de uma pilha de subcomposições da seguinte maneira: quando

encontramos uma pilha não-vazia, retiramos a subcomposição alternativa do topo e a colocamos no ambiente. Estando esta subcomposição fora da pilha, ela terá vértices mí- nimos que podem ser executados. Entretanto, a própria pilha também continua sendo um vértice mínimo, podendo ser avaliada novamente. Para que façamos uma nova avali- ação da pilha apenas quando ocorrer uma falha na subcomposição que está fora, sendo a mesma excluída, criamos um atributo booleano execute nos vértices do tipo pilha de sub- composições. Para que uma pilha seja um vértice mínimo, além de não possuir arestas de controle chegando, a mesma precisa ter esse parâmetro com um valor false. O signiĄcado deste valor é que Şnão existem subcomposições da pilha sendo executadasŤ. No momento em que uma pilha é avaliada e retirada uma subcomposição para execução, setamos esse atributo com o valor true, fazendo assim com que este vértice não seja ŞavaliávelŤ.

Todos os vértices possuem atributos chamados idhashCodeSoma e idhashCodeMu. Estes atributos indicam a qual bloco de estruturas + e Mu o vértice pertence (sendo o valor igual a -1, ele não pertence a nenhum bloco). Utilizando esta mesma idéia, criamos um atributo na classe Vertice, chamado idPilhaSubcomposicoes, que indica a qual pilha de subcomposições o vértice está relacionado. Quando uma subcomposição alternativa exe- cuta completamente, pegamos a referência da pilha, através deste atributo, e excluímos todas as outras subcomposições relacionadas, além da própria pilha. Da mesma forma, quando ocorre uma falha, excluímos todo o resto da subcomposição e, através desta refe- rência ao vértice pilha, setamos o valor do atributo execute para false, indicando que o mesmo pode ser avaliado.

Outro fato é que precisamos de uma forma para observar quando uma subcom- posição alternativa chegou a Ąm, visto que os vértices estarão ŞsoltosŤ. Quando termi- namos a avaliação de cada vértice, veriĄcamos a possível ocorrência do Ąm de uma sub- composição alternativa, utilizando o parâmetro idPilhaSubcomposicoes. O Ąm de uma subcomposição alternativa é alcançado no momento em que um vértice que contém um idPilhaSubcomposicoes diferente de -1 é avaliado, reduzido, e não possui arestas de controle para qualquer outro vértice (com exceção do vértice +). Nem todos os vértices precisam passar por essa avaliação, visto que:

• Vértices do tipo Chamada e PilhaDeChamadas são sempre precedidos de um vértice do tipo Retorno;

• Vértices do tipo Expr tem sua avaliação feita dentro do âmbito de vértices Soma e são sempre seguidos de alguma subcomposição (segundo as construções [E1]P1 +

[E2]P2 ou IF[E]P).

• Vértices do tipo Unfolding são sempre precedidos de alguma composição, segundo a construção Unfolding P;

4.2. A Máquina de Redução Estendida 77 Todos os outros vértices (Retorno, Sync,PilhaSubcomposicoes e Unfold) passam por este teste nas avaliações. A única peculiaridade é em relação ao vértice +. De acordo com sua construção, ele sempre terá arestas de controle que saem para vértices do tipo Expr, porém pode ser um vértice que represente o Ąm de uma composição. Isso ocorre quando, numa subcomposição alternativa, a última construção é do tipo [E1]P1 + [E2]P2

ou IF[E]P. Vejamos o exemplo de um especiĄcação de composição em PEWS, mostrada na Ągura 24:

{P1 ; IF [x < 3] P2 % P3 % P4}

Figura 24 Ű Exemplo de Ąnal de subcomposição

Temos três subcomposições, identiĄcadas por Subcomp1, Subcomp2 e Subcomp3.

Considerando que a subcomposição do topo da pilha é Subcomp1, a mesma é retirada para

execução. Supomos que P1 execute normalmente, Ącando apenas a construção IF [x <

3] P2. Se x é menor que três, a redução ocorre normalmente, retirando os vértices + e Expr

(representado por x < 3) e prosseguindo a execução de P2. Entretanto, se x ≥ 3, todos os

vértices são excluídos, pois não há a necessidade de executar. Porém, chegamos ao Ąnal de uma subcomposição alternativa que executou com sucesso, necessitando da exclusão das outras subcomposições alternativas e também do vértice pilha de subcomposições. Sendo assim, para o vértice +, aplicamos o teste de chegada de Ąm de subcomposição no caso da guarda do vértice Expr ter o valor false (no caso da escolha não-determinística, as duas guardas precisam ser falsas).

Além das inserções de novas características referentes ao tratamento de falhas opcionais, inserimos uma nova característica que foi colocada como padrão da máquina de redução: o tratamento do uso de variáveis não-inicializadas. A composição pode fazer uma referência a uma variável que não exista no ambiente. Isso ocorre em vértices de chamada de serviço (quando uma variável não-inicializada é colocada como parâmetro de entrada do serviço) ou em vértices de avaliação de expressões (Expr). Para isso, criamos a classe VariaveisNaoInicializadas, que possui apenas um método booleano, chamado verificarVariaveisNaoInicializadas, que recebe uma lista de parâmetros (inteiros,

strings e variáveis) e o ambiente de variáveis atual, veriĄcando se existe alguma variável que não foi colocada no ambiente. As duas únicas situações onde não foram encontradas soluções foram: (i) condicionais simples fora do âmbito de pilhas de subcomposições e (i) chamadas de serviços foram do âmbito de pilhas de subcomposições ou pilha de chamadas de serviços. Sendo assim, foram implementadas soluções para as seguintes situações:

• Escolhas não-determinísticas Ű Suponha uma escolha do tipo [x<5] P1 + [y<5]

P2, onde x é uma variável não-inicializada e y possui algum valor no ambiente de

variáveis. Ao tentar executar a guarda [x<5] e não achar um valor deĄnido para x, a máquina irá excluir a composição P1, além da guarda [x<5]. É como se con-

siderássemos que a avaliação da mesma retornou um valor false na sua avaliação. Posteriormente, tentaremos avaliar a segunda guarda, onde o valor de y foi deĄnido no ambiente, e obteremos sucesso na avaliação;

• Condicionais simples e Chamadas de Serviços dentro de uma pilha de

subcomposições Ű Caso uma variável nesses dois tipos de vértices não estejam

inicializadas, consideraremos que houve um erro, e tentaremos a execução de outra subcomposição da pilha relacionada;

• Pilha de Chamadas Ű Numa pilha de chamadas, ao tentar executar um serviço que possua em seus parâmetros de entrada uma ou mais variáveis que não foram inicializadas, excluiremos esse serviço e tentaremos a execução do próximo serviço da pilha de chamadas. Isso é feito porque podem existir serviços na pilha que não necessitem dessas variáveis para execução. Além disso, caso a pilha de chamadas não tenha mais serviços a serem executados, porém a mesma encontra-se dentro de uma pilha de subcomposições, consideramos que ocorreu um erro na subcomposição e excluiremos a mesma, para que outra subcomposição seja executada, dando mais possibilidades de execução para a composição principal;

O ambiente usado para a implementação da máquina de redução de grafos foi o

Eclipse [7], utilizando a linguagem de programação Java. A escrita das composições de serviços são feitas através de linha de comando, onde a leitura da composição é realizada e traduzida para o formato esperado pela máquina através de um gerador de parsers, chamado Jacc [16] (Just another compiler - compiler for Java). A gramática utilizada foi retirada de [8], sendo que alterações foram realizadas para o suporte aos métodos de recuperação de falhas.

Benzer Belgeler