• Nenhum resultado encontrado

Utilizando técnicas de compilação condicional e AOM

N/A
N/A
Protected

Academic year: 2019

Share "Utilizando técnicas de compilação condicional e AOM"

Copied!
42
0
0

Texto

(1)

Universidade Federal de Pernambuco – UFPE

Centro de Informática - CIn

Tópicos Avançados em

Engenharia de Software

Utilizando técnicas de

compilação condicional e AOM

Dupla:

Caio César Sabino Silva (ccss2@cin.ufpe.br)

Lais Sousa de Andrade (lsa@cin.ufpe.br)

Professor: Paulo Borba (phmb@cin.ufpe.br)

(2)

Sumário

1. Introdução

2. Resolvendo problemas encontrados no projeto

2.1. Problema 1: Imutabilidade da definição de Publicações

2.1.1. Situação anterior

2.1.2. Valores observados

2.1.3. Princípios utilizados

2.1.4. Refatorando o código

2.1.4.1. PropriedadePublicacao.java

2.1.4.2. Publicacao.java

2.1.4.3. TipoPropriedade.java

2.1.4.4. TipoPublicacao.java

2.1.4.5. publicacao-aom.xml

2.1.4.6. AOMHelper.java

2.1.5. Situação posterior

2.1.5.1 TreeMap

2.1.5.2. Polymetric View

2.1.5.3. Páginas do sistema

2.2. Problema 2: Imutabilidade no povoamento do banco de dados

2.2.1. Situação anterior

2.2.1.1. GerarBanco.java

2.2.1.2. TreeMap

2.2.2. Valores observados

2.2.3. Princípios utilizados

2.2.4. Refatorando o código

2.2.4.1. membros.xml

2.2.4.2. publicacoes.xml

2.2.4.3. noticias.xml

2.2.4.4. linhaspesquisa.xml

2.2.4.5. ReflectionXmlGerador.java

2.2.4.6. AOMXmlGenerator.java

2.2.4.7. GerarBanco.java

2.2.5. Situação posterior

3. Adicionando nova funcionalidade

3.1. Funcionalidade escolhida

3.2. Implementação da funcionalidade

3.2.1. Formato do arquivo BibTex

3.2.1.1. Exemplo de entrada num arquivo .bib

3.2.2. Acrescentando a funcionalidade

3.2.2.1. BibTexAspect.aj

3.2.2.2. AdicionarPublicacoesFromBibTexServlet.java

3.2.2.3. adicionarpublicacao.jsp

3.2.2.4. web.xml

(3)

4.1. Impactos das alterações

4.2. Adaptando o mecanismo de validação

4.2.1. Situação anterior

4.2.2. Otimizando a solução

4.2.2.1. Novo trecho do publicacao-aom.xml

4.2.2.2. IValidationRule

4.2.2.3. Exemplo de RuleObject - RangeLengthRule

4.2.2.4. AbstractBusinessEntity.validate.properties

4.2.2.5. AbstractBusinessEntity.validation.xml

4.2.2.6. XMLValidationUtil.java

4.2.2.7. Alteração no AOMHelper

(4)

1. Introdução

Nesta atividade, alguns problemas de modularidade e reuso de código tentarão ser

solucionados, utilizando para isso as técnicas vistas nas últimas aulas, destacando

principalmente compilação condicional e AOM.

(5)

2. Resolvendo problemas encontrados no projeto

Alguns problemas de modularidade e reuso de código foram encontrados e

solucionados na atividade anterior. Algumas soluções ainda podem ser melhoradas e alguns

problemas menores ainda restam no sistema.

Nas próximas subseções, um problema encontrado no código será identificado e

solucionado utilizando novas técnicas de refatoramento como compilação condicional, AOM ou

técnicas já vistas anteriormente.

2.1. Problema 1: Imutabilidade da definição de Publicações

No projeto, temos a classe Publicacao e alguns subtipos da mesma. Cada tipo de

publicação no projeto tem um conjunto fixo de atributos, o que torna tais classes rígidas, sem

capacidade de alterar sua definição. O maior problema com esta rigidez está no fato de o

tratamento de publicações específicas no sistema exige código de verificação e manipulação

específico para cada um de seus subtipos, tornando o código maior e mais complexo.

2.1.1. Situação anterior

Mostraremos o código com a definição de uma Publicacao do sistema.

public abstract class Publicacao extends AbstractBusinessEntity implements Comparable<Publicacao>{

public final static String CONFERENCIA = "Artigo em Conferência";

public final static String PERIODICO = "Artigo em Periódicos e Revistas"; public final static String POSGRADUACAO = "Pós-Graduação";

protected String tipo;

protected List<Membro> autores;

protected List<String> autoresNaoMembros;

protected String titulo;

protected String ano;

protected byte[] pdf;

// Getters and setters...

}

(6)

public class AdicionarPublicacaoServlet extends PublicacaoModelMapperServlet {

@Override

protected Publicacao getObject() { Publicacao ret = null;

String tipo = (String) this.getFormfields().get("tipo"); String nivel = (String) this.getFormfields().get("nivel");

if(tipo.equals("Artigo em Conferência")){ ret = new ArtigoConferencia(); }

if(tipo.equals("Artigo em Periódicos e Revistas")){ ret = new ArtigoPeriodico();

}

if(tipo.equals("Pós-Graduação")){ if(nivel.equals("Mestrado")){

ret = new PublicacaoPosGraduacao();

((PublicacaoPosGraduacao) ret).setNivel(Nivel.MESTRADO);

}

if(nivel.equals("Doutorado")){

ret = new PublicacaoPosGraduacao();

((PublicacaoPosGraduacao) ret).setNivel(Nivel.DOUTORADO);

}

}

return ret; }

... }

2.1.2. Valores observados

Analisando o código de Publicacao e seus subtipos, percebe-se que o mesmo é:

Rígido: cada classe de Publicacao tem um número fixo de atributos e toda instância

tem o mesmo esqueleto. Para a inserção de um novo tipo de Publicacao, é necessário

modificar todo código que trate dos subtipos da mesma, em vários lugares, deixando

esta modificação mais difícil.

Não tão flexível: embora haja uma superclasse aberta a extensão, em caso de

adição de um novo tipo de publicação, é preciso inserir uma nova classe no ambiente

de execução, o que não é tão prático e exige parada na execução do servidor e

re-compilação do código.

Específico:

sobre cada subtipo da classe Publicacao, o código que manipula cada

subclasse específica está bastante preso à definição da mesma.

Frágil: alterações na definição de uma publicação afeta vários lugares do sistema, que

podem ser facilmente quebrados com a modificação de um subtipo da mesma.

2.1.3. Princípios utilizados

(7)

a

minimização de repetições em vários lugares que tratam com cada tipo específico de

Publicacao.

2.1.4. Refatorando o código

Utilizando a técnica de AOM (Adaptive Object-Model), pode-se definir uma estrutura de

uma publicação que pode ser adaptável a um modelo definido num arquivo XML. A princípio,

pensou-se em fazer isso para todas as entidades do sistema, mas se verificou que o código se

tornaria excessivamente complexo (como o artigo de

Yoder and Johnson

alerta, há dificuldades

de implementação de AOM quando suas entidades precisam ser persistidas). Dessa forma o

que vamos fazer a seguir visa conciliar o custo de complexidade do refatoramento com os

benefícios que podem vir do uso da técnica de AOM.

A princípio, verificou-se atributos obrigatórios de uma publicação e verificou-se

que

“titulo” (usado inclusive como chave no BibTex),

“ano”,

“pdf”,

“autores” e

“tipo” são

fixos em publicação (na especificação dos requisitos, vemos que são obrigatórios em quaisquer

tipos de publicação). Além disso, eles são necessários na implementação do Dao feita como

atributos para ordenação, e por isso devem ser tratados como colunas da tabela do banco de

dados. Por isso decidiu-se tratá-los como atributos fixos da classe

Publicacao

.

Observando que os demais atributos podem ser diversos, dependendo do tipo de

Publicação, e utilizando o padrão Property para poder definir adaptar os modelos dos tipos de

publicação em relação a seus atributos, foi criada a classe

PropriedadePublicacao

:

2.1.4.1. PropriedadePublicacao.java

@Entity

public class PropriedadePublicacao {

@Id

@GeneratedValue private Long id;

@Basic

private String nome;

@Lob

private byte[] valor;

public PropriedadePublicacao(String nome, Serializable valor) { this.setNome(nome);

this.setValor(valor); }

public PropriedadePublicacao() {}

public Object getValor() { Object obj = null;

if(this.valor != null){

obj = SerializationUtils.deserialize(this.valor); }

return obj; }

(8)

this.valor = SerializationUtils.serialize(valor); }

// Getters e setters... }

Perceba que há um grande inconveniente que se refere a como o valor da propriedade é

representado no banco de dados. Utilizando objetos serializáveis, foram definidos métodos que

abstraem como o valor serializado é de fato armazenado no banco de dados (getValor e

setValor).

Após isso, os subtipos de Publicacao deixaram de existir e agora Publicacao é definida

dessa forma:

2.1.4.2. Publicacao.java

@Entity

@Table(uniqueConstraints = {@UniqueConstraint(columnNames={"titulo"})})

public class Publicacao extends AbstractBusinessEntity implements Comparable<Publicacao>{

private String titulo;

private String tipo;

private List<Membro> autores;

private List<String> autoresNaoMembros;

private String ano;

private byte[] pdf;

private List<PropriedadePublicacao> propriedades;

// ...

}

Além disso, foram criadas a classe

TipoPublicacao

, em conformidade ao padrão

TypeObject para permitir a definição de tipos de publicação que representa um tipo de

publicação (artigo de conferência, trabalho de pós-graduação, ...), guardando a lista de

propriedades possíveis que cada publicação desse tipo pode ou deve ter, e a classe

TipoPropriedade

, que guarda características de uma propriedade específica de um tipo de

Publicação, como qual o tipo de Java que representa um valor dessa propriedade, qual o valor

da propriedade de texto que ela deve buscar no arquivo de linguagens para mostrar o nome

desse campo.

2.1.4.3. TipoPropriedade.java

package br.ufpe.cin.rgms.publicacao.modelo;

public class TipoPropriedade {

private String tipo;

(9)

private boolean optional;

public TipoPropriedade(String tipo, String text, boolean optional){ this.setTipo(tipo);

this.setText(text);

this.setOptional(optional); }

2.1.4.4. TipoPublicacao.java

package br.ufpe.cin.rgms.publicacao.modelo;

public class TipoPublicacao {

private String type;

private String text;

private HashMap<String, TipoPropriedade> propertyTypes;

public TipoPublicacao(String type, String text) { this.setType(type);

this.setText(text);

this.setPropertyTypes(new HashMap<String, TipoPropriedade>()); }

public void addPropertyType(String propName, TipoPropriedade propType){ this.propertyTypes.put(propName, propType);

}

public TipoPropriedade getPropertyType(String propName){ return this.propertyTypes.get(propName);

}

// Getters e setters... }

(10)

Perceba que as relações em linhas horizontais não são implementadas de maneira

direta (Publicação guarda apenas uma String que é a identificação do tipo de publicação e não

há ligação direta entre PropriedadePublicacao e TipoPublicacao, ambos apenas possuem

mesmo atributo nome). Isso se deve ao fato de que apenas Publicacao e

PropriedadePublicacao são persistidas no banco de dados para efeito de simplicidade e

minimizar o custo do refatoramento (caso contrário, precisaria ser definido um DAO para essas

duas outras entidades).

Precisou-se após isso definir tipos de publicação que se adaptam a um modelo definido

num arquivo. Para evitar complicações de definir num banco de dados os meta-dados e

precisar estender os serviços do Dao atuais, as classes que representam um tipo de publicação

e um tipo de propriedade não serão persistidas e serão associadas a um arquivo de XML

chamado publicacao-aom.xml. O seu conteúdo segue essa estrutura:

2.1.4.5. publicacao-aom.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<aom xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:publicacao-aom.xsd">

<publicacao-types> <publicacao-type>

(11)

<text-option>Artigo em Conferência</text-option>

<optional-attributes> <attribute>

<name>pages</name>

<class>java.lang.String</class> <text>paginas</text>

</attribute> </optional-attributes> <required-attributes>

<attribute>

<name>booktitle</name>

<class>java.lang.String</class> <text>conferencia</text>

</attribute> </required-attributes> </publicacao-type>

... <publicacao-types> </aom>

Temos a classe AOMHelper, de acordo com os padrões Helper e Singleton, que lida

com a manipulação das definições de Publicacao existentes no XML e é usada pelo sistema

para saber quais tipos de publicação são suportados pela aplicação no momento atual e quais

atributos cada tipo utiliza. Ela utiliza outra classe auxiliar

XMLHelper também seguindo o

mesmo pattern.

2.1.4.6. AOMHelper.java

public class AOMHelper {

private static AOMHelper instance;

private Hashtable<String, TipoPublicacao> map = new Hashtable<String, TipoPublicacao>();

static{

AOMHelper.instance = new AOMHelper(); }

public static AOMHelper getInstance() { return instance;

}

public AOMHelper() { this.readAOM(); }

public TipoPublicacao getTipoPublicacao(String tipo){ return this.map.get(tipo);

}

public Set<String> getTipos(){ return this.map.keySet(); }

public void readAOM(){

synchronized (this.map) { try{

this.map = new Hashtable<String, TipoPublicacao>(); XMLHelper helper = XMLHelper.getInstance();

Document xml = helper.getXML(

(12)

Collection<Element> typeElements = helper.getElements( xml.getDocumentElement(), "publicacao-type" );

for(Element type : typeElements){

String entry = helper.getContent(

helper.getMatchingElement(type, "entry-type")); String option = helper.getContent(

helper.getMatchingElement(type, "text-option")); TipoPublicacao tipo = new TipoPublicacao(entry, option);

for(Element attribute :

helper.getElements(helper.getMatchingElement(type, "optional-attributes"), "attribute")){ this.loadAttributes(tipo, attribute, true); }

for(Element attribute :

helper.getElements(helper.getMatchingElement(type, "required-attributes"), "attribute")){ this.loadAttributes(tipo, attribute, false); }

this.map.put(entry, tipo); }

}catch(SAXException e){

throw new IllegalStateException(); } catch (IOException e) {

throw new IllegalStateException(); } catch (ParserConfigurationException e) { throw new IllegalStateException(); }

} }

private void loadAttributes(TipoPublicacao tipo, Element attribute, boolean optional) { XMLHelper helper = XMLHelper.getInstance();

String name = helper.getContent(helper.getMatchingElement(attribute, "name")); String clazz = helper.getContent(helper.getMatchingElement(attribute, "class")); String text = helper.getContent(helper.getMatchingElement(attribute, "text"));

tipo.addPropertyType(name, new TipoPropriedade(clazz, text, optional)); }

}

Após as alterações, houve um problema: o mecanismo de validação definido

anteriormente obtia os atributos das publicações por Reflection e agora não é mais dessa forma

para os atributos variáveis dos tipos de publicação. Agora temos listas de atributos obrigatórios

e opcionais no xml, mas isso é bastante restrito. O mecanismo de validação foi estendido como

pode ser visto na seção de otimização de soluções.

Após isso, foi necessário alterar os jsps para que as páginas de inserir/alterar

publicação se adequem à definição dinâmica de um tipo de publicação, bem como os Servlets

que fazem tais operações.

2.1.5. Situação posterior

(13)

2.1.5.1 TreeMap

2.1.5.2. Polymetric View

(14)

2.1.5.3. Páginas do sistema

Trechos de código nos jsps onde se verificava os tipos de publicação e colocava seus

atributos foram alterados no alterarpublicacao.jsp e adicionarpublicacao.jsp, uma vez que no no

corpo do jsp, pode-se iterar sobre os atributos de cada tipo de publicação mostrando seus

campos:

<p> <%

AOMHelper helper = AOMHelper.getInstance();

for(PropriedadePublicacao t : publicacao.getPropriedades()){

%>

<LABEL for="<%= t.getNome() %>" id="label<%= t.getNome() %>"> <% out.println(Properties.getProperty(

this.getServletContext(),

helper.getTipoPublicacao(publicacao.getTipo()). getPropertyType(t.getNome()).getText()) );

%>

</LABEL>

<INPUT onkeypress="return noenter();" type="text" name="<%= t.getNome() %>" size="80" value="<% out.print(t.getValor().toString()); %>">

<%

}

%> </p>

(15)

Página de cadastro após alterações: uma complicação foi que podia haver campos de mesmo nomes entre tipos de publicação diferentes. Deve ser apresentado ao usuário um único campo e não múltiplas vezes. Isso foi tratado como

pode ser visto na screenshot.

(16)

Teste executado na inserção após refatoramento

(17)

2.2. Problema 2: Imutabilidade no povoamento do banco de dados

Atualmente o povoamento do banco de dados é feito pela classe

GerarBanco,

mais

especificamente com um único método, o

popularBanco()

que sozinho inicializa os dados e

os insere na base de dados. Esta geração de dados está presa ao código, exigindo uma

modificação toda vez que se quer modificar uma informação que será inserida, ou inserir mais

dados no gerador. Além de deixar a geração de dados presa ao código, o mesmo não está bem

modularizado, de modo que a classe tem um método muito grande, com quase todo o código

da classe.

2.2.1. Situação anterior

Aqui está a classe antes do refatoramento:

2.2.1.1. GerarBanco.java

package br.ufpe.cin.rgms.util;

public class GerarBanco {

public static void main(String[] args) {

Configuration conf = new AnnotationConfiguration(); conf.configure();

SchemaExport se = new SchemaExport(conf); se.create(true, true);

popularBanco(); }

private static void popularBanco() { try {

File file = new File("WebContent/images/login-page-bg.jpg"); byte[] foto = new byte[(int) file.length()];

FileInputStream reader = new FileInputStream(file); reader.read(foto);

Estudante felype = new Estudante("felype.ferreira@gmail.com","Felype","Santiago", "Estudante","Centro de Inform?tica","UFPE","8134395054",

"www.cin.ufpe.br/~fsf2","Olinda","Brasil","Ativo",null,foto, "Paulo Henrique Monteiro Borba","");

Estudante michelle = new Estudante("michelle.cms@gmail.com","Michelle","Silva", "Estudante","Centro de Inform?tica","UFPE","8131345911","www.cin.ufpe.br/~mcms", "Recife","Brasil", "Ativo",null,foto,"Paulo Henrique Monteiro Borba","");

felype.setSenha("senha"); michelle.setSenha("senha");

Facade.getInstance().inserirMembro(felype); Facade.getInstance().inserirMembro(michelle); List<Membro> membros = new ArrayList<Membro>();

(18)

naoMembro.add("Michelle");

Publicacao p1 = new Publicacao(membros,naoMembro, "Uma abordadegem de Teste", "2010", null, "conference"); p1.addProperty(new PropriedadePublicacao("pages", "112-117"));

p1.addProperty(new PropriedadePublicacao("month", "Maio"));

p1.addProperty(new PropriedadePublicacao("booktitle", "Artigo em Conferência"));

Facade.getInstance().inserirPublicacao(p1);

List<String> not = new ArrayList<String>(); not.add("Unknown");

p1 = new Publicacao(membros,not, "Linha de Produto de Software", "2012", null, "phdthesis"); p1.addProperty(new PropriedadePublicacao("month", "Março"));

p1.addProperty(new PropriedadePublicacao("school", "Pós-Graduação")); Facade.getInstance().inserirPublicacao(p1);

List<String> financiadores = new ArrayList<String>(); financiadores.add("Financiador 1");

financiadores.add("Financiador 2"); financiadores.add("Financiador 3");

List<String> linksRelacionados = new ArrayList<String>(); linksRelacionados.add("Link relacionado 1");

linksRelacionados.add("Link relacionado 2"); linksRelacionados.add("Link relacionado 3");

List<Publicacao> publicacoes = Facade.getInstance().getPublicacoes();

NoticiaCadastrada noticia = new NoticiaCadastrada("Lalalala",michelle,

Noticia.CADASTRADA, "jjhdjhfj"); Facade.getInstance().inserirNoticia(noticia);

NoticiaTwitter twi = new NoticiaTwitter(felype, Noticia.TWITTER,

"http://twitter.com/felypesantiago"); Facade.getInstance().inserirNoticia(twi);

Facade.getInstance().inserirLinhaPesquisa(new LinhaPesquisa("T?tulo","Breve

descri??o","Descri??o detalhada",financiadores,linksRelacionados,membros,publicacoes)); } catch (RGMSException e) {

// TODO Auto-generated catch block e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block e.printStackTrace();

} } }

2.2.1.2. TreeMap

(19)

2.2.2. Valores observados

Analisando o código de GerarBanco, temos as seguintes características do mesmo é:

Não tão flexível: a adição de um novo valor de entidade que já é inserida pelo gerador

exige a inserção de mais código, sem oportunidade para reuso de nenhuma inserção

que é feita

Rígido: difícil de ser modificado para inserir outros tipos de dados ou para realizar a

operação sobre outros dados.

Específico: código fixo, específico para a inserção de cada valor definido no mesmo.

2.2.3. Princípios utilizados

Para permitir o reuso do código, foram aplicados os seguintes principios:

Generalização de código: tornando o código que insere entidades no banco de dados

mais genérico e permitindo o reuso do mesmo.

(20)

da especificidade do que está sendo inserido em listas ou no próprio banco, que não

permite o uso de laços.

Extração de pontos de extensão: que permitam a modificação dos dados que serão

inseridos no banco, adicionado ou removendo entidades, sem modificações no código.

Open Closed Principle:

fazendo que qualquer modificação nos dados a serem

inseridos, seja na quantidade ou no conteúdo do mesmo, não exija uma modificação no

código de geração do banco.

2.2.4. Refatorando o código

Inicialmente foi feita a separação de dois concerns desta classe: o primeiro, a geração

dos dados a serem inseridos no banco de dados, e o segundo a inserção dos mesmos no

banco. Para isso, visando manter os princípios definidos acima, foram criados alguns xmls, um

para cada entidade básica do sistema, que definem valores que serão inseridos no banco de

dados. Cada xml tem basicamente a seguinte formação:

<xml>

<entity_set class=”className”> <entity_name id=”<id>”>

<attribute1_name>value</attribute1_name> <attribute2_name>value</attribute2_name> ...

<attributeK_name ref=”<id>” /> ...

<attributeN_name>value</attributeN_name> </entity_name>

... </entity_set> </xml>

Seguindo este formato, os dados inseridos pela classe

GerarBanco

tornaram-se os

seguintes xmls:

2.2.4.1. membros.xml

<xml>

<entity_set class="br.ufpe.cin.rgms.membro.modelo.Estudante"> <entity_name id="felype">

<email>felype.ferreira@gmail.com</email> <nome>Felype</nome>

<sobrenome>Santiago</sobrenome> <tipo>Estudante</tipo>

<departamento>Centro de Informática</departamento> <universidade>UFPE</universidade>

<telefone>8134395054</telefone>

<website>www.cin.ufpe.br/~fsf2</website> <cidade>Olinda</cidade>

<pais>Brasil</pais> <situacao>Ativo</situacao>

<foto>WebContent/images/login-page-bg.jpg</foto> <orientador>Paulo Henrique Monteiro Borba</orientador> <coOrientador>x</coOrientador>

(21)

</entity_name> <entity_name id="michelle"> <email>michelle.cms@gmail.com</email> <nome>Michelle</nome> <sobrenome>Silva</sobrenome> <tipo>Estudante</tipo>

<departamento>Centro de Informática</departamento> <universidade>UFPE</universidade> <telefone>8131345911</telefone> <website>www.cin.ufpe.br/~mcms</website> <cidade>Recife</cidade> <pais>Brasil</pais> <situacao>Ativo</situacao> <foto>WebContent/images/login-page-bg.jpg</foto> <orientador>Paulo Henrique Monteiro Borba</orientador> <coOrientador>x</coOrientador> <senha>senha</senha> <publicacoes> </publicacoes> </entity_name> </entity_set> </xml>

2.2.4.2. publicacoes.xml

<xml> <entity_set class="br.ufpe.cin.rgms.publicacao.modelo.Publicacao"> <entity_name id="p1" type="conference">

<autores> <autor ref="felype"/> </autores> <autoresNaoMembros> <autor>Unknown</autor> </autoresNaoMembros>

<titulo>Uma abordadegem de Teste</titulo> <ano>2010</ano>

<pages>112-117</pages> <month>Maio</month>

<booktitle>Conferência X</booktitle> </entity_name>

<entity_name id="p2" type="phdthesis"> <autores> <autor ref="felype"/> </autores> <autoresNaoMembros> <autor>Unknown</autor> </autoresNaoMembros>

(22)

<tipo>Notícia Cadastrada</tipo> <titulo>jjhdjhfj</titulo> </entity_name>

</entity_set>

<entity_set class="br.ufpe.cin.rgms.noticia.modelo.NoticiaTwitter"> <entity_name id="n2">

<link>http://twitter.com/felypesantiago</link> <autor ref="felype"></autor>

<tipo>Notícia Importada do Twitter</tipo> </entity_name>

</entity_set> </xml>

2.2.4.4. linhaspesquisa.xml

<xml>

<entity_set class="br.ufpe.cin.rgms.linhasdepesquisa.modelo.LinhaPesquisa"> <entity_name id="l1">

<titulo>Uma abordadegem de Teste</titulo> <breveDescricao>Breve descrição</breveDescricao>

<descricaoDetalhada>Descrição detalhada</descricaoDetalhada> <financiadores>

<financiador>Financiador 1</financiador> <financiador>Financiador 2</financiador> <financiador>Financiador 3</financiador> </financiadores>

<linksRelacionados>

<linkRelacionado>Link relacionado 1</linkRelacionado> <linkRelacionado>Link relacionado 2</linkRelacionado> <linkRelacionado>Link relacionado 3</linkRelacionado> </linksRelacionados>

<membros>

<membro ref="felype"></membro> <membro ref="michelle"></membro> </membros>

<publicacoes>

<membro ref="p1"></membro> <membro ref="p2"></membro> </publicacoes>

</entity_name> </entity_set> </xml>

Todos eles foram inseridos em uma pasta chamada

schema

,

que fica no pacote

util

do projeto. Agora que os dados são descritos em arquivos .xml separados do código, qualquer

modificação nos dados não exige mais que o projeto seja recompilado, mas apenas que o

banco de dados seja gerado novamente com o mesmo código gerador. Este código agora se

encarrega apenas de ler estes arquivos .xml e criar as entidades, inserindo-as no banco.

Para isso foram definidas duas classes auxiliares:

2.2.4.5. ReflectionXmlGerador.java

public class ReflectionXmlGerador {

private Document document;

private XMLHelper helper;

(23)

IOException, ParserConfigurationException{ helper = XMLHelper.getInstance();

this.document = helper.getXML(this.getClass().getResourceAsStream(xml)); }

public Collection<Object> inserir(Hashtable<String, Object> map) throws ClassNotFoundException, InstantiationException, IllegalAccessException{

Collection<Object> objects = new ArrayList<Object>();

for(Element entitySet :

helper.getElements(document.getDocumentElement(), "entity_set")){ objects.addAll(this.handleEntitySet(entitySet,map));

}

return objects; }

protected Collection<Object> handleEntitySet(Element entitySet, Hashtable<String, Object> map) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

Class clazz = Class.forName(entitySet.getAttribute("class")); Collection<Object> objects = new ArrayList<Object>();

for(Element entity : helper.getElements(entitySet, "entity_name")){ objects.add(this.handleEntity(entity, clazz, map));

}

return objects; }

protected Object instantiateEntity(Class clazz, Element entity) throws InstantiationException, IllegalAccessException{ return clazz.newInstance();

}

protected Object handleEntity(Element entity, Class clazz, Hashtable<String, Object> map) throws InstantiationException, IllegalAccessException {

Object object = this.instantiateEntity(clazz, entity);

for(Element attribute : helper.getElements(entity)){

Object value = this.handleAttribute(object, attribute, clazz, map); this.setAttribute(value, object, attribute.getNodeName());

}

map.put(entity.getAttribute("id"), object); return object;

}

protected void setAttribute(Object value, Object object, String nodeName) { ReflectionUtil.setField(value, object, object.getClass(), nodeName); }

protected Object handleAttribute(Object object, Element attribute, Class clazz, Hashtable<String, Object> map) {

Class type = getAttributeType(object, clazz, attribute.getNodeName()); Object ret = null;

if(Collection.class.isAssignableFrom(type)){ Collection c = new ArrayList();

for(Element value : helper.getElements(attribute)){

c.add(this.handleValue(object, value, map, String.class)); }

ret = c; }else{

(24)

return ret; }

protected Object handleValue(Object object, Element attribute, Hashtable<String, Object> map, Class type) {

Object ret = null;

if(attribute.getAttribute("ref") != null && !attribute.getAttribute("ref").isEmpty()){ ret = map.get(attribute.getAttribute("ref"));

}else{

ret = this.getValue(type, helper.getContent(attribute)); }

if(ret == null){

System.out.println(object + " - " + type + " - " + attribute.getNodeName()); throw new IllegalArgumentException();

}else{

return ret; }

}

protected Object getValue(Class type, String value) { Object ret = null;

if(type.equals(String.class)){ ret = value;

}else if(type.equals(byte[].class)){ try{

File file = new File(value);

byte[] foto = new byte[(int) file.length()]; FileInputStream reader = new FileInputStream(file); reader.read(foto);

ret = foto; }catch(IOException e){

e.printStackTrace(); }

}

return ret; }

protected Class getAttributeType(Object object, Class clazz, String nodeName) { return ReflectionUtil.getField(clazz, nodeName).getType();

}

}

Essa classe define pontos de extensão a serem usados pela classe que manipula

Publicacao usando AOM:

2.2.4.6. AOMXmlGenerator.java

public class AOMXmlGerador extends ReflectionXmlGerador {

public AOMXmlGerador(String xml) throws SAXException, IOException, ParserConfigurationException{ super(xml);

}

@Override

(25)

return p; }

@Override

protected Class getAttributeType(Object object, Class clazz, String nodeName) { Class c = null;

try{

c = ReflectionUtil.getField(clazz, nodeName).getType(); }catch(IllegalStateException e){

try {

Publicacao publicacao = (Publicacao) object; AOMHelper helper = AOMHelper.getInstance();

c = Class.forName(

helper.getTipoPublicacao(publicacao.getTipo()).getPropertyType(nodeName).getTipo()); } catch (ClassNotFoundException e1) {

e1.printStackTrace(); }

}

return c; }

@Override

protected void setAttribute(Object value, Object object, String nodeName) { try{

super.setAttribute(value, object, nodeName); }catch(IllegalStateException e){

Publicacao publicacao = (Publicacao) object;

publicacao.addProperty(new PropriedadePublicacao(nodeName, (Serializable) value));

} }

}

A nova classe

GerarBanco

ficou assim:

2.2.4.7. GerarBanco.java

public class GerarBanco {

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SAXException, IOException, ParserConfigurationException { Configuration conf = new AnnotationConfiguration();

conf.configure();

SchemaExport se = new SchemaExport(conf); se.create(true, true);

popularBanco(); }

private static void popularBanco() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SAXException, IOException, ParserConfigurationException { try{

Hashtable<String, Object> map = new Hashtable<String, Object>();

(26)

}catch(RGMSException e){ e.printStackTrace(); }

}

private static void inserirLinhasPesquisa(Hashtable<String, Object> map) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SAXException, IOException, ParserConfigurationException, RGMSException {

for(Object object : new ReflectionXmlGerador("linhaspesquisa.xml").inserir(map)){ Facade.getInstance().inserirLinhaPesquisa((LinhaPesquisa) object); }

}

private static void inserirNoticias(Hashtable<String, Object> map) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SAXException, IOException, ParserConfigurationException, RGMSException {

for(Object object : new ReflectionXmlGerador("noticias.xml").inserir(map)){ Facade.getInstance().inserirNoticia((Noticia) object);

} }

private static void inserirPublicacoes(Hashtable<String, Object> map) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SAXException, IOException, ParserConfigurationException, RGMSException {

for(Object object : new AOMXmlGerador("publicacoes.xml").inserir(map)){ Facade.getInstance().inserirPublicacao((Publicacao) object); }

}

private static void inserirMembros(Hashtable<String, Object> map) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SAXException, IOException, ParserConfigurationException, RGMSException {

for(Object object : new ReflectionXmlGerador("membros.xml").inserir(map)){ Facade.getInstance().inserirMembro((Membro) object);

} }

}

2.2.5. Situação posterior

Depois deste refatoramento, a inserção de dados fica separada da definição do mesmo,

sendo que a descrição dos dados agora ocorre livre de código, em arquivos .xml. A inserção

de dados no banco está mais genérica, ligada apenas a um diretório de arquivos xml para

inserção.

(27)

TreeMap em função do número de linhas de código

TreeMap em função da complexidade

(28)

3. Adicionando nova funcionalidade

Nesta seção, uma funcionalidade escolhida será adicionada ao sistema de forma que se

possa gerar uma versão com ou sem tal funcionalidade.

3.1. Funcionalidade escolhida

Como nova funcionalidade no sistema, será implementada a opção de oferecer a

importação de publicações via bibtex. Esta foi a funcionalidade escolhida pois utiliza

diretamente as subclasses de Publicacao, que foram transformadas nesta refatoração em

adaptive model-objects, como foi descrito na sessão anterior. A implementação deste serviço

ficou muito mais fácil com o uso de AOMs, como será visto nesta sessão.

Como este serviço é opcional, foi necessária a aplicação de técnicas de compilação

condicional em todo código inserido especialmente para para esta nova funcionalidade. Deste

modo, apenas os clientes que optarem por esta funcionalidade irão receber uma versão do

sistema que inclui estes bytecodes na versão executável do mesmo.

3.2. Implementação da funcionalidade

Como pré-processador foram utilizados o software

Antenna

, que permite o uso de

diretivas de pré-processamento em código java, e o

Velocity

, que permite a geração de

arquivos .jsp de acordo com algumas diretivas também.

Primeiro, vamos adicionar a funcionalidade em si no projeto. Depois modificaremos o

sistema para permitir que a mesma seja separada em um código opcional, que será compilado

apenas de acordo com o cliente para o qual o executável será destinado.

3.2.1. Formato do arquivo BibTex

A possibilidade de importação de publicações via bibtex exige do sistema a conversão

de arquivos no padrão bibtex em objetos do tipo Publicacao. Na versão do sistema não

refatorada, isso iria exigir um conversor específico para cada tipo de publicação suportado pelo

sistema, e em uma futura modificação em subtipos de publicação ou a inserção de um novo

tipo levaria este código a ser modificado também. Com a versão já refatorada do sistema foi

possível inserir um código mais genérico e flexível para a conversão de um bibtex em uma

publicação.

Um arquivo bibtex gerado pelo atual sistema tem a seguinte estrutura:

3.2.1.1. Exemplo de entrada num arquivo .bib

(29)

atributeName_1 = atributeValue_1, atributeName_2 = atributeValue_2, ...

}

O que muda entre os antigos subtipos de Publicacao são apenas o entry type e a lista

de atributos. Uma vez que cada instância de publicação tem sua lista (no caso implementado

aqui, uma hashtable) de atributos definida em um arquivo independente, não no código, o

mapeamento entre um objeto Publicacao e um bibtex pode ser feito com apenas um codigo

que relaciona estes dois arquivos.

Utilizando uma biblioteca chamada

JavaBib, a manipulaçao de arquivos bibtex fica

mais simples e legível agora que temos uma definição mais genérica de uma Publicação.

3.2.2. Acrescentando a funcionalidade

Utilizando esta classe, o código de

BibTexAspect

que escreve o arquivo bibtex então é

adicionado de um método que realiza a criação de uma publicação a partir de um bibtex. O

código anterior que gerava este tipo de arquivo foi alterado para usar a biblioteca JavaBib

também. Depois destas modificações, a classe ficou assim:

3.2.2.1. BibTexAspect.aj

package br.ufpe.cin.rgms;

import java.io.File;

import java.io.FileNotFoundException; import java.io.PrintWriter;

import bibtex.dom.BibtexEntry; import bibtex.dom.BibtexFile;

import br.ufpe.cin.rgms.publicacao.modelo.PropriedadePublicacao; import br.ufpe.cin.rgms.publicacao.modelo.Publicacao;

//#ifdef PUBLICACAO_BIBTEX_IMPORT import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List;

import bibtex.parser.BibtexParser; import bibtex.parser.ParseException;

import br.ufpe.cin.rgms.publicacao.modelo.TipoPublicacao; import br.ufpe.cin.rgms.util.ListGenerator;

import br.ufpe.cin.rgms.util.aom.AOMHelper; //#endif

privileged public aspect BibtexAspect {

public File Publicacao.createBibTex() throws FileNotFoundException{ BibtexFile file = new BibtexFile();

BibtexEntry entry = file.makeEntry(this.getTipo(), this.getTitulo());

String autoresMembros = (this.listaAutoresMembros()!=null)? this.listaAutoresMembros(): ""; String autoresNaoMembros = (this.listaAutoresNaoMembros()!=null)?

this.listaAutoresNaoMembros():"";

(30)

entry.setField("title", file.makeString(this.getTitulo())); entry.setField("year", file.makeString(this.getAno()));

for(PropriedadePublicacao p : this.getPropriedades()){

entry.setField(p.getNome(), file.makeString(p.getValor().toString())); }

file.addEntry(entry);

File arq = new File(this.getTitulo() + ".bib"); PrintWriter pw = new PrintWriter(arq);

file.printBibtex(pw); pw.close(); return arq; } //#ifdef PUBLICACAO_BIBTEX_IMPORT

public static List<Publicacao> Publicacao.createFromFile(File file) throws IOException, ParseException{ BibtexFile bib = new BibtexFile();

BibtexParser parser = new BibtexParser(true); parser.parse(bib, new FileReader(file));

List<Publicacao> list = new ArrayList<Publicacao>();

for(Object o : bib.getEntries()){

BibtexEntry entry = (BibtexEntry) o;

String author =

StringsUtil.removerColchetesOuAspas(entry.getFields().get("author").toString()); String year =

StringsUtil.removerColchetesOuAspas(entry.getFields().get("year").toString()); String title =

StringsUtil.removerColchetesOuAspas(entry.getFields().get("title").toString());

Publicacao ret = new Publicacao(ListGenerator.createListaMembro(author), ListGenerator.createListaNaoMembro(author), title, year, null, entry.getEntryType());

AOMHelper helper = AOMHelper.getInstance();

TipoPublicacao tipo = helper.getTipoPublicacao(ret.getTipo());

for(Object field : entry.getFields().keySet()){

if(tipo.getPropertyType(field.toString()) != null){

ret.addProperty(new PropriedadePublicacao(field.toString(), entry.getFieldValue(field.toString()).toString())); } } list.add(ret); } return list; } //#endif }

(31)

3.2.2.2. AdicionarPublicacoesFromBibTexServlet.java

//#ifdef PUBLICACAO_BIBTEX_IMPORT

package br.ufpe.cin.rgms.publicacao.controle;

public class AdicionarPublicacoesFromBibTexServlet extends RGMSUploadFormServlet {

@Override

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { byte[] bytes = this.getArquivo();

if(bytes != null){

File file = File.createTempFile("bibtex", "publicacao");

FileOutputStream fos = new FileOutputStream(file); fos.write(bytes);

fos.close();

try {

List<Publicacao> publicacoes = Publicacao.createFromFile(file);

for (Publicacao publicacao : publicacoes) {

Facade.getInstance().inserirPublicacao(publicacao);

}

this.handleSuccess(request, response); } catch (Exception e) {

this.handleError(request, response, e);

}

} else {

this.handleError(request, response, new IllegalArgumentException("Problemas ao ler informações do arquivo recebido.")); }

}

protected void handleError(HttpServletRequest request, HttpServletResponse response, Exception erro) { request.setAttribute("publicacaostatus", erro.getMessage());

}

protected void handleSuccess(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { request.setAttribute("publicacaostatus", "Publicacao inserida com sucesso.");

} } //#endif

O último passo consiste em adicionar na camada View a opção de fazer upload de um

arquivo .bib para a inserção de publicações e habilitar o servlet definido somente se tal

funcionalidade estiver habilitada. Para isso, o JSP

adicionarpublicacao.jsp

foi modificado,

e um novo formulário com o campo de upload foi adicionado na página. Para isso foi utilizada a

ferramenta Velocity.

3.2.2.3. adicionarpublicacao.jsp

...

#if($publicacao_from_bibtex)

(32)

enctype="multipart/form-data"> <p>

<LABEL for="bibtexfile">

<%out.println(Properties.getProperty(this.getServletContext(),"pdf"));%> </LABEL> <input onkeypress="return noenter();" type="hidden" name="MAX_FILE_SIZE" value="500" />

<input name="bibtexfile" type="file" /> </p>

</FORM> #end

...

E no web.xml:

3.2.2.4. web.xml

#if($publicacao_from_bibtex) <servlet>

<servlet-name>AdicionarPublicacoesFromBibTex</servlet-name>

<servlet-class>br.ufpe.cin.rgms.publicacao.controle.AdicionarPublicacoesFromBibTexServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>AdicionarPublicacoesFromBibTex</servlet-name> <url-pattern>/AdicionarPublicacoesFromBibTex.do</url-pattern> </servlet-mapping>

#end

Para concluir, foi feito um gerador para poder gerar versões do RGMS com ou sem a

funcionalidade opcional adicionada, preprocessando a parte do velocity e permitindo gerar o

código final com o Antenna.

adicionarpublicacao.jsp gerado pelo Velocity no RGMSGenerator. Perceba também que o projeto gerado não contém o pacote util.generator

3.2.2.5. RGMSGenerator.java

public class RGMSGenerator {

(33)

private static VelocityContext context;

private static List<String> skipFolders;

public static void processDirectory(File dir, String path)

throws ResourceNotFoundException, ParseErrorException, Exception{ new File("../" + path).mkdirs();

if(dir.isDirectory()){

for(File file : dir.listFiles()){ if(file.isDirectory()){

if(!skipFolders.contains(file.getName())){

processDirectory(file, path + "/" + file.getName()); }

}else{

File f = new File("../" + path + "/" +

file.getName()).getAbsoluteFile(); f.createNewFile(); processFile(f, file); } } } }

private static void processFile(File f, File original) throws ParseErrorException, Exception { try{ System.out.println(original.getAbsolutePath()); if(original.getName().endsWith(".jsp") || original.getName().endsWith(".xml") || original.getName().endsWith(".properties") || original.getName().endsWith(".java")){

Template t = ve.getTemplate( original.getAbsolutePath() );

PrintWriter writer = new PrintWriter(f); t.merge( context, writer );

writer.close(); }else{ copy(f, original); } }catch(ResourceNotFoundException e){ copy(f, original); } }

private static void copy(File f, File original) throws IOException{ FileCopyUtils.copy(original, f);

}

public static void main( String args[] ) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, Exception

{

ve = new VelocityEngine();

Properties config = new Properties(); config.put("resource.loader", "file"); config.put("file.resource.loader.class",

"org.apache.velocity.runtime.resource.loader.FileResourceLoader"); config.put("file.resource.loader.path", "");

config.put("file.resource.loader.cache", "true");

skipFolders = new ArrayList<String>(); skipFolders.add(".svn");

skipFolders.add("generator"); skipFolders.add("build"); skipFolders.add(".classes");

(34)

context = new VelocityContext();

Properties props = new Properties();

props.load(RGMSGenerator.class.getResourceAsStream("RGMSGenerator.properties"));

for(String feature : props.stringPropertyNames()){ context.put(feature, props.get(feature)); }

File file = new File("").getAbsoluteFile(); processDirectory(file, "/Generated/RGMS_Work"); }

}

O ideal é que essa classe seja substituída posteriormente por um gerador menos

mecânico. Essa classe foi criada apenas para efeito de teste. Após todas estas modificações,

na versão do projeto que inclui a nova funcionalidade a página de inserção de publicação ficou

assim:

(35)

Tela de sucesso mostrada após inserção de publicações por meio de um arquivo bibtex.

Para poder testar, outro erro teve que ser corrigido no

ListGenerator

onde a busca de

membros no banco de dados, dada uma lista de nomes, era feita. Depois disto, a classe

ListGenerator

foi toda modificada, ficando assim:

3.2.2.6. ListGenerator.java

package br.ufpe.cin.rgms.util;

public class ListGenerator {

public static List<Membro> createListaMembro(String autoresMembros){ List<Membro> ret = new ArrayList<Membro>();

List<Membro> membros = Facade.getInstance().getMembros();

String[] names = autoresMembros.split(",");

for (String string : names) {

for (Membro membro : membros) {

if(string.equals(membro.getNome()+" "+membro.getSobrenome())){

ret.add(membro);

}

}

}

return ret;

}

public static List<String> createListaNaoMembro(String autoresNaoMembros){ List<String> lista = Arrays.asList(autoresNaoMembros.split(",")); return lista;

(36)

4. Otimizando refatoramentos anteriores

4.1. Impactos das alterações

Após as mudanças feitas no problema 1 principalmente, o mecanismo de validação

definido no refatoramento da etapa anterior não é mais completo pois algumas propriedades de

Publicacao não podem ser obtidas por reflexão como decorrência do uso do padrão AOM.

4.2. Adaptando o mecanismo de validação

Precisamos alterar o mecanismo de validação definido anteriormente para que ele

possa obter as propriedades de uma Publicacao não só por reflexão, mas também pela lista de

propriedades associada a ela.

4.2.1. Situação anterior

A interface de definição de regras de validação é da forma:

public interface IValidationRule {

public void setParameters(String[] parameters);

public boolean validate(Object value);

}

O xml publicacao-aom.xml era da seguinte forma:

<?xml version="1.0" encoding="ISO-8859-1"?>

<aom xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:publicacao-aom.xsd">

<publicacao-types> <publicacao-type>

<entry-type>conference</entry-type>

<text-option>Artigo em Conferência</text-option>

<optional-attributes> <attribute>

<name>pages</name>

<class>java.lang.String</class> <text>paginas</text>

</attribute> </optional-attributes> <required-attributes>

<attribute>

<name>booktitle</name>

(37)

<text>conferencia</text>

</attribute> </required-attributes> </publicacao-type>

... <publicacao-types> </aom>

Apenas há regra de obrigatoriedade em atributos. Isso se torna um pouco limitado.

4.2.2. Otimizando a solução

Vamos fazer de forma similar ao antigo mecanismo: associar a cada propriedade de

publicação um conjunto de regras. Isso pode ser atingido utilizando o padrão

RuleObject

que se adequa ao AOM perfeitamente. Dessa forma, publicacao-aom.xml pode ter um atributo

assim:

4.2.2.1. Novo trecho do publicacao-aom.xml

<attribute name=”pages”>

<class>java.lang.String</class> <text>paginas</text>

<validation> <rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RangeLengthRule</class> <param>

<name>min</name> <value>1</value> </param>

<param>

<name>max</name> <value>20</value> </param>

</rule> <rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RequiredFieldValidationRule</class> </rule>

</validation> </attribute>

E a interface de regra de validação associa um nome a cada parâmetro:

4.2.2.2. IValidationRule

public interface IValidationRule {

public void setParameters(Hashtable<String, String> parameters);

public boolean validate(Object value);

}

(38)

4.2.2.3. Exemplo de RuleObject - RangeLengthRule

public class RangeLengthRule implements IValidationRule {

private int max; private int min;

@Override

public void setParameters(Hashtable<String, String> parameters) { this.max = Integer.parseInt(parameters.get("max")); this.min = Integer.parseInt(parameters.get("min")); }

public boolean checkLength(Object value){ int length;

if(value != null){

if(value instanceof String){

length = ((String) value).length(); }else if(value instanceof Collection<?>){

length = ((Collection<?>) value).size(); }else{

throw new IllegalArgumentException(); }

}else{

throw new IllegalArgumentException(); }

return length >= this.min && length <= this.max; }

@Override

public boolean validate(Object value) { boolean valid = value != null;

if(valid){

if(value instanceof PropriedadePublicacao){

value = ((PropriedadePublicacao) value).getValor(); }

valid = this.checkLength(value); }

return valid; }

}

Antes a validação era definida nas demais classes com arquivos de propriedades.

Preferiu-se alterar para uma representação com xml pelo fato de ser mais legível:

4.2.2.4. AbstractBusinessEntity.validate.properties

(39)

4.2.2.5. AbstractBusinessEntity.validation.xml

<?xml version="1.0" encoding="UTF-8"?>

<validation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="file:../../../../../validation.xsd"> <attribute name="id">

<rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RequiredFieldValidationRule</class> </rule>

</attribute> </validation>

Para isso, o PropertiesValidationUtil foi substituido por um XMLValidationUtil:

4.2.2.6. XMLValidationUtil.java

@Service(value = "XMLValidationUtil")

public class XMLValidationUtil implements IValidationUtil {

private IValidationRuleFactory factory;

@Autowired

public void setValidationRuleFactory(

@Qualifier(value="ValidationRuleFactory") IValidationRuleFactory factory){ this.factory = factory;

}

private boolean validate(AbstractBusinessEntity object, Class clazz){ try{

XMLHelper helper = XMLHelper.getInstance(); Document xml =

helper.getXML(clazz.getResourceAsStream(String.format("%s.validation.xml", clazz.getSimpleName()))); boolean validate = true;

Iterator<Element> attributes =

helper.getElements(xml.getDocumentElement(), "attribute").iterator();

while(attributes.hasNext() && validate){ Element property = attributes.next();

String propertyName = property.getAttribute("name"); Iterator<Element> rules =

helper.getElements(property, "rule").iterator();

while(rules.hasNext() && validate ){ Element rule = rules.next();

Hashtable<String, String> map = new Hashtable<String, String>(); Iterator<Element> params =

helper.getElements(rule, "param").iterator();

while(params.hasNext()){

Element param = params.next(); String name =

helper.getContent(helper.getMatchingElement(param, "name")); String value =

helper.getContent(helper.getMatchingElement(param, "value")); map.put(name, value);

}

IValidationRule validation = factory.createValidationRule( helper.getContent(helper.getMatchingElement(rule, "class")), map);

(40)

ReflectionUtil.getFieldValue(object, object.getClass(), propertyName));

} }

return validate; }catch(IOException e){

throw new IllegalStateException(); }catch(ParserConfigurationException e){

throw new IllegalStateException(); } catch (SAXException e) {

throw new IllegalStateException(); }

}

public boolean validate(AbstractBusinessEntity object){ Class<?> c = object.getClass();

boolean valid = true;

while(!c.equals(Object.class) && valid){ valid = this.validate(object,c); c = c.getSuperclass();

}

return valid; }

}

4.2.2.7. Alteração no AOMHelper

Com isso, o seguinte método foi acrescentado no AOMHelper:

public boolean validate(Publicacao publicacao){

TipoPublicacao tipo = this.getTipoPublicacao(publicacao.getTipo()); boolean valid = true;

Iterator<String> fields = tipo.getPropertyTypes().keySet().iterator();

while(valid && fields.hasNext()){ String field = fields.next();

TipoPropriedade property = tipo.getPropertyTypes().get(field); Iterator<IValidationRule> rules = property.getRules().iterator();

while(valid && rules.hasNext()){

valid = rules.next().validate(publicacao.getProperty(field)); }

}

return valid; }

4.2.2.8. Alteração no PublicacaoManager

Por fim, no PublicacaoManager, o código de validar foi estendido:

@Override

(41)

if(!AOMHelper.getInstance().validate(tipo)){

throw new RGMSException("Dados inválidos na inserção"); }

}

4.2.3. Situação posterior

O xml de publicacao-aom.xml ficou no seguinte formato:

<?xml version="1.0" encoding="ISO-8859-1"?>

<aom xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:publicacao-aom.xsd"> <publicacao-types>

<publicacao-type>

<entry-type>conference</entry-type>

<text-option>artigo_conferencia</text-option> <attribute-list>

<attribute name="pages">

<class>java.lang.String</class> <text>paginas</text>

<validation> <rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RangeLengthRule</class> <param>

<name>min</name> <value>1</value> </param>

<param>

<name>max</name> <value>20</value> </param>

</rule> <rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RequiredFieldValidationRule</class> </rule>

</validation> </attribute>

<attribute name="booktitle">

<class>java.lang.String</class> <text>conferencia</text>

<validation> <rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RequiredFieldValidationRule</class> </rule>

</validation> </attribute>

<attribute name="month"> <name>month</name>

<class>java.lang.String</class> <text>mes</text>

</attribute> </attribute-list> </publicacao-type> <publicacao-type>

<entry-type>article</entry-type>

(42)

<attribute name="pages">

<class>java.lang.String</class> <text>paginas</text>

</attribute>

<attribute name="number">

<class>java.lang.String</class> <text>numero</text>

</attribute>

<attribute name="volume">

<class>java.lang.String</class> <text>volume</text>

</attribute>

<attribute name="journal">

<class>java.lang.String</class> <text>jornal</text>

<validation> <rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RequiredFieldValidationRule</class> </rule>

</validation> </attribute>

</attribute-list> </publicacao-type> <publicacao-type>

<entry-type>phdthesis</entry-type> <text-option>pos</text-option> <attribute-list>

<attribute name="month">

<class>java.lang.String</class> <text>mes</text>

</attribute>

<attribute name="school">

<class>java.lang.String</class> <text>universidade</text> <validation>

<rule>

<class>br.ufpe.cin.rgms.base.validation.rule.RequiredFieldValidationRule</class> </rule>

</validation> </attribute>

Referências

Documentos relacionados

O estudo foi gerado pela problemática “Como se dá o processo de escolarização dos alunos ribeirinhos em escolas urbanas?”, sendo que os resultados que dele emergiram versam sobre

A Lei nº 2/2007 de 15 de janeiro, na alínea c) do Artigo 10º e Artigo 15º consagram que constitui receita do Município o produto da cobrança das taxas

Capítulo 7 – Novas contribuições para o conhecimento da composição química e atividade biológica de infusões, extratos e quassinóides obtidos de Picrolemma sprucei

Nas larvas mantidas sem alimento, as mesmas características foram observadas: o epitélio do mesêntero tornou-se mais achatado e células normais circundadas por células

Em todas as vezes, nossos olhos devem ser fixados, não em uma promessa apenas, mas sobre Ele, o único fundamento da nossa esperança, e em e através de quem sozinho todas as

A Tabela 11 sistematiza os quocientes de representatividade obtidos através do cruzamento entre os dados pessoais dos respondentes e a questão 14, que pretende aferir se a

Both the distribution of toxin concentrations and toxin quota were defined by epilimnetic temperature (T_Epi), surface temperature (T_Surf), buoyancy frequency (BuoyFreq) and

Assim, este trabalho apresenta uma abordagem que tem como objetivo principal: (i) analisar a cobertura de código levando em consideração os fluxos de chamadas existentes no sistema