Livro completo - Departamento de Informática e Estatística
Transcrição
Livro completo - Departamento de Informática e Estatística
ERCEMAPI 2011 V Escola Regional de Informática Ceará – Maranhão – Piauí Teresina, 07 e 08 de novembro de 2011 LIVRO TEXTO DOS MINICURSOS Edição Sociedade Brasileira de Computação Organizadores André Macêdo Santana Pedro de Alcântara dos Santos Neto Raimundo Santos Moura Promoção Sociedade Brasileira de Computação Patrocínio Realização Universidade Federal do Piauí Instituto Federal de Educação, Ciência e Tecnologia do Piauí Apoio Fundação de Amparo a Pesquisa do Estado do Piauí Copyright Sociedade Brasileira de Computação Todos os direitos reservados i Escola Regional de Informática Ceará, Maranhão, Piauí (2011: Teresina, PI) Escola Regional de Informática Ceará, Maranhão, Piauí: Livro texto dos minicursos, 07 e 08 de novembro de 2011. [livro eletrônico] / organizadores, André Macêdo Santana, Pedro de Alcântara dos Santos Neto, Raimundo Santos Moura. – Teresina: Sociedade Brasileira de Computação – SBC, 2011. 200p. : il. ; livro eletrônico. Realização: Universidade Federal do Piauí - UFPI. Promoção: Sociedade Brasileira de Computação – SBC. ISBN 978-85-7669-258-4. 1. Computação; Ciência da Computação; I. Santana, André Macêdo. II. Santos Neto, Pedro de Alcântara dos. III. Moura, Raimundo Santos IV. Título. CDD 004.1 ii ERCEMAPI 2011 PREFÁCIO Em 2007, quando da primeira edição da Escola Regional de Informática Ceará Maranhão - Piauí (ERCEMAPI) houve muitos questionamentos sobre a continuidade dessa ideia, que foi inicialmente encabeçada por um grupo cearense. Sua segunda edição (2008), no estado do Maranhão, foi bastante tímida e os questionamentos continuaram. Em 2009, tivemos a consolidação do evento, com uma participação maciça de estudantes do Piauí, Ceará e Maranhão, além de estudantes de outros estados. O evento foi pela primeira vez organizado fora das capitais dos estados e mostrou que a ideia de sua realização era totalmente válida e bem aceita pela comunidade acadêmica dos estados pertencentes à então regional Nordeste 3. Agora em 2011, na sua quinta edição, o Piauí teve a honra de sediar o evento mais uma vez. Houve uma inversão na ordem de organização, entre Piauí e Maranhão, à pedido dos representantes maranhenses. A responsabilidade de organização neste ano era, maior ainda, dado o sucesso da edição anterior realizada em Sobral-CE. No entanto, mais uma vez conseguimos superar todas as expectativas e realizar um evento memorável, que só estimula a sua ampliação. Houve cerca de 500 participantes nesta edição, um recorde muito difícil de ser batido! Geramos um grande “problema“ aos nossos colegas maranhenses, que terão que trabalhar bastante para tentar manter o ritmo crescente do evento. Certamente teremos a dedicação dos nossos vizinho nessa tarefa, além do nosso apoio incondicional ao que for necessário para tal realização. O grande sucesso da edição 2011 deveu-se à realização conjunta do evento com um evento tradicional no estado, o Simpósio Piauiense de Informática (InfoPI), bem como a realização da Escola de Microeletrônica do Piauí e da Escola de Sistemas Embarcados do Piuaí. Foi uma avalanche de conhecimento concentrada em uma semana inteira de palestras, minicursos e apresentações de trabalho. iii O objetivo inicial do evento, quando criado em 2007, era de aproximar os estados vizinhos nas atividades de ensino e pesquisa. Neste ano, pudemos notar que o objetivo inicial foi alcançado. Atualmente existem orientações de trabalhos de mestrado em conjunto, participação em bancas e desenvolvimento de artigos. Tudo isso fruto da aproximação propiciada por esse evento, que já faz parte do calendário acadêmico dos três estados. Sua força é tanta que, mesmo com a criação de uma regional da SBC específica para o estado do Ceará, continuaremos juntos nas próximas edições do ERCEMAPI. É com esse espírito que gostaríamos de expressar nosso agradecimento a todos os que contribuíram para tornar a ERCEMAPI um evento de tanto sucesso. Próximo ano estaremos na "terra das palmeiras” para fincar a bandeira do ERCEMAPI em definitivo também nesse estado. Que venha a edição de 2012! Teresina, novembro de 2011 André Macêdo Santana Pedro de Alcântara dos Santos Neto Raimundo Santos Moura iv ERCEMAPI 2011 V Escola Regional de Informática – Ceará – Maranhão – Piauí COMISSÃO ORGANIZADORA Coordenador Geral: Raimundo Santos Moura Coordenação do Comitê de Programa: André Macêdo Santana Comissão Científica: André Castelo Branco Soares Erick Baptista Passos Ivan Saraiva Silva Comissão Financeira: Fabio de Jesus Lima Gomes Kelson Rômulo Teixeira Aires Comissão de Marketing e Divulgação: Adalton de Sena Almeida Harilton da Silva Araújo Joselé Martins Pedro de Alcântara dos Santos Neto Vinicius Ponte Machado Comissão de Infra-Estrutura: Eduilson Lívio Neves da Costa Carneiro Rodrigo de Melo Sousa Veras v ERCEMAPI 2011 SUMÁRIO Capítulo 01 Linhas de produto de software: Uma tendência da indústria 07 Airton Silva (UFPE), Paulo Anselmo (UFPE), Vinicius Garcia (UFPE) e Patrícia Muniz (RISE) Capítulo 02 Planejamento de capacidade de sistemas através de Cadeias de Markov 32 Rubens de Souza Matos Júnior (UFPE), Carlos Julian Araújo (UFPE), Franscico de Souza (UFPI) e Paulo Maciel (UFPE) Capítulo 03 Computação autônomica aplicada a segurança de redes 54 Ariel Teles (UFMA), Francisco José Silva (UFMA) e Zair Abdelouahab (UFMA) Capítulo 04 Linked Data: da Web de documentos para a web de dados 79 Bernadette Lóscio (UFPE), Danusa Ribeiro Bezerra da Cunha (UFCE) e Damires Sousa (IFPB) Capítulo 05 Desenvolvimento de Aplicações para Plataforma Google Android 100 Fabio de Jesus Lima Gomes (IFPI), Manoel Taenan (IFPI) e Rafael Lins (IFPI) Capítulo 06 Desenvolvendo aplicações multi-tenancy para computação em nuvem 124 Josino Neto (UFPE), Vinicius Garcia (UFPE) e Wilton Oliveira Ferreira (UFPE) Capítulo 07 Introdução a agentes autônomos e sistemas multiagentes 141 Samy Sá (UFCE), João Alcântara (UFCE) e Marcos de Oliveira (UFCE) Capítulo 08 Desevolvimento para dispositivos móveis que utilizar a plataforma iOS 166 Ricardo Freitas (UFPI), Roniel Soares (UFPI), José Almi Soares Filho (UFPI), Kelson Aires (UFPI), André Soares (UFPI), Vinicius Machado (UFPI), Laurindo Neto (UFPI) Capítulo 09 Desenvolvimento de aplicações móveis utilizando a linguagem declarativa QML 191 Ricardo Erikson (UFAM), Adriano Gil (UFAM), Paulo Mendonça (UFAM), Cícero Costa Filho (UFAM) e Vicente Lucena (UFAM) Capítulo 10 Técnicas de Processamento de imagens em diagnóstico auxiliado por computador Rodrigo Veras (UFPI), Iális Paula Jr (UFCE) e Fátima Medeiros (UFCE) vi 216 Capítulo 1 Linhas de Produtos de Software: Uma tendência da indústria Francisco Airton Pereira da Silva, Paulo Anselmo da Mota Silveira Neto, Vinicius Cardoso Garcia e Patrícia Fontinele Muniz Abstract Over the past few years a new approach to software reuse has gained attention both by industry and academia. This approach is known as software product line development wich is based upon the systematic reuse of software artifacts, through exploiting commonalities and managing variabilities among products, that are established under a common architecture. This concept of product lines have been used by the manufacturing industry for a long time to reduce costs and increase productivity. However, product line practice in the software industry is a relatively new concept. Studies have shown that organizations can yield remarkable improvements mainly in productivity by applying this approach. In this context, this chapter presents some of the most important aspects related to software product lines, such as variability, development processes and finally presents some tools to support Software Product Line (SPL). Resumo Ao longo dos últimos anos uma nova abordagem de reuso de software tem ganhado atenção tanto pela indústria quanto pela academia. Esta abordagem é conhecida como Linhas de Produtos de Software na qual é baseada na reutilização sistemática de artefatos de software, através da exploração de pontos comuns e a gestão de variabilidade entre os produtos, que são estabelecidos sob uma mesma arquitetura. Este conceito de linhas de produtos tem sido utilizado pela indústria de manufatura a anos para reduzir custos e aumentar a produtividade. No entanto esta prática possui um conceito relativamente novo na indústria de software. Estudos têm demonstrado que as organizações têm apresentado melhorias principalmente na produtividade aplicando esta abordagem. Neste contexto, este trabalho apresenta alguns dos aspectos mais importantes sobre linhas de produtos de software, tais como variabilidade, processos de desenvolvimento e finalmente mostrando algumas ferramentas de apoio a Linhas de Produtos de Software (LPS). 7 1.1. INTRODUÇÃO A maneira que os bens de consumo são produzidos mudou significativamente no decorrer do tempo. Anteriormente mercadorias eram especificas para clientes individuais. Cada vez mais, o número de pessoas que poderiam ter recursos para comprar vários tipos de produto foi aumentando. No domínio dos automóveis, isso levou à invenção por Henry Ford da linha de produção, o que permitiu a produção para um mercado de massa muito mais barata do que a criação de cada produto em uma base artesanal. No entanto, a linha de produção reduziu as possibilidades de diversificação. A grosso modo, ambos os tipos de produtos, individual e os produzidos em massa também podem ser identificados no domínio de software: eles são denominados como software individual e software padrão. Geralmente, cada um destes tipos de produtos tem suas desvantagens. Produtos de software individuais são bastante caros pois exigem um maior esforço desenvolvendo espeficidades de um único produto, enquanto produtos de software padrão (sem muita especificidade) existe a falta de diversificação suficiente para atender bem às expectativas de muitos clientes diferentes. No início os clientes estavam satisfeitos com os produtos padronizados em massa por um tempo, mas nem todas as pessoas queriam o mesmo tipo de carro para qualquer finalidade. Certos carros são usados para viajar por uma única pessoa, outros por grandes famílias por exemplo. Assim, a indústria foi confrontada com uma crescente demanda por produtos individualizados. Este foi o início da chamada customização em massa, o que significava atender às exigências dos clientes dando-lhes o que eles queriam. Para o cliente a customização em massa significa a capacidade de ter um produto individualizado, no entanto para a indústria significa maiores investimentos em tecnologia que elevam os preços de produtos individualizados e / ou menores margens de lucro para a empresa. Ambos os efeitos são indesejáveis. Assim, muitas empresas, especialmente na indústria automobilística, começaram a apresentar plataformas comuns para os seus diferentes tipos de carros planejando de antemão quais peças seriam usadas em vários tipos de carros diferentes. Ao longo do tempo a plataforma foi sendo trabalhada a fim de se tornar cada vez mais adaptável a novos componentes. As partes compreendendo a plataforma eram geralmente mais caras em termos do projeto e os custos de preparação de fabricação. Porém esta estratégia levou a uma redução no custo de produção para um tipo de carro particular [21]. A indústria de automóveis passou por esta transformação na forma de produzir seus produtos e a indústria de software também necessita atender ao mesmo requisito de individualidade e cada vez mais produção em massa. O mercado de desenvolvimento de software precisa construir os produtos de software com melhor qualidade, redução de custos, adaptação rápida às mudanças, e menor tempo de colocação no mercado atendendo às necessidades dos clientes. Visando estas expectativas vários esforços em criar processos e arquituras de sistemas tem sido realizadas ao longo dos anos [16]. Seguindo por este caminho uma nova abordagem para reutilização de software tem ganhado considerável atenção tanto pela indústria quanto pela academia. Esta abordagem é conhecida como Linha de Produtos de Software (LPS) onde a idéia básica é o 8 trabalho sobre um grupo de sistemas compartilhando um conjunto comum e gerenciado de funcionalidades (features) que satisfazem necessidades específicas de um segmento, e desenvolvidos a partir de um aglomerado comum de artefatos base e de forma previamente planejada. Em outras palavras LPS faz uso da reusabilidade para construir sistemas com menos esforço desde que estes pertençam a uma mesma família, ou seja, que possuam pontos em comum [17]. Este capítulo está organizado como segue: na seção 1.2 será apresentada uma visão geral sobre as peculiaridades sobre Linhas de Produtos de Software, incluindo idéias comparativas entre estas metodologias e o reuso de software tradicional. Na seção 1.3 como as variantes de um produto podem ser gerenciadas. Na seção 1.4 atividades exercidas no desenvolvimento de LPS, mostrando algumas ferramentas. Finalmente na seção 1.5 são apresentadas as conclusões. 1.2. VISÃO GERAL SOBRE LINHAS DE PRODUTOS DE SOFTWARE Os artefatos mencionados na seção anterior devem ser reutilizados de uma forma consistente e sistemática, a fim de construir aplicativos robustos em LPS. Artefatos reutilizáveis abrangem todos os tipos de artefatos de desenvolvimento de software, tais como modelos de requisitos, modelos de arquitetura, componentes de software e planos de teste. A experiência de projetos que fazem uso de reutilização na década de 1990 mostraram que, sem planejamento adequado, os custos do projeto com reutilização pode ser maior do que para desenvolver os artefatos a partir do zero. Assim, é fundamental planejar com antecedência os produtos para os quais a reutilização será aplicada, juntamente com as features que caracterizam estes produtos. O planejamento para reutilização continua durante todo o processo de desenvolvimento [21]. Para facilitar a customização em massa a plataforma deve fornecer os meios para satisfazer as necessidades dos diferentes stakeholders. Para este propósito, o conceito de variabilidade foi criado, para explorar as características que variam em relação aos diversos produtos. Como conseqüência de aplicar este conceito, os artefatos que podem serem diferentes nas aplicações da linha de produtos são modelados usando variabilidade [21]. 1.2.1. Processos de Desenvolvimento O paradigma de linha de produtos de software é dividido em dois processos, a saber: 1. Engenharia de Domínio: Este processo é responsável por estabelecer a plataforma de reutilização e, assim definir comunalidade e a variabilidade da linha de produtos. A plataforma consiste em todos as tipos de artefatos de software (requisitos, design, testes, etc.) também chamados de ativos base. 2. Engenharia de Aplicação: Este processo é responsável por derivar aplicações concretas a partir da plataforma estabelecida na engenharia de domínio. Ela explora a variabilidade da linha de produtos e assegura sua correta instanciação de acordo com as necessidades específicas das aplicações finais. 9 Figura 1.1. Dois ciclos de vida que separam engenharia de domínio e aplicação. A vantagem dessa divisão é que há uma separação de objetivos, para construir uma plataforma robusta e para construir aplicações específicas em um curto espaço de tempo. Para ser eficaz, os dois processos devem interagir de uma maneira que seja benéfica para ambos. A Figura 1.1 mostra como estes processos interagem entre si e qual o resultado dos mesmos através de um fluxo de informações. Tanto na engenharia de domínio quanto na engenharia de aplicação são realizadas atividades de análise, arquitetura, implementação e testes para ao final serem gerados os produtos. 1.2.2. Linha de Produtos de Software e Reuso de Software Tradicional Desenvolvimento de uma LPS envolve "reuso"e a primeira vista este desenvolvimento pode parecer apenas um reuso de software tradicional. No entanto desenvolvimento de uma linha de produtos de software é muito mais elaborado do que a reutilização de software tradicional. No reuso de software tradicional, as organizações possuem repositórios onde a saída de praticamente todo o esforço de desenvolvimento é armazenado. Este repositório normalmente contêm alguma biblioteca de reutilização de componentes, módulos e algoritmos que desenvolvedores são incentivados a usar. O problema com este tipo de reutilização é que, geralmente, leva mais tempo para encontrar a funcionalidade desejada e adaptá-la à aplicação atual do que para construí-la novamente [7]. Este tipo de reutilização ad hoc não é o que caracteriza o desenvolvimento de linhas de produtos de software. Em desenvolvimento de linhas de produtos o reuso é planejado, automatizado e sistemático [20]. O "repositório de reuso"de uma linha de 10 produtos de software é conhecido como ativos base. Estes elementos incluem todos os artefatos que são os mais caros para desenvolver como modelos de domínio, requisitos, arquitetura, componentes, casos de teste, etc. Além disso, esses ativos são do início do desenvolvido a fim de serem reutilizados em vários produtos. Isto significa que a customização de ativos para o produto atual, normalmente não inclue nenhum código novo como ocorreria em abordagens tradicionais. Ao invés disto ocorre uma instanciação do produto, utilizando mecanismos de variabilidade incorporadas aos ativos base. Outra abordagem para reutilização de software que se assemelha a de desenvolvimento de software em linhas de produtos é conhecido como a abordagem "clone and own"[20]. Os produtos desenvolvidos por esta abordagem tem um foco no produto individual, não ocorre um foco na família de produtos de software, e portanto, não implementa os mecanismos de variabilidade que caracterizam o desenvolvimento da família de produtos. Portanto quando um projeto novo é iniciado por esta abordagem, a equipe de desenvolvimento tenta encontrar um outro produto dentro da organização que se assemelha o produto atual, tanto quanto possível. Eles, então, copiam tudo o que podem a partir desse projeto, modificam e adicionam o que é necessario para lançar o novo produto. Esta abordagem pode render uma economia considerável em comparação com desenvolvimento de todos os produtos a partir do zero. No entanto, ao comparar o "clone and own"com a abordagem de desenvolvimento de linha de produtos, o "clone and own"apresenta algumas desvantagens principais: 1. No desenvolvimento de linha de produtos todos os artefatos de maiores custos do projeto são reutilizados, não apenas o código que é o foco principal da abordagem "clone and own". 2. No desenvolvimento de linha de produtos todos os ativos base são desenvolvidos tendo em mente a idéia de reutilização e variabilidade. Na abordagem "clone and own", tudo é desenvolvido com foco no produto individual, onde os recursos não serão gastos no desenvolvimento de mecanismos de variabilidade que não serão usados por este produto. Isto implica que o "clone"exigirá um esforço considerável de customização para atender os requisitos do novo produto comparado a outro dentro de uma linha de produtos. Os ativos base de uma linha de produtos provavelmente não irão precisar de um alto grau de customização para atender aos requisitos de novos produtos, mas sim uma instanciação de mecanismos integrados de variabilidade anteriormente construídos. 3. Quando "clonamos"um produto já existente, para criar um novo produto, as ligações entre estes projetos são inexistentes e futuramente ocorrerá manutenções individuais nos dois produtos. No caso do desenvolvimento de linha de produtos, uma economia considerável pode ser realizada ao longo do ciclo de vida destes, pois a manutenção dos ativos base é uma responsabilidade compartilhada por todos os produtos na família. 11 1.2.3. Aspectos importantes na adoção de Linhas de Produtos de Software A adoção de linhas de produtos de software é diferente em alguns sentidos em relação a adopção de outros tipos de tecnologias e processos. Sua adoção requer bastante tempo, principalmente em estágios iniciais [17]. Isso envolve a mudança na forma de desenvolvimento de sistemas que possui a idéia de sistema único para o desenvolvimento de vários produtos a partir de uma plataforma [19]. A adoção envolve ter uma base de ativos base, processos de suporte, e estruturas organizacionais; desenvolver produtos a partir da base de ativos de uma forma que sejam atingidos objetivos de negócio; e instituir mecanismos para melhorar e extender o esforço de produção de software, desde que faça sentido. No entanto, dependendo do cenário, atingir as metas de adoção pode tornar-se uma atividade complexa. A fim de evitar complexidades adicionais durante a fase de adoção, Gary [8] argumenta que uma organização que pretende adotar uma abordagem de linha de produto deve ter objetivos claros em mente. Para atingir seus objetivos, a organização seleciona um ou mais estratégias de adoção que especificam como ele serão abraçadas as práticas de linhas de produtos. Em seguida, um plano de adoção mostra em detalhes que atividades devem ser realizadas para implementar essas estratégias. Neste sentido, todo o conceito de adoção de linha de produtos se torna muito pessoal para a organização, o contexto específico parece desempenhar um papel significativo na decisão de adoção [8]. Portanto, a transferência deve ser sistematicamente e planejada. 1.2.4. Vantagens em Linhas de Produtos de Software Teorias relacionadas a linhas de produtos de software pode gerar diversos benefícios classificados em três tipos: benefícios organizacionais, os benefícios de engenharia de software e os benefícios de negócio. Os benefícios organizacionais agrupam vantagens como uma melhor compreensão do domínio, a maior facilidade de treinar pessoas, um produto de maior qualidade e consequentemente confiança do cliente. Os benefícios da engenharia de software incluem vantagens como a reutilização de requisitos e seus componentes, uma melhor análise de requisitos, uma outra visão sobre os requisitos para o cliente, controle de qualidade de software, estabelecimento de padrões de programação. E por último mas não menos importante, benefícios comerciais dizem respeito à redução de manu-tenção e custos de teste (graças à reutilização entre vários produtos semelhantes). Além disso, as linhas de produtos geram uma melhor eficiência nos processos e a possibilidade de aumentar o orçamento e melhorar o planejamento do tempo por ter maior controle dos componentes que fazem parte do produto final. Uma descrição detalhada dos benefícios pode ser encontrada na seção "benefícios e custos da linha de produtos"em [6]. Várias estatísticas concretas são apresentados para ilustrar as melhorias de custos, tempo de mercado e produtividade [15]. 12 1. Nokia é capaz de produzir 25-30 modelos diferentes de celular por ano por causa da abordagem de linha de produtos. 2. Cummins, Inc., foi capaz de reduzir o tempo que leva para produzir o software de um motor diesel de cerca de um ano para cerca de uma semana. 3. Motorola observou uma melhoria de produtividade de 400% em uma família de determinado tipo de celular. 4. Hewlett-Packard reportou uma redução no time to market por um fator de sete e um aumento na produtividade por um fator de seis, em uma família de sistemas de impressoras. 1.2.5. Desvantagens em Linhas de Produtos de Software Realizar uma mudança de um modo original de desenvolvimento de software em uma organização do ponto de vista de um produto único para uma abordagem com linha de produtos envolve uma mudança fundamental para a organização e para as mentalidades que pode trazer o que chamamos de resistência a mudanças. Outro problema está relacionado ao fato de poucos engenheiros de software terem uma visão global sobre toda a arquitetura de linha de produtos. Nos estudos de caso apresentados em várias partes do livro [11], ele enfatiza a necessidade de um especialista na linha de produtos que sabe perfeitamente como funciona o domínio da aplicação, que tem responsabilidade suficiente, autoridade, experiência dentro da empresa, que tem motivação e compreensão sobre teorias em linhas de produtos de software. Algumas dificuldades podem aparecer, quando uma decisão deve ser tomada para mesclar ou não um novo produto com a linha de produtos. A evolução do domínio requer cautela para fixar o alcance da linha de produtos. De fato, um escopo muito amplo torna a manutenção de ativos base demasiadamente complexa para haver reutilização de forma eficaz, e um escopo muito limitado não justifica o custo de desenvolvimento e manutenção do núcleo dos ativos base. Projetar arquiteturas de linha de produto muitas vezes se torna difícil, devido à falta de diretrizes, representações maduras de como validar arquiteturas de referência e ferramentas integradas para desenvolver e explorar as linhas de produtos de software. Dado as dificuldades apresentadas, Sholom [11] lista alguns problemas diferentes com base em um questionário, ressaltando que a resistência organizacional e de investimento são os mais críticos dentre todos os problemas (Tabela 1.1). Quando comparamos o desenvolvimento tradicional de software com o desenvolvimento a partir de linhas de produtos (Figure 1.2), podemos salientar que neste último precisa-se de uma gestão a longo prazo, porque os benefícios visíveis não são imediatos. De fato, nos primeiros estágios em LPS é necessário um investimento inicial considerável onde o ponto de equilíbrio de investimento/retorno seria alcançado geralmente a médio prazo. Um número suficiente de produtos devem ser desenvolvidos a fim de apreciar as vantagens reais . 13 Tabela 1.1. Riscos na adoção de LPS [11] Risco Porcentagem Resistencia organizacional 52% Resistência da gestão 36% Resistência dos desenvolvedores 32% Preocupações com grandes investimentos 45% Falta de pessoal devidamente treinado 29% Incapacidade de medir o impacto 19% Preocupação com o tempo de um projeto 18% Figura 1.2. Comparação de custos entre abordagem tradicional e LPS [11] 1.3. VARIABILIDADE O conceito de variabilidade está relacionado às possibilidades de mudar ou personalizar um sistema. A Figura 1.3 ilustra como a variabilidade de um sistema de software é limitada durante todo o projeto de construção do mesmo. No início a quantidade de sistemas possíveis é grande pois as restrições são mínimas. Durante a execução do projeto na construção do sistema as possibilidades diminuem, até que finalmente em tempo de execução existe exatamente um sistema apenas. Em cada etapa do desenvolvimento, decisões de design são feitas. Cada decisão restringe o número de possíveis sistemas Com a abordagem de LPS as variabilidades são modeladas na arquitectura de referência da LPS (através da representação dos Pontos de Variabilidade e Variantes respectivos) e resolvidas antes da instalação dos produtos. • Um Ponto de Variabilidade corresponde a um aspecto de variação funcional num elemento de software base. O ponto de variabilidade define o conjunto de possíveis variantes, o mecanismo de variabilidade a utilizar para os instanciar e o tempo de ativação dos variantes, e.g. instanciação da arquitectura de software do produto, tempo de compilação, execução. 14 • Um Variante corresponde a uma opção do conjunto de possíveis instâncias de variação que um ponto de variabilidade poderá originar. Figura 1.3. Quantidade de produtos possíveis Em geral há sempre um certo grau de variabilidade difícil de manter em LPS. Reusabilidade e flexibilidade têm sido a força motriz por trás do desenvolvimento de técnicas como orientação a objetos, frameworks orientados a objeto e LPS. Os Pontos de Variabilidade podem ser apresentados em vários níveis de abstração [4]: • Descrição da Arquitetura. Tipicamente, o sistema é descrito usando uma combinação de documentos de alto nível de design, linguagens de descrição de arquitetura e documentação textual. • Documentação por Diagramas. A este nível o sistema pode ser descrito usando várias notações UML. • Código-Fonte. A este nível, uma descrição completa na forma de código-fonte é criado. • Código Compilado. O código fonte é convertido para o código compilado usando um compilador. Os resultados desta compilação pode ser influenciada por meio de directivas de pré-processamento para então o resultaodo ser combinado. • Código Ligado. Durante a fase de ligação, os resultados da fase de compilação são combinados. Isto pode ser feito estaticamente (em tempo de compilação) ou dinamicamente (em tempo de execução). 15 • Código de Execução. Durante a execução, o sistema já construído é iniciado e configurado. Ao contrário das representações anteriores, o sistema de execução é dinâmico e muda o tempo todo. 1.3.1. Gerenciando Variabilidades Ao desenvolver uma LPS, o objetivo final é torná-la flexível o suficiente para atender às novas exigências. Os pontos de variabilidade mais importantes precisam ser identificados antecipadamente, a fim de alcançar este objetivo. Acontece que muitas vezes é muito difícil de adaptar uma arquitetura existente para suportar um certo ponto de variabilidade. Segundo Muhammad [4] a gestão da variabilidade consiste nas seguintes tarefas: • Identificando Variabilidades. Na fase inicial de desenvolvimento de uma LPS, os desenvolvedores são confrontadas com uma série de requisitos. Eles devem de alguma forma trabalhar sobre estes requisitos formando uma especificação mais adequada para a LPS. O objetivo deste processo não é chegar a um especificação completa da LPS, mas sim identificar a diferença entre os produtos (ou seja, onde as "coisas"tendem a variar) e quais características são compartilhadas por todos os produtos. Feature Models apresentam uma excelente forma de representar variabilidades. • Introduzindo a variabilidade no sistema. Uma vez que a variabilidade foi identificada, o sistema deve ser projetado de tal forma que ela possa ser introduzida. Existe uma ampla gama de mecanismos e técnicas para esta tarefa. O mecanismo escolhido depende do nível em que a variabilidade é introduzida, o tempo que o sistema será ligado a um variante particular e a forma como novas variantes (se houver) serão adicionadas ao sistema. • Agrupando as variantes. Esta fase resulta em um conjunto de variantes associadas a um ponto de variabilidade. A coleção de variantes pode ser implícita ou explícita. No caso da implícita o sistema baseia-se no conhecimento dos desenvolvedores ou usuários para escolherem variantes adequadas quando assim for necessário. Uma coleção explícita, por outro lado, implica que o sistema pode, por si só, decidir qual variante usar. A coleção pode ser fechada, o que significa que nenhuma nova variante pode ser adicionada, ou ela pode permanecer aberta para novas adições. • Vinculando o sistema a uma variante. Resulta em um sistema onde um ponto de variabilidade particular é associado a uma de suas variantes. Esta vinculação pode ser feita internamente ou externamente, a partir da perspectiva de sistemas. Uma ligação interna implica que o sistema possui a capacidade de vincular uma variante particular, ao passo que se a ligação é realizada externamente, o sistema tem que contar com outras ferramentas, como ferramentas de gerenciamento de configuração para executar a vinculação. Relacionando esta com o agrupamento de variantes, vemos que a gestão de variabilidade pode ser implícita e externa, implícita e interna, ou explícita e interna. A seleção de qual variante usar envolve escolher uma variante dentre o conjunto de variantes. 16 1.3.2. Feature Model Comunalidade e variabilidade entre produtos de uma linha pode ser expressa em termos de features. O conceito de feature foi originalmente apresentado pelo método Feature Oriented Domain Analysis (FODA). De acordo com este modelo uma feature é uma característica do sistema visível ao usuário final. Uma feature é definida como uma unidade lógica de comportamento que é especificada por um conjunto de requisitos funcionais e de qualidade. Além disso, o comportamento deve ser relevante para um ou vários stakeholders de um produto [7]. O conceito de feature simplifica o trabalho com os requisitos, porque ele pode ser usado para agrupar um conjunto de requisitos relacionados. Em outras palavras, as features são uma forma de abstrair os requisitos. É importante perceber que existe uma relação n-para-n entre features e requisitos. Por esta relação com os requisitos o conceito de feature pode também diminuir a distância em termos de comunicação entre o usuário final e o desenvolvedor. Os usuários podem reportar defeitos ou requisições de uma nova funcionalidade por meio de features e desenvolvedores podem então reinterpretar esta representação transformando-a em ações a serem aplicadas ao ciclo de vida da construção do produto [7]. Deste modo, á medida que reunimos em um gráfico, as features de um sistema nós construímos um feature model. O feature model procura apresentar uma visão geral de alto nível das principais características comuns e variáveis de uma linha de produtos. 1.3.3. Notação F.O.D.A O método Feature Oriented Domain Analysis (FODA) foi desenvolvido no Software Engineering Institute para representar graficametne o feature model. Sua descrição detalhada do processo de Análise de Domínio o fez tornar-se um dos métodos mais populares na década de 1990. Em particular, FODA fez uma contribuição significativa para a popularidade atual da análise orientada a modelo: o uso de várias visões complementares de um domínio para transmitir informações mais completas sobre ele [14]. A modelagem de features de maneira explícita é a chave para o método FODA, e é base para muitos dos trabalhos posteriores. FODA se posiciona como parte integrante da engenharia de domínio a fim de facilitar a engenharia de aplicação. O feature model foi introduzido como parte do método FODA, e representa uma hierarquia de propriedades de conceitos do domínio. É uma das técnicas mais bem sucedidas para facilitar a reutilização de artefactos de software. O feature model é uma representação hierárquica, que visa captar os relacionamentos estruturais entre as features de um domínio de aplicação. O modelo também representa as features comuns e variáveis de instâncias de conceitos (por exemplo, sistemas de software) e dependências entre as features variáveis. Um feature model consiste em um diagrama composto de features e alguma informação adicional, tais como descrições semânticas de cada feature, pontos variáveis, prioridades e regras de dependência. No contexto das linhas de produto de software, um feature model representa a própria linha de produtos. Uma feature pode ser de um dos seguintes tipos: 17 • Obrigatória: A feature tem de estar presente em todos os membros da linha de produtos. • Opcional: A feature pode ou não estar presente em um membro da linha de produtos. • Alternativa: É uma feature que é composta de um conjunto de features das quais se escolhe uma ou mais, devendo-se indicar se é necessário escolher apenas uma ou se se pode escolher mais que uma. Nestas features é necessário fazer a distinção de alternativas OR, que permite mais do que uma feature, e XOR, que mostra a exclusão mútua. Uma feature obrigatória é representada por uma aresta terminada por um círculo preenchido a preto. Uma feature opcional é representada por uma aresta terminada por um círculo vazio. As features alternativas são representadas por arestas que estão ligadas e conectadas por um arco. Se o arco for vazio deve-se escolher apenas uma das alternativas (XOR), se for preenchido é permitido escolher mais de uma alternativa (OR) [23]. Czarnecki [13] propõe colocar cardinalidade nas features para poder remover ambiguidades e representar a informação mais facilmente, sendo as diferentes cardinalidades assim definidas: • 0..1 Pode-se escolher uma ou nenhuma feature do conjunto de sub-features. • 1 Exatamente uma feature tem de ser escolhida de um conjunto de sub-features. • 0..* Um número arbitrário de features (ou nenhuma) tem de ser selecionado do conjunto de sub-features. • 1..* Pelo menos uma feature tem de ser seleccionada do conjunto de sub-features. Figura 1.4. e-Shop [22] A Figura 1.4 mostra uma possível representação para o modelo de features do Sistema e-Shop [22]. 18 Neste modelo são features obrigatórias Catalog, Payment, GUI e Info, são features opcionais Security, Banners, Offers, Search. A feature Security possui um grupo de subfeatures numa alternativa XOR que indica que apenas uma delas é escolhida para a aplicação. No caso da feature Payment, esta é composta por duas sub-features numa alternativa OR (uma ou as duas podem ser escolhidas). Note que uma feature filha pode aparecer apenas em um produto se sua feature superior aparecer. A feature raiz é parte de todos os produtos dentro da LPS. Adicionalmente aos relacionamentos parental entre features, pode ocorrer também o que chamamos de cross-tree entre features, que significa que uma feature pode estar relacionada a outra só que não em uma relação de parentesco, podendo ser dos seguintes dois tipos: • Requires. Se uma feature A requer a feature B, a inclusão de A em um produto implica na inclusão também de B. No sistema de exemplo pagamento com cartão de crédito deve implementar uma política de segurança alta. • Excludes. Se a feature A exclui a feature B, ambas as features não podem fazer parte do mesmo produto. No sistema de exemplo o produto que implementar uma interface GUI mobile não deve incluir suporte a banners. 1.3.4. Configuration Knowledge O feature model, por si só, apenas representa a modelagem do domínio, mas não representa o modo como os produtos deste domínio serão gerados a partir dos artefatos da linha de produtos. O mapeamento entre o feature model e os artefatos de implementação é o que se chama de Configuration Knowledge [12]. Os artefatos de implementação podem estar modelados direto no modelo que representa o configuration knowledge ou em um modelo a parte, neste caso o configuration knowledge associará os artefatos de um modelo com as features do feature model. Quando não estão dentro do configuration knowledge, os artefatos de implementação encontram-se em um modelo que assume diferentes nomes, dependendo da ferramenta utilizada, tais como family model, component model, architecture model, etc. O mapeamento existente no configuration knowledge é essencialmente um conjunto de regras que definem que artefatos de implementação (classes, arquivos de recursos, etc) entram em cada produto da linha. Tomando o E-Shop 1.4 como exemplo em um ambiente de desenvolvimento orientado a objetos, uma classe chamada Catalog (que trata dos produtos disponíveis na loja) estaria representada no configuration knowledge como uma regra que diz se a feature Catalog estiver selecionada em um produto, a classe entraria nesse produto também, essencialmente uma relação de implica (feature implica em artefato de implementação). Por trás do processo de geração de produtos de uma linha de produtos de software se encontra uma engine de resolução de expressões lógica, que verifica se todas as restrições presentes no feature model foram respeitadas em cada instância, e, para cada seleção de features, verifica que artefatos de implementação estarão habilitados na instância. Uma vez que todas as verificações tenham sido realizadas, a saída do processo de 19 geração de produtos vai ter como resultado, para cada produto, uma lista dos artefatos de implementação habilitados no produto. Dependendo da ferramenta utilizada, o usuário pode especificar diretamente na ferramenta o que será feito com essa lista, como gerar um projeto individual na IDE utilizada com uma cópia de cada artefato de implementação, ou invocar um sistema de empacotamento dos produtos que geraria os executáveis finais, permitindo assim uma maior customização do processo de geração. 1.4. ENGENHARIA DE LINHA DE PRODUTOS DE SOFTWARE 1.4.1. Atividades Essenciais em Linha de Produtos de Software Linhas de Produto de Software combinam três atividades essenciais e altamente interativas que se misturam práticas de negócios e tecnologia. Em primeiro lugar, atividade de Core Asset Development onde o objetivo não é criar o produto final imediatamente e sim visa o desenvolvimento de ativos a serem reutilizados em outras atividades posteriores. Em segundo lugar, vem a atividade denominada Product Development que parte dos ativos base já desenvolvidos anteriormente reusando-os. Finalmente Management Activity, que inclui gestão técnica e organizacional [16]. A Figura 1.5 mostra esta tríade de atividades essenciais. Cada círculo giratório representa uma das atividades essenciais. Todos os três são ligados entre si e em movimento perpétuo, mostrando que todos os três são essenciais e estão intimamente ligados, podendo ocorrer em qualquer ordem, e são altamente iterativos [17]. Figura 1.5. Atividades Essenciais [17] 20 1.4.1.1. Desenvolvimento de Ativos Base Core Asset Development é uma atividade na forma de ciclo de vida que resulta em ativos base que em conjunto compõem a plataforma da linha de produto [16]. O objetivo desta atividade é definir os aspectos comuns e a variabilidade da linha de produtos, e, portanto, obter artefatos reutilizáveis para em seguida possuir uma capacidade de produção maior [21]. A Figura 1.6 mostra o núcleo desta atividade juntamente com suas saídas e insumos necessários. Figura 1.6. Desenvolvimento de Ativos Base [17] As setas rotatórias na Figura 1.6 sugerem que não há um momento certo de adicionar uma restrição ou novos padrões no desenvolvimento e estas entradas afetam diretamente as saídas no processo. Em alguns contextos os produtos existentes são a base para os ativos base em outros estes podem ser desenvolvidos do zero para futuro reuso. Ativos base incluem, mas não estão limitados à arquitetura e sua documentação, especificações, componentes de software, ferramentas como geradores de componentes ou aplicação, modelos de desempenho, cronogramas, orçamentos, planos de teste, casos de teste, planos de trabalho e processo descrições [17]. Embora possa ser possível a criação de ativos base que podem ser utilizados em todos os produtos sem quaisquer adaptações, em muitos casos, algumas adaptações são necessárias para torná-los mais utilizáveis no contexto mais amplo de uma linha de produtos. Logo, mecanismos de variação dos principais ativos base utilizados ajudam a controlar as adaptações necessárias e suportar as diferenças entre os produtos de software [5]. Essas adaptações devem ser planejadas antes do desenvolvimento e facilitada para a equipe de desenvolvimento do produto sem colocar em risco as propriedades existentes dos ativos base. 21 1.4.1.2. Desenvolvimento de Produtos Na atividade de desenvolvimento de produto, os produtos são desenvolvidos a partir dos ativos base, com base no plano de produção, para satisfazer as exigências da linha de produtos de software. Os insumos essenciais da atividade de desenvolvimento de produto são requisitos, escopo da linha de produtos, ativos base e o plano de produção [3]. De posse do plano de produção, que detalha como os ativos base serão utilizados para construir um produto, o engenheiro de software pode montar as partes da linha de produtos. A Figura 1.7 ilustra o produto atividade de desenvolvimento, juntamente com suas saídas e fatores contextuais. Figura 1.7. Desenvolvimento de Produtos [17] Como na Figura 1.5, as setas de rotação na Figura 1.7 indicam iterações entre as partes envovidas. Por exemplo, a existência e a disponibilidade de um determinado produto pode assim afetar os requisitos de produtos subseqüentes. Como outro exemplo de construção, um produto que tem em comunalidades previamente não reconhecidas em relação a outro produto da linha vai criar a necessidade de para atualização dos ativos base e fornecer uma base para explorar essa comunilidade em futuros produtos [17]. Além disso, esta atividade tem a obrigação de dar feedback sobre quaisquer problemas ou deficiências encontradas com os ativos base. 1.4.1.3. Atividade de Gestão A atividade de Management desempenha um papel vital no sucesso da institucionalização da linha dentro de uma organização porque fornece e coordena a sua infra-estrutura necessária. Esta tarefa envolve atividades essenciais realizadas a nível técnico e organizacionais para apoiar o ciclo de vida do processo [3]. A gestão supervisiona a construção dos ativos base e atividades de desenvolvimento do produto, garantindo que os grupos que constroem os ativos base e os grupos que 22 constroem os produtos estão plenamente envolvidos nas atividades individuais, acompanhando o processo definido para a linha de produtos acompanhando seu progresso [17]. Isto representa não apenas os aspectos técnicos mas também aspectos gerenciais e organizacionais. O conjunto de ativos base e plano de como eles são usados para construir os produtos não nascem sem previamente estudar o ambiente, caracterizar o negócio, portanto deve existir investimento organizacional. A gestão deve dirigir, controlar e garantir a plena utilização dos ativos. Linhas de produtos de software está mais relacionado a práticas de negócios do que práticas técnicas [17]. Embora as organizações sejam diferentes em termos da natureza de seus produtos, de mercado ou missão, objetivos de negócio, estrutura organizacional, cultura e políticas, as disciplinas de processo de software, e assim por diante, atividades essenciais aqui discutidas aplicam-se em qualquer situação, uma vez que representam o nível mais alto de generalidade, que envolve os aspectos mais importantes sobre o desenvolvimento em LPS. Em geral, as organizações realizam essa divisão de responsabilidades em uma variedade de maneiras. Algumas organizações têm equipes dedicadas a cada função e outros usam as mesmas pessoas para ambas. Depende da disponibilidade orçamentária, estratégia entre outros aspectos. Na verdade, é válido mencionar que não há atividade primária, isto é, em alguns contextos, os produtos já existentes são quebrados em ativos base, enquanto que em outros, os ativos base podem ser desenvolvidos para uso futuro [18]. De acordo com Clements e Northrop [17], em muitos casos, a parte da gestão é a atividade responsável pelo sucesso ou fracasso do produto final de uma linha de produtos. 1.4.2. Abordagens de Construção de Linha de Produtos de Software Segundo Chen [10], existem três abordagens principais de desenvolvimento em linha de produtos de software (ver Figura 1.8): • Proativa: Com esta abordagem os ativos base são desenvolvidos primeiro para futuro produtos. Aqui são considerados todos os produtos a serem gerados previamente fazendo-se um planejamento inicial completo. • Reativa: Nesta abordagem os ativos base já existem, bem como uma versão da linha de produtos, o que acontece é a evolução desta linha realizando-se incrementos na mesma à medida que novos requisitos aparecem. • Extrativa: Com esta abordagem, inicialmente são analizados quais os produtos já existentes e como eles são estruturados de modo a extrair as comunalidades e variabilidades destes para então poder se derivar uma versão inicial da Linha de Produtos. Dependendo do grau de planejamento, a abordagem proativa pode ser classificada em abordagem big bang e abordagem incrementa (ver Figura 1.9). 23 Figura 1.8. Abordagens em LPS Na estratégia big bang, LPS é adotada para os novos produtos de uma só vez, uma mudança drástica. Primeiramente a engenharia de domínio é construída completamente e a plataforma é modelada e desenvolvida. Quando os ativos base estão prontos a engenharia de aplicação inicia e as aplicações são derivadas da plataforma [21]. Com abordagem incremental, os ativos base são gradualmente desenvolvidas para suporte aos futuros produtos. Já a abordagem reativa pode ser quebrada em três subcategories (ver Figura 1.9): Figura 1.9. Subcategorias de abordagens em LPS [10] • baseado em infra-estrutura, 24 • baseado em branch-and-unit, e • baseado em bulk-integration. A abordagem baseada em infra-estrutura não permite que os produtos individuais estejam desatualizados em relação aos ativos base comuns a vários produtos, ou seja, a plataforma deve manter sempre a integridade em relação aos produtos, exigindo que novas características comuns sejam implementadas primeiro nos ativos base que irão compor os produtos, em seguida, construindo os produtos. Tanto o branch-and-unit quanto a abordagem bulk-integration permitem um desalinhamento temporário entre ativos base e os produtos individuais. A diferença entre estas duas sub-abordagens está na quantidade de produtos que se espera ser lançado antes de atualizar os ativos base. A estratégia branch-and-unit exige que novas features comuns sejam reintegradas nos ativos base imediatamente após o lançamento do novo produto, enquanto que bulkintegration permite que novas features comuns serem reintegrados após o lançamento de um grupo de produtos. Estas abordagens não são mutuamente exclusivas. Por exemplo, uma linha de produtos pode ser adotada por meio de um abordagem proativa e, em seguida, evoluir através de uma abordagem reativa. 1.4.3. Ferramentas Nesta seção iremos abordar algumas ferramentas relacionadas basicamente à modelagem de feature models. Aguiar [2] realizou um estudo comparativo sobre as principais ferramentas de apoio a Linha de Produtos de Software e abaixo seguem algumas características descritivas deste trabalho. 1.4.3.1. fmp O fmp (Feature Model Plugin) é um plugin gratuito para o Eclipse desenvolvido pelo Generative Software Development Lab, da University of Waterloo [1]. Ele utiliza como base o Eclipse Modeling Framework, o que, de acordo com os desenvolvedores, reduziu significativamente o esforço de desenvolvimento. O fmp apenas apresenta suporte à modelagem do domínio, através de feature models. Ele não possui nenhuma funcionalidade de configuration knowledge, nem nada que permita, dentro do próprio fmp, o mapeamento necessário entre features e artefatos da linha de produtos para permitir a geração dos produtos. Os modelos gerados pelo feature model são gravados no sistema como um arquivo XML (do inglês Extensible Markup Language), o que pode permitir que geradores, utilizando XSLT (do inglês Extensible Stylesheet Language Transformation), por exemplo, possam processar o modelo. Por ser um plugin para o Eclipse e possuir uma API bem definida, possibilita que outras ferramentas se integrem com o mesmo. 25 Figura 1.10. Interface do fmp O fmp implementa modelagem de features baseada em cardinalidades [13], ou seja, permite a definição de cardinalidades de feature e de grupo, atributos de feature, referências e anotações definidas pelo usuário. Cardinalidades de feature e grupo permitem que sejam especificados o número mínimo e máximo de filhos de uma feature que podem ser selecionados, 0 ou mais, 1 a 4, por exemplo. Atributos de feature permitem que cada feature possua um valor, que pode ser uma string, um booleano, um inteiro, etc, o que aumenta a expressividade do modelo. Anotações definidas pelo usuário permitem que o usuário adicione novas propriedades as features, além das básicas (nome, cardinalidade, valor), permitindo assim uma maior customização do modelo. Para suportar anotações definidas pelo usuário, o fmp permite que o usuário altere o meta-modelo do feature model, permitindo, por exemplo, que toda feature tenha um atributo chamado "quantidade"; ou qualquer coisa que o usuário deseje colocar (Ver Figura 1.10). 1.4.3.2. XFeature O XFeature é um plugin para o Eclipse desenvolvido pela P&P Software, que é uma empresa que se originou no Institute of Automatic Control of ETH (Swiss Federal Institute of Technology). 26 O XFeature foi criado para demonstrar um conceito de uma ferramenta para automatizar o processo de modelagem e configuração de artefatos reusáveis de software. A ferramenta se apresenta como inovadora pela possibilidade de customização do metamodelo da família de produtos [9]. O XFeature tem suporte à modelagem do domínio, através de feature models e não possui a funcionalidade de configuration knowledge, que permite o mapeamento necessário entre features e artefatos da linha de produtos para permitir a geração dos produtos. Originalmente o XFeature foi desenvolvido visando o seu uso em aplicações espaciais (sistemas de controle embutidos para naves espaciais, por exemplo), porém, não há nenhuma restrição na maneira que foi desenvolvido que não permita o seu uso em outros contextos. Em relação à implementação, o XFeature foi implementado baseado na versão 3.3 do Eclipse, ao contrário do fmp, ele não utilizou o EMF para a definição do meta-modelo de feature model. O seu editor é baseado no GEF (Graphical Editing Framework) do Eclipse. O XFeature depende fortemente de XML e de transformações XSL. Scripts Ant são utilizados para a verificação e geração de arquivos de configuração da aplicação, tais como os que customizam o editor dos modelos. O processo de criação de modelos no XFeature é consideravelmente mais complicado do que nas outras ferramentas. Primeiramente se define qual será a configuração utilizada (FD, FMP, SimplifiedICSR ou ICSR). Em seguida, é criado o que o XFeature chama de family model, que é o meta-modelo do domínio. Após a validação do family model sob a configuração utilizada, o usuário deve clicar em um botão que gera dois meta-modelos: o de aplicação e o de display. O meta-modelo de aplicação é o que será utilizado como configuração dos modelos que vão conter as instâncias da linha de produto, e o meta-modelo de display define quais os elementos visuais que aparecerão no editor. Apesar de ser mais complicado, o XFeature permite que o usuário tenha mais controle sobre como são criados os feature models, uma vez que o usuário não fica preso à única configuração presente nas outras aplicações. Uma vez criados os modelos das instâncias da linha de produtos, eles podem ser validados contra seu meta-modelo, que foi definido pelo usuário anteriormente. Quando os modelos estiverem validados, eles podem ser utilizados como entrada pra outras ferramentas que possuam a parte de configuration knowledge para gerar os produtos finais. A Figura 1.11 demonstra a interface do XFeature. O XFeature só possui uma maneira de visualizar e editar os seus modelos, que é em forma de árvore, como demonstrado na Figura 3, onde encontramos feature raiz, no topo da árvore, features opcionais, pontilhadas, e features obrigatórias, em caixas amarelas, assim como o editor de propriedades de features. O XFeature também permite a definição de restrições globais sobre o feature model. O usuário cria um modelo de restrições, que passa pelo global constraints compiler, que vai gerar um conjunto de arquivos XSL que permitem a verificação das restrições. 27 Figura 1.11. Interface do Xfeature 1.4.3.3. pure::variants O pure::variants é um plugin para o Eclipse desenvolvido pela pure-systems GmbH, uma empresa que se originou no Institute Otto-von-Guericke-Universität Magdeburg e no Fraunhofer Instituts Rechnerarchitektur und Softwaretechnik. A empresa tem como objetivo o desenvolvimento de softwares para sistemas embarcados, que se baseia no desenvolvimento de componentes de software e de ferramentas de desenvolvimento de software. O pure::variants foi desenvolvido para suportar o desenvolvimento e a implantação de linhas de produtos e famílias de software. O pure::variants provê suporte no desenvolvimento durante as atividades de análise, modelagem, implementação e implantação. O pure::variants apresenta suporte à modelagem do domínio, através de feature models e possui a funcionalidade de configuration knowledge, que permite o mapeamento necessário entre features e artefatos da linha de produtos para permitir a geração dos produtos. O pure::variants é uma ferramenta comercial, logo, requer licença para uso. Ela possui uma versão gratuita para testes, que não pode ser usada comercialmente e possui restrições no tamanho dos modelos. É sabido que o custo do pure::variants varia muito dependendo da quantidade de licenças adquiridas. Mas sabe-se que normalmente o preço de uma licença para uma máquina na versão developer custa em torno de 200 euros por mês. O custo também varia dependendo de qual versão da ferramenta será utilizada, a Professional ou a Enterprise. A versão Enterprise possui controle de versões integrado (utilizando CVS, por exemplo), permite que o usuário desfaça modificações no modelo mesmo após ele ter sido fechado (não apenas quando ele está aberto, como ocorre na 28 Professional), colaboração on-line e gerenciamento de modelos centralizado. O pure::variants utiliza uma notação baseada em FODA. Ele possui 3(três) tipos de modelos diferentes: o feature model: onde são definidas as características comuns e variabilidades da linha de produtos, o family model: a parte do configuration knowledge onde se encontram os componentes que compõem os artefatos da linha de produtos, e o variant model: que é o modelo que define uma instância da linha de produtos. A divisão dos 3 modelos é clara e consideravelmente mais intuitiva do que os inúmeros meta-modelos e modelos que o XFeature define, e a definição das instâncias em arquivos separados facilita a organização da linha. Além disso, as mudanças no feature model são sincronizadas automaticamente com os variant models. Uma vez criados os variant models, eles podem ser validados para determinar se cumprem as restrições do feature model, que foi definido pelo usuário anteriormente. Uma vez que estes modelos estejam validados, eles podem ser usados juntamente com o family model e o feature model para a geração dos produtos da linha, utilizando os recursos de geração do pure::variants. A Figura 1.12 apresenta o Family Model, onde encontramos componentes (as caixas marrons), restrições (as expressões após a placa com exclamação), classes (as bolas verdes com um "C"), aspectos (as bolas laranjas com um "A"), arquivos (o papel dobrado na borda). Figura 1.12. Interface de Edição do Family Model do pure::variants 29 1.5. Conclusões Ao longo deste capítulo foram analizados diversos conceitos sobre LPS, propiciando ao leitor uma visão geral sobre o assunto. Além disto foram mostradas características sobre a notação F.O.D.A para construção de feature models bem como o que vem a ser uma Configuration Knowledge, dentre outros conceitos e princípios. Foi visto que as organizações podem obter benefícios de negócio consideráveis em termos de redução de custos, redução do tempo de colocação no mercado, aumento na qualidade do produto, etc, aplicando desenvolvimento de linhas de produto. Há, porém, riscos e custos associados à adopção das empresas para esta adoção que devem ser avaliados antes de embarcar em uma iniciativa de reutilização deste tipo. Organizações têm colocado projetos ad hoc em "stand by"a fim de deixar livre recursos para construir ativos base de uma LPS. Isto implica em grandes custos, no etando existem muitas experiências bem sucedidas deste tipo de empreendimento. Portanto qualquer organização que desenvolve produtos como sistemas simples, que tem bons conhecimentos do domínio do negócio e um nível razoavelmente elevado de maturidade do processo, deve, pelo menos, fazer um estudo de viabilidade para observar se o desenvolvimento de uma LPS pode ser benéfica para eles. Referências [1] GENERATIVE SOFTWARE DEVELOPMENT GROUP. http://www.sei.cmu.edu/plp/ web-page acessada em 14 de outubro de 2011. [2] Rogerio Aguiar. Comparacao entre ferramentas para Linha de Produtos de Software. Escola Politecnica de Pernambuco. Departamento de Sistemas e Computacao. http://dsc.upe.br/ tcc/20081/RogeriomonografiaFinal.pdf Acessado em 14 de outubro de 2011. [3] F. Ahmed, P. Campbell, and M.S. Lagharid. Cognitive factors in software product line engineering. In Computer Modelling and Simulation, 2009. UKSIM ’09. 11th International Conference on, pages 352 –355, march 2009. [4] Muhammad Ali Babar, Lianping Chen, and Forrest Shull. Managing variability in software product lines. IEEE Software, 27:89–91, 94, 2010. [5] Felix Bachmann and Paul C Clements. Variability in software product lines. Software Engineering Institute Pittsburgh USA, (September):46, 2005. [6] Len Bass, Paul Clements, and Rick Kazman. Software Architecture in Practice. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 2 edition, 2003. [7] Jan Bosch. Design and use of software architectures: adopting and evolving a product-line approach. ACM Press/Addison-Wesley Publishing Co., New York, NY, USA, 2000. [8] Stan Buhne, Gary Chastek, Timo Kakola, Peter Knauber, Linda Northrop, and Steffen Thiel. S.: Exploring the context of product line adoption. In In: Proc. 5th Int. Workshop on Product Family Engineering, 2003. 30 [9] V Cechticky, A Pasetti, O Rohlik, and W Schaufelberger. Xml-based feature modelling. Software Reuse Methods Techniques and Tools, pages 101–114, 2004. [10] Y Chen, G C Gannod, and J S Collofello. A software product line process simulator. Software Process: Improvement and Practice, 11(4):385–409, 2006. [11] Sholom Cohen. Product line state of the practice report. Technical report, Software Engineering Institute, Carnegie Mellon University, 2002. [12] Krzysztof Czarnecki and Ulrich W. Eisenecker. Generative programming: methods, tools, and applications. ACM Press/Addison-Wesley Publishing Co., New York, NY, USA, 2000. [13] Eisenecker U. Czarnecki K., Helsen S. Staged configuration using feature models, software product lines. International Conference SPLC, 2004. [14] K Kang. Feature-oriented domain analysis feasibility study. Technical report, SEI Technical Report, 1990. [15] Paul C. Clements Len Baas and Rick Kazman. Software Architecture in Practice. Second Edition. SEI Series in Software Engineering. Addison-Wesley, 2003. [16] Schmid K. Linden, F. J. v. d. and E. Rommes. Software Product Lines in Action:The Best Industrial Practice in Product Line Engineering. Springer-Verlag, 2007. [17] C.A. Long. Software product lines: practices and patterns [book review]. Software, IEEE, 19(4):131 –132, jul/aug 2002. [18] John D. McGregor, Linda M. Northrop, Salah Jarrad, and Klaus Pohl. Guest editors’ introduction: Initiating software product lines. IEEE Softw., 19:24–27, July 2002. [19] Northrop. Software product line adoption roadmap. SEI Technical Note, 2004. [20] SEI PLP. Framework for Product Line Practice. http://www.sei.cmu.edu/plp/, 2003. [21] Klaus Pohl, Günter Böckle, and Frank J. van der Linden. Software Product Line Engineering: Foundations, Principles and Techniques. Springer-Verlag New York, Inc., Secaucus, NJ, USA, 2005. [22] S. Segura, R.M. Hierons, D. Benavides, and Ruiz-Corte. Automated test data generation on the analyses of feature models: A metamorphic testing approach. pages 35 –44, april 2010. [23] Arie van Deursen and Paul Klint. Domain-specific language design requires feature descriptions. Journal of Computing and Information Technology, 10, 2001. 31 Capítulo 2 Chapter 1 Planejamento de capacidade de sistemas através de Cadeias de Markov Rubens Matos, Julian Araújo, Francisco Vieira e Paulo Maciel Resumo Neste capítulo, pretende-se definir formalmente as cadeias de Markov e demonstrar como elas podem ser utilizadas no planejamento de capacidade de sistemas computacionais, auxiliando na garantia de métricas de desempenho e disponibilidade. Serão mostradas ferramentas que facilitam a análise de uma grande variedade de sistemas, dando destaque a avaliação de redes de computadores e alguns tipos de sistemas distribuídos. Serão apresentados exemplos de avaliação de desempenho com métricas que normalmente merecem interesse em diversos ambientes: tempo de resposta, taxa de conclusão de tarefas (throughput) e nível de utilização de recursos. Esses indicadores estão diretamente relacionados à percepção do desempenho do sistema e podem também indicar a necessidade de mudanças, como atualização de componentes ou ajustes na configuração. 2.1. Introdução 1. Em muitas atividades industriais, comerciais e humanas, bem como em fenômenos naturais, está presente um alto grau de incerteza ou risco. Neste sentido, modelos matemáticos probabilísticos têm sido utilizados para fazer previsões de valores para ajudar nas tomadas de decisões. Dentre os modelos probabilísticos, um que se destaca é conhecido como processo de Markov1 que corresponde a um fenômeno constituído de estados finitos e discretos, e cuja probabilidade de transição entre tais estados, num intervalo de tempo também discreto, depende apenas do estado corrente e do estado seguinte. Uma sequência de estados seguindo este processo dá-se o nome de cadeia de Markov [20, 2, 4, 16, 22, 25]. Os modelos Markovianos têm sido usados intensivamente na modelagem de desempenho e dependabilidade desde a década de 50 [9]. As áreas onde os modelos de Markov podem ser aplicados se incluem a Administração, Economia, Meteorologia, Física, Química e as Engenharias. 1 Andrei Andrejevitch Markov (1856-1922) - matemático russo. 32 Markov obteve os primeiros resultados para estes processos em 1906. Uma generalização para espaços de estados infinitos contáveis foi feita por Kolmogorov2 em 1936. Cadeias de Markov estão relacionadas ao movimento Browniano e à hipótese ergódica, dois importantes tópicos da Física nos primeiros anos do século XX, mas a motivação de Markov para o desenvolvimento da teoria parece ter sido estender a teoria dos grandes números a eventos dependentes. Além do desempenho, aspectos de confiabilidade merecem grande atenção para a garantia da qualidade do serviço prestado por um sistema. As cadeias de Markov podem ser utilizadas para capturar o comportamento do sistema e permitir a descrição e previsão de métricas de confiabilidade, como o tempo médio para falha, assim como a disponibilidade e o downtime anuais. A análise conjunta dos aspectos de desempenho e confiabilidade, chamada de análise de performabilidade, também será tratada neste estudo, uma vez que muitos sistemas podem permanecer funcionais mesmo nas falhas. Além disso, serão também apresentados os MRMs (Markov Reward Models) e uma introdução aos modelos de Neste capítulo, pretende-se mostrar como as cadeias de Markov podem ser utilizadas no planejamento de capacidade de sistemas computacionais, auxiliando na garantia de métricas de desempenho e disponibilidade almejadas. Serão apresentadas ferramentas que facilitam a análise de uma grande variedade de sistemas. Como exemplos, serão utilizados estudos de avaliação de desempenho com métricas que normalmente merecem interesse em diversos ambientes, como tempo de resposta, taxa de conclusão de tarefas (throughput) e nível de utilização de recursos. Esses indicadores estão diretamente relacionados à percepção do desempenho do sistema e podem indicar a necessidade de mudanças, tais como upgrade de componentes ou ajustes na configuração. 2.2. Fundamentação teórica 1. Para entender o mecanismo das cadeias de Markov e suas aplicações, é essencial o conhecimento de conceitos fundamentais sobre probabilidade, diagramas de transição, vetor de probabilidade, matriz de transição, cadeia ergódica, cadeia regular e regime estacionário. Estes conceitos serão estudados a seguir. 2.2.1. Noções de probabilidade 1. As noções de experimentação, espaço amostral e eventos são fundamentais para o estudo da teoria da probabilidade. Para este estudo a noção de conjunto é fundamental. Os conjuntos podem ser finitos ou infinitos. Os conjuntos infinitos podem ser enumeráveis ou não-enumeráveis. Um conjunto infinito é enumerável se for possível construir uma bijeção entre ele e o conjunto N dos números naturais. Um experimento probabilístico é alguma ocorrência similar ao lançamento de uma moeda ou de um dado, cujos resultados não são determinísticos. No caso do lançamento de uma moeda, pode aparecer cara (H) ou coroa (T). No caso do lançamento de um dado podem aparecer qualquer valor entre 1 e 6. Espaço amostral. Define-se como espaço amostral, associado a um experimento, e 2 Andrey Nilolaevich Kolmogorov (1903 - 1987) - matemático russo. 33 denota-se por S o conjunto de todos os possíveis resultados deste experimento, podendo ser finito, infinito enumerável ou infinito não enumerável. Qualquer subconjunto do espaço amostral é conhecido como “evento”. No caso do lançamento de uma moeda, S = {H, T }. No caso do lançamento de um dado, S = {1, 2, 3, 4, 5, 6}. Se o experimento for o lançamento de 3 moedas ao mesmo tempo, S = {HHH, T HH, HT H, HHT, T T H, T HT, T T H, T T T }, onde o elemento T T H é diferente do elemento HT T devido à posição das caras e das coroas nos dois elementos. Como um exemplo mais interessante para usuários da computação, pode-se citar o espaço amostral derivado de um experimento que consiste na observação do número de e-mails chegados ao provedor do NPD (Núcleo de Processamento de Dados) da UFPI em cada dia. Este espaço amostral é infinito enumerável porque pode-se rotular cada e-mail que chega com um único número natural, n ∈ N, ou seja, S = {n | n ∈ N}. Outro exemplo interessante consiste na medida dos tempos de espera em um ponto de ônibus. Neste caso, S é infinito e cada resultado é um número real não negativo, ou seja, S = {t | t ≥ 0}. A definição de eventos como um subconjuntos de um espaço amostral S, permite que as operações e propriedades típicas sobre conjuntos também possam ser a eles aplicadas. Entre estas operações e propriedades se incluem a união, interseção e complementação, as leis da comutatividade, da associatividade, da distribuição, da identidade, da idempotência, da dominação, da absorção e a lei de de Morgan e estas propriedades podem ser verificadas graficamente através dos diagramas de Venn. Além disso, elas podem ser extendidas para qualquer número de eventos [25]. Cálculo de probabilidades. A forma clássica de se calcular a probabilidade de um evento A, que será denotada por P(A), com m elementos em um espaço amostral finito S = {a1 , a2 , · · · an }, onde os n pontos amostrais ai (i = 1, 2, · · · , n) devem ter a mesma probabilidade de ocorrer, ou seja, eles são equiprováveis, é dada pela relação entre a quantidade de casos favoráveis ao evento A e a quantidade de elementos do espaço amostral S, apresentada como um número real ou em percentagem. Ou seja, P(A) = mn . Exemplo. Ao se retirar aleatoriamente uma carta de um baralho com 52 cartas, todas elas 1 têm a mesma probabilidade de ser a carta escolhida. Neste caso, a probabilidade será 52 . 4 No entanto, a probabilidade de que esta carta seja uma dama é 52 , uma vez que existem 4 damas em um baralho. Eventos exclusivos. Dois eventos A e B são ditos mutuamente exclusivos ou disjuntos se a interseção entre eles for vazia, ou seja, se A ∩ B = 0. / Se os eventos A e B forem mutuamente exclusivos, então não é possível que ambos ocorram no mesmo experimento. Diz-se que os eventos de uma lista são mutuamente exclusivos se todas as interseções entre eles resultarem no evento nulo. Eventos independentes. Diz-se que dois eventos são estatisticamente independentes se a ocorrência de um deles não afetar a ocorrência do outro. Desta forma, dois lançamentos seguidos de um mesmo dado constituem dois eventos independentes porque o resultado do primeiro lançamento não tem qualquer influência sobre o resultado do segundo, e viceversa. 34 Multiplicação de probabilidades. Sendo Ai eventos independentes, a probabilidade da ocorrência conjunta é definida pela regra da multiplicação P(A1 .A2 . · · · An ) = P(A1 ∩ A2 ∩ · · · ∩ An ) = P(A1 ).P(A2 ). · · · .P(An ) Exemplo. No lançamento de duas moedas, a probabilidade de se obter duas caras é 1 4 = 0, 25 ou 25%. Este mesmo resultado pode ser obtido utilizando o fato dos dois lançamentos serem independentes fazendo a multiplicação das probabilidades, ou seja, P{HH} = P(H).P(H) = 21 . 21 = 14 . Probabilidade condicionada. Se a condição de independência não for satisfeita, é necessário usar uma fórmula mais geral, que envolve as probabilidades condicionadas. Neste caso, sendo dados dois eventos A e B, a probabilidade de que o evento B ocorra, mas com a informação adicional de que o evento A já ocorreu, é denotada por P(B | A), que se lê: “probabilidade de B dado que A já ocorreu”. Exemplo. Seja a retirada ao acaso de uma carta em um baralho de 52 cartas e sejam os eventos A = A carta é de paus e B = A carta é um 10. A probabilidade da carta 1 . Porém, a probabilidade da carta ser um 10, mas selecionada ser um 10 de paus é 52 já sabendo antecipadamente que o naipe é paus, reduz o espaço amostral que passa a ter somente 13 elementos, em vez de 52. Isto significa que a probabilidade com esta 1 inflormação é 13 . De um modo geral, dados dois eventos não independentes A e B, a probabilidade condicionada de A dado que B já aconteceu é dada por P(A | B) = P(A.B) P(A ∩ B) = P(B) P(B) Axiomas da probabilidade. Seja S um espaço amostral de um experimento randômico. Se o evento A consistir do único ponto da amostra, s, então P(A) = P(s). A função de probabilidade P deve satisfazer aos seguintes axiomas devidos a Kolmogorov. A1 : 0 ≤ P(A) ≤ 1, para qualquer evento A ⊆ S. A2 : P(S) = 1. A3 : P(A1 ∪ A2 ∪ A3 ∪ · · · ) = P(A1 ) + P(A2 ) + P(A3 ) + · · · desde que todos os eventos Ai sejam mutuamente exclusivos. Exercícios resolvidos. 1. Para qualquer evento A, P(A) = 1 − P(A). Prova: Como A e A são eventos mutuamente exclusivos, então S = A ∪ A. Pelo axioma A2, P(S) = 1 = P(A) + P(A), donde P(A) = 1 − P(A). 2. Sendo 0/ o evento impossível, então P(0) / = 0. Prova: Sabe-se que S = 0. / Logo, P(S) = 1 − P(S) = 1 − 1 = 0, pelo axioma A2. 35 Variáveis aleatórias. Dado um experimento e um espaço amostral S a ele associado, denomina-se variável aleatória à função X que associa a cada elemento s de S um número real X(s). Como pode ser notado, a definição de uma variável aleatória é a mesma definição de função, o que pode gerar alguma dúvida, notadamente aos iniciantes neste estudo. Seja X uma variável aleatória. Se os valores de X formarem um conjunto finito ou infinito enumerável, então X é denominada variável aleatória discreta. Se, no entanto, este conjunto for um intervalo ou uma coleção de intervalos, ela é denominada variável aleatória contínua. Função de probabilidade. Seja X uma variável aleatória discreta. Sejam x1 , x2 , x3 , · · · , seus possíveis valores. A cada resultado xi associamos um número real p(xi ) = P(X = xi ), que é a probabilidade do ponto xi , de forma que: 1. p(xi ) ≥ 0, para todo xi e 2. ∑∞ i=1 p(xi ) = 1 Esta função é denominada função de probabilidade da variável aleatória X. Lei da probabilidade total. Sendo A e B dois eventos, sabe-se que a interseção de A com o espaço amostral S é igual a A e que a interseção de B com seu complemento BC é o conjunto vazio, ou seja, A = A ∩ S e B ∪ BC = S substituindo a segunda equação na primeira e aplicando-se o Teorema de de Morgan, encontra-se A = A ∩ (B ∪ BC ) = (A ∩ B) ∪ (A ∩ BC ) No entanto, os eventos A ∩ B e A ∩ BC são mutuamente exclusivos e isto pode ser verificado através dos diagramas de Venn para dois conjuntos A e B. Desta forma, pode-se utilizar o axioma A3 das probabilidades para encontrar P(A) = P(A ∩ B) + P(A ∩ BC ) Esta equação além de facilitar os cálculos de probabilidade em muitas situações pode ainda ser utilizada para qualquer partição de eventos que sejam mutuamente exclusivos. Neste caso, sendo dados n eventos mutuamente exclusivos, Bi , i = 1, 2, · · · , n de um espaço amostral S e um evento qualquer A, a equação generalizada é dada por n P(A) = ∑ P(A ∩ Bi ), n ≥ 1 i=1 que é conhecida como a lei da probabilidade total. Esta equação normalmente é apresentada sob a forma de probabilidades condicionais, ou seja, n n P(A) = ∑ P(A ∩ Bi ) = ∑ P(A | Bi )P(Bi ) i=1 i=1 36 Distribuição das probabilidades. Uma distribuição de probabilidades é um modelo matemático que relaciona o valor da variável aleatória com a probabilidade de ocorrência desse valor no espaço amostral ou na população. Estes pontos, na forma (xi , p(xi )) ∈ S ×R, podem ser plotados em um gráfico para se verificar a forma como eles se distribuem ao londo do espaço amostral S. Esta distribuição pode ser discreta ou contínua. Distribuição discreta de probabilidades. Quando o parâmetro que está sendo medido só pode assumir certos valores, como por exemplo valores inteiros 0, 1, 2, . . . , a distribuição de probabilidades é chamada discreta. Por exemplo, o lançamento de um dado só pode apresentar os resultados 1, 2, 3, 4, 5 e 6. Portanto a distribuição de suas probabilidades é discreta. Exemplo. Seja encontrar a distribuição discreta das probabilidades da variável aleatória discreta que consiste no lançamento de três moedas. Neste caso o espaço amostral S = {HHH, HHT, HT H, T HH, HT T, T HT, T T H, T T T } e X = {0, 1, 2, 3} (o número de caras). Ou seja, X = 0 ⇒ {T T T } X = 1 ⇒ {HT T, T HT, T T H} X = 2 ⇒ {HHT, HT H, T HH} X = 3 ⇒ {HHH} Desta forma, pode-se construir uma tabela como xi p(xi ) 0 1 2 3 1 8 3 8 3 8 1 8 e plotar os pontos (xi , p(xi )) em um gráfico para se ter a noção da forma como a distribuição se comporta. Isto pode ser visto na Figura 2.1. p(x i) 3/8 1/8 0 1 2 3 xi Figure 2.1. Gráfico de uma distribuição discreta de probabilidades. 1. Distribuição contínua de probabilidades. Quando a variável que está sendo analisada tem valores em intervalos reais, a distribuição das probabilidades destes valores é contínua. Uma função matemática cujos valores sejam as probabilidades em cada ponto do intervalo real, é chamada de função densidade de probabilidade, normalmente denotada por fdp. A área sob a curva que expressa a fdp é igual a 1 que é o valor total das probabilidades. Esta forma pode ser verificada no gráfico da Figura 2.2. A definição da função que dá a distribuição de probabilidades é feita encontrandose a função que passe pelos pontos (xi , p(xi ). Esta tarefa nem sempre á fácil de ser realizada. Felizmente existem alguns modelos de distribuição de probabilidades que podem 37 p 01 p 0,8 p 00 0,15 0,2 11 1 0 0 1 0,7 p 10 p p 21 20 p 02 0,3 0,5 0,1 0,05 p 12 2 [ 0,8 0,15 P = 0,7 0,2 0,5 0,3 2 p 0,2 a) b) 22 0,05 0,1 0,2 ] c) Figure 2.3. Diagrama de transições. 1 Uma cadeia de Markov é dita homogênea se para todos os estados i e j P{Xn+1 = j | Xn = i} = P{Xn+m+1 = j | Xn+1 = i} para n = 0, 1, 2, · · · e m ≥ 0. A probabilidade de ir para o estado j no passo n + 1 e para o estado k no passo n + 2, dado que a cadeia está no estado i no passo n é dada por P{Xn+2 = k, Xn+1 = j | Xn = i} = P{Xn+2 = k, Xn+1 = j, Xn = i}P{Xn+1 = j | Xn = i} = P{Xn+2 = k, Xn+1 = j}P{Xn+1 = j | Xn = i} = p jk (n + 1)pi j (n) onde foram usadas inicialmente as propriedades da probabilidade condicional e depois a propriedade da cadeia de Markov. Uma sequência de estados visitados por uma cadeia é chamada de caminho da amostra. Assim, p jk (n + 1)pi j (n) é a probabilidade do caminho da amostra i, j, k que inicia no estado i no passo de tempo n. Exemplo (uma cadeia de Markov não homogênea). Seja a cadeia discreta de Markov {Xn , n = 1, 2, · · · } com apenas os estados a e b. No passo n a probabilidade de que a cadeia permaneça em seu estado atual é dada por paa (n) = pbb (n) = 1/n, enquanto a probabilidade de que ela mude de estado é pab (n) = pba (n) = (n − 1)/n, conforme pode ser visualizado na Figura 2.4. (n-1)/n 1/n a 1/n b (n-1)/n Figure 2.4. Uma cadeia de Markov de dois estados. 1 A matriz de transições é dada por 1/n (n − 1)/n P(n) = (n − 1)/n 1/n e as primeiras quatro matrizes de transições são dadas por 1 0 1/2 1/2 1/3 2/3 1/4 3/4 P(1) = , P(2) = , P(3) = , P(4) = 0 1 1/2 1/2 2/3 1/3 3/4 1/4 38 As cadeias de Markov são casos particulares de processos estocásticos Markovianos, em que o número de estados possíveis é finito ou infinito enumerável. Na realidade, elas são tipos mais simples de processos estocásticos que possuem propriedades importantes quando o número de transições cresce. As propriedades a seguir são válidas para as cadeias de Markov. 1. Pi j ≥ 0 para quaisquer estados i e j. 2. Pi j ≤ 1 para quaisquer estados i e j. 3. ∑∞j=0 Pi j = 1 para todo i = 0, 1, 2, · · · Matriz de transição. Para efeito deste estudo, as probabilidades de transições dos estados Xn = i para os estados Xn+1 = j são conhecidas como probabilidades de transições de passo único ou apenas probabilidades de transições das cadeias de Markov, ou seja, pi j = P{Xn+1 = j | Xn = i}, ∀n A matriz P formada pelos elementos pi j da linha i e da coluna j, para todos os i e j, é chamada de matriz de probabilidades de transições ou apenas matriz de transições. Assim temos P(n) = p00 (n) p01 (n) p02 (n) p10 (n) p11 (n) p12 (n) p20 (n) p21 (n) p22 (n) .. .. .. . . . pi0 (n) pi1 (n) pi2 (n) .. .. .. . . . ··· ··· ··· .. . ··· .. . p0 j (n) · · · p1 j (n) · · · p2 j (n) · · · .. .. . . pi j (n) · · · .. .. . . Uma maneira simples de visualizar uma cadeia de Markov é através de uma máquina de estados finitos ou por meio de um grafo dirigido, onde cada aresta é rotulada com as probabilidades de transição de um estado para o outro, sendo estes estados representados pelos nós, conectados pelas arestas. Vamos introduzir esta idéia através de um exemplo. Exemplo. Considere uma cadeia de Markov que descreve as mudanças climáticas diárias de uma dada localidade. Vamos considerar apenas três tipos de padrão de tempo: chuvoso (0), nublado (1) e ensolarado (2), onde o tempo é observado diariamente. Em um determinado dia chuvoso, a probabilidade de que vai chover no dia seguinte é estimada em 0, 8 e a probabilidade de que será nublado é de 0, 15, enquanto a probabilidade de que o dia seguinte seja ensolarado é apenas 0, 05. Se um dia está nublado, a probabilidade de que o dia seguinte também seja nublado é 0, 2, de que seja chuvoso é 0, 7, e de que seja ensolarado é 0, 1. Se um dia é ensolarado, a probabilidade de que o dia seguinte seja também ensolarado é 0, 2, de que o dia seguinte seja nublado é 0, 3 e de que o dia seguinte seja chuvoso é 0, 5. Graficamente, isto pode ser observado na Figura 2.3, onde também está mostrada a matriz de transições. 39 p 01 p 0,8 p 00 0,15 0,2 11 1 0 0 1 0,7 p 10 p p 21 20 p 02 0,3 0,5 0,1 0,05 p 12 2 [ 0,8 0,15 P = 0,7 0,2 0,5 0,3 2 p 0,2 a) b) 22 0,05 0,1 0,2 ] c) Figure 2.3. Diagrama de transições. 1 Uma cadeia de Markov é dita homogênea se para todos os estados i e j P{Xn+1 = j | Xn = i} = P{Xn+m+1 = j | Xn+1 = i} para n = 0, 1, 2, · · · e m ≥ 0. A probabilidade de ir para o estado j no passo n + 1 e para o estado k no passo n + 2, dado que a cadeia está no estado i no passo n é dada por P{Xn+2 = k, Xn+1 = j | Xn = i} = P{Xn+2 = k, Xn+1 = j, Xn = i}P{Xn+1 = j | Xn = i} = P{Xn+2 = k, Xn+1 = j}P{Xn+1 = j | Xn = i} = p jk (n + 1)pi j (n) onde foram usadas inicialmente as propriedades da probabilidade condicional e depois a propriedade da cadeia de Markov. Uma sequência de estados visitados por uma cadeia é chamada de caminho da amostra. Assim, p jk (n + 1)pi j (n) é a probabilidade do caminho da amostra i, j, k que inicia no estado i no passo de tempo n. Exemplo (uma cadeia de Markov não homogênea). Seja a cadeia discreta de Markov {Xn , n = 1, 2, · · · } com apenas os estados a e b. No passo n a probabilidade de que a cadeia permaneça em seu estado atual é dada por paa (n) = pbb (n) = 1/n, enquanto a probabilidade de que ela mude de estado é pab (n) = pba (n) = (n − 1)/n, conforme pode ser visualizado na Figura 2.4. (n-1)/n 1/n a 1/n b (n-1)/n Figure 2.4. Uma cadeia de Markov de dois estados. 1 A matriz de transições é dada por 1/n (n − 1)/n P(n) = (n − 1)/n 1/n e as primeiras quatro matrizes de transições são dadas por 1 0 1/2 1/2 1/3 2/3 1/4 3/4 P(1) = , P(2) = , P(3) = , P(4) = 0 1 1/2 1/2 2/3 1/3 3/4 1/4 40 Como pode ser observado pelas matrizes de transições, as probabilidades de que a cadeia mude de estado vai aumentando, enquanto as probabilidades de que ela permaneça nos estados anteriores vai diminuindo a medida que o tempo passa. Pode-se observar o comportamento markoviano, onde cada matriz depende apenas do estado anterior e não de outros estados. A probabilidade de que seja tomado o caminho que inicia no estado a e permaneça neste estado após as duas primeiras transições e depois move-se para o estado b lá permanece após o terceiro e quarto passos é dada por P{X5 = b, X4 = b, X3 = a, X2 = a | X1 = a} = paa (1)paa (2)pab (3)pbb (4) = 1 × 1/2 × 2/3 × 1/4 = 1/12 2.3.1. Tipos de cadeias de Markov 1. Cadeias de Markov podem ser classificadas quanto à natureza do tempo de transição entre os estados, ou seja, discreto (DTMC - Discrete-Time Markov Chain) ou contínuo (CTMC - Continuous-Time Markov Chain). Nas cadeias de Markov de Tempo Discreto as transições podem ocorrer somente em intervalos de tempo conhecidos, isto é, passo-a-passo. Este é o caso dos exemplos apresentados anteriormente. Sistemas que são bem representados por DTMCs incluem aqueles onde as transições ocorrem seguindo uma base diária, como visto na Figura 2.3, e aqueles que seguem um relógio discreto como o escalonador de tarefas num computador. Se as transições entre estados puderem ocorrer em instantes de tempo arbitrários (contínuos) a cadeia de Markov é uma CTMC. A propriedade Markoviana de ausência de memória é mantida para ambos os tipos de cadeias [2]. No caso da DTMC, as transições obedecem a uma distribuição geomérica, pois esta é a única distribuição de tempo discreto que apresenta tal propriedade. No caso da CTMC, o tempo das transições segue uma distribuição exponencial. A Figura 2.5 é um exemplo simples de cadeia de Markov de tempo contínuo. Neste caso, a matriz de transição é chamada de matriz geradora infinitesimal pois, neste caso, as transições ocorrem com uma taxa, em vez de uma probabilidade, devido à natureza contínua desse tipo de modelo. Considerando o modelo da Figura 2.5, a matriz geradora Q é composta por elementos qii e qi j , onde i 6= j e ∑ qi j = −qii , ou seja, em uma CTMC os elementos da diagonal têm seus valores definidos de forma que a soma de cada linha da matriz seja igual a 0. Considerando um espaço de estados S = {0, 1, 2}, a matriz Q será: q00 q01 q02 −0, 001 0, 001 0 0 −2 2 Q = q10 q11 q12 = q20 q21 q22 0, 2 0 −0, 2 Para calcular o vetor de probabilidade de uma CTMC, usa-se a Equação 1 (no caso de probabilidades transientes, dependentes do tempo) ou o sistema de Equações 2 (no caso de probabilidades estacionárias, quando o tempo tende a infinito). π 0 (t) = π(t)Q, given π(0). 41 (1) Figure 2.5. Uma cadeia de Markov de tempo contínuo. 1 πQ = 0, ∑ πi = 1 (2) i∈S Explicações detalhadas sobre como chegar a essas equações podem ser encontradas em [2]. Cadeias de Markov também podem ser classificadas de acordo com a frequência com que seus estados podem ser alcançados ao longo do tempo. Em uma cadeia irredutível, cada estado pode ser alcaçado a partir de qualquer outro estado, de forma que todos os estados são recorrentes3 nesse tipo de modelo. Se, uma vez que o sistema deixa um determinado estado, aquele estado não puder mais ser visitado, a cadeia de Markov é dita acíclica. Se uma cadeia não for acíclica nem irredutível, é classificada como phasetype [18]. Ela irá, eventualmente, alcançar um estado absorvente4 , mas enquanto isso não acontece, a cadeia passa por um ou mais estados transientes5 . A Figura 2.6 mostra exemplos dos três tipos de cadeias. Figure 2.6. Tipos de cadeias de Markov de acordo com a frequência de visitas aos estados. 1 3 Um 4 Um estado é chamado recorrente se o sistema é capaz de retornar a esse estado inúmeras vezes [18]. estado é chamado absorvente se uma vez que ele é alcançado, a cadeia não for capaz de sair desse estado. 5 Um estado é chamado transiente se, durante um período de tempo finito, o sistema visitar esse estado um número finito de vezes [18]. 42 2.3.2. Modelos Markovianos com Recompensa .1 Os modelos Markovianos com recompensa (Markov reward models - MRM) [21] são usados geralmente para obter métricas compostas, provendo a análise integrada de desempenho e confiabilidade, por exemplo. Para construir um MRM, uma taxa constante de recompensa ri é associada a cada estado i de uma cadeia de Markov. Também é possível associar taxas de recompensa às transições da cadeia [24], e nesse caso elas são conhecidas como recompensas de impulso (impulse rewards). Em geral, a recompensa associada a um estado denota o nível de desempenho6 obtido pelo sistema enquanto encontra-se naquele estado [18]. Usando novamente o modelo da Figura 2.5, se as taxas de recompensas forem definidas como um vetor r = (r1 , r2 , r3 ) = (5, 0, −2), representando a receita obtida em cada estado, a taxa de reward instantânea esperada para o tempo t é: E[X(t)] = ∑ ri πi (t) = 5π0 (t) + 0π1 (t) + −2π2 (t). (3) i∈S E[X(t)] pode ser considerado como a receita média obtida pela operação do sistema no tempo t. Outros tipos de recompensa podem ser usados, tais como poder computacional, status de disponibilidade do sistema, finalização de tarefas por unidade de tempo. Independentemente da taxa de recompensa, ri que é adotada, a recompensa esperada em estado estacionário pode ser computada na forma da Equação 4. E[X] = ∑ ri πi (4) i∈S Devido à possibilidade de calcular métricas como ganho médio em estado estacionário e ganho total em um determinado intervalo de tempo, os modelos Markovianos de recompensa têm sido vastamente adotados em estudos de performabilidade, que combinam aspectos de performance e dependabilidade como ferramentas unificadas [23]. 2.3.3. Modelos de geração automática .1 DTMCs e CTMCs permitem a análise de muitos tipos de sistemas, com acurácia e poder de modelagem adequados. Outros formalismos de modelagem, tais como as redes de filas [7], redes de Petri estocásticas (SPNs) [15, 10], e redes de Petri determinísticas e estocásticas (DSPNs) [11], podem ser consideradas técnicas de especificação de alto nível para geração de cadeias de Markov. Esses formalismos são úteis em muitas ocasiões, devido à possibilidade de explosão do espaço de estados da cadeia de Markov, à medida que aumenta o número de componentes e a complexidade das interações num sistema. Uma cadeia de Markov de tempo contínuo (CTMC) pode ser associada a uma rede de Petri temporizada e estocástica. Em redes de Petri (RdP) isso é garantido devido a ausência de memória no atraso dos disparos das transições (distribuição exponencial) [14]. Nesse sentido, uma rede de Petri estocástica [8] é isomórfica ao formalismo da CTMC. 6A palavra “desempenho” aqui é usada num sentido amplo, significando qualquer aspecto mensurável do comportamento do sistema. 43 (a) Exemplo de uma rede de Petri. M0 = (1,0,0,0) t0 t1 M1 = (0,1,0,0) M2 = (0,0,1,0) t3 t2 M3 = (0,0,0,1) t4 M0 (b) Árvore de alcançabilidade. 0 t0 t1 t4 2 1 t3 t2 3 (c) Cadeia de Markov. Figure 2.7. Exemplo da equivalência entre cadeia de Markov e rede de Petri. 1 Adicionalmente, para auxiliar na geração da cadeia de Markov equivalente à rede de Petri estocástica, constrói-se a árvore de alcançabilidade. A árvore de cobertura ou alcançabilidade baseia-se na geração de uma árvore que possibilite a representação de todas as possíveis marcações de uma RdP. A Figura 2.7(a) representa um modelo em RdP, sua representação através da árvore de alcançabilidade (Figura 2.7(b)) e a cadeia de Markov equivalente (Figura 2.7(c)). A partir da árvore de alcançabilidade, pode-se construir a cadeia de Markov equivalente ao modelo. O espaço de estados da cadeia de Markov correspondente à árvore de alcançabilidade de uma rede de Petri estocástica, sendo esta equivalente às marcações encontradas. Por conseguinte, na Figura 2.7(b), a árvore de alcançabilidade possui 4 marcações que são equivalentes aos estados da Cadeia de Markov. 44 2.4. Modelos clássicos para avaliação de desempenho e disponibilidade .1 Quando busca-se avaliar aspectos de desempenho e disponibilidade de um sistema, podem ser gerados diferentes modelos, a depender da habilidade e experiência de quem os cria, ou do nível de generalização utilizado, entre outras questões. Esta seção visa apresentar exemplos de modelos que servem como base para muitos estudos, dos mais simples aos mais complexos, envolvendo os aspectos de desempenho e disponibilidade de um sistema. 2.4.1. Cadeias de nascimento-morte .1 Uma cadeia de Markov homogênea, irredutível, de parâmetro contínuo é denominada processo de nascimento e morte se as únicas mudanças permitidas, a partir de um determinado estado i do processo, são para seus vizinhos imediatos, ou seja, para os estados i + 1 (nascimento) ou para o estado i − 1 (morte). Um nascimento se processa com a taxa λi e uma morte com a taxa µi , ou seja, dependem apenas do estado i. Este processo pode ser visto graficamente na Figura 2.8. λ0 λ1 1 0 μ1 μ2 λ3 λ2 3 2 μ3 λ4 4 μ4 ........... μ5 Figure 2.8. Diagrama de um processo de nascimento e morte. 1 Este tipo de cadeia de Markov é muito utilizado para representar sistemas que funcionam com base na simples chegada de requisições, que serão atendidas por n servidores. A chegada de uma nova requisição é um nascimento na cadeia e, portanto, acontece com uma taxa λi , ocupando um servidor do sistema. Já o serviço oferecido pelo sistema é efetuado a uma taxa µi , configurando-se como uma morte na cadeia, que libera um servidor para atender outras possíveis requisições. Aspectos importantes do desempenho do sistema podem ser conhecidos através da probabilidade de se estar em determinados estados da cadeia de nascimento e morte. Um exemplo é a probabilidade do sistema encontrar-se ocioso, que corresponde a π0 . Se essa probabilidade for calculada para estado estacionário do sistema, ainda pode-se obter o tempo médio (em horas) de ociosidade do sistema ao longo do período de um ano. Para isso, basta multiplicar π0 por 8760 horas, que é corresponde ao número de horas de 1 ano. De forma semelhante, se o sistema possui somente n servidores e chegarem novas requisições com uma taxa λn , que não poderão ser atendidas devido a todos os servidores estarem ocupados, a probabilidade de uma requisição ser rejeitada devido ao sistema estar completamente ocupado é calculada como πn · λn . Sistemas de fila também podem ser representados por cadeias de nascimento e morte, com a finalidade de se obter métricas como ocupação média da fila e nível de utilização do sistema. O custo energético médio do sistema também pode ser calculado, ao associar recompensas (rewards) referentes à energia consumida em cada estado da cadeia. 45 2.4.2. Modelos simples de falha e reparo .1 Quando se deseja analisar aspectos relacionados a eventuais falhas e reparos de um sistema computacional, um modelo simples como o apresentado na Figura 2.9 pode servir como base para as construções de outras cadeias mais complexas. Nesse modelo, há apenas 3 estados possíveis para o sistema: Funcionando, Quebrado, ou Em Reparo. Considerando-se, por exemplo, que o sistema esteja funcionando, uma falha ocorrerá com uma taxa λ , que deve ser igual ao inverso do tempo médio para falha do sistema, conhecido como MTTF (Mean Time To Failure). Se o MTTF do sistema for 1000 horas, então o valor de λ será 1/1000, ou 0, 001. Figure 2.9. Diagrama de um modelo simples de falha e reparo. 1 A ocorrência de uma falha coloca o sistema no estado Quebrado, à espera de que a equipe de reparo seja acionada e possa efetivamente iniciar o conserto. A taxa δ corresponderá ao inverso desse tempo gasto desde a falha até o início do reparo. Se esse tempo for de 30 minutos, δ será igual a 2, pois deve-se considerar o inverso do tempo em horas (1/0, 5). A partir do instante em que o sistema já se encontra em reparo, caso o tempo médio desta atividade seja 2 horas, a taxa µ será igual a 0, 5. Dentre as métricas mais importantes que se pode obter deste modelo, estão o percentual de disponibilidade (ou indisponibilidade) em estado estacionário (A) e o downtime (tempo de indisponibilidade) anual (D). O percentual de disponibilidade é calculado simplesmente através da probabilidade do sistema estar no estado Funcionando, πFuncionando . É importante citar que o percentual de indisponibilidade (U) pode ser calculado indiretamente pelo percentual de disponibilidade, como 1 − A, ou diretamente através da soma das probabilidades dos estados Quebrado e Em Reparo. Já o downtime anual, em horas, será igual ao percentual de indisponibilidade multiplicado por 8760, que é o tempo em horas correspondente a um ano. Obtém-se então D = U · 8760. Cadeias de Markov mais complexas podem ser geradas ao conhecer aspectos de falhas isoladas em componentes específicos de um sistema, de forma que ele continua a funcionar mesmo após a quebra de algumas partes. Em muitos casos, o tamanho da cadeia de Markov cresce exponencialmente à medida que mais componentes do sistema vão sendo representados, com suas respectivas falhas e reparos independentes. Diante deste fato, é comum utilizar algumas simplificações que tornam o modelo mais fácil de ser entendido e diminui o tempo exigido para sua solução. 46 2.5. Ferramenta de apoio à modelagem e soluções de cadeias de Markov .1 Nos últimos anos, diversos trabalhos têm sido desenvolvidos para avaliação e modelagem de cadeias de Markov. Nesse sentido, algumas ferramentas são acadêmicas e comerciais. Podemos citar Sharpe [3], JMT [3], Isograph [6] e R [17]. • SHARPE (Symbolic Hierarchical Automated Reliability and Performance Evaluator) é uma ferramenta acadêmica para modelagem e avaliação de desempenho, disponiblidade, confiabilidade e performabilidade. A ferramenta suporta a modelagem através de árvore de falha, diagrama de blocos de confiabilidade, cadeia de Markov, rede de Petri estocástica generalizada, entre outros. • JMT (Java Modelling Tools) é um framework para avaliação de desempenho, planejamento de capacidade e estudos de caracterização de carga. A ferramenta disponibiliza a modelagem de cadeia de Markov, redes de filas, entre outros. • Isograph é uma ferramenta comercial para modelagem e avaliação de confiabilidade, predição de confiabilidade e manutenção. Além disso, a ferramenta oferece análise através de diagrama de blocos de confiabilidade, análise dos efeitos dos modos de falha, análise por árvores de eventos e análise de árvore de eventos e análise de Markov. • R é um ambiente open source para análises estatísticas e gráficos. Adicionalmente, a ferramenta suporta a modelagem de cadeia de Markov. 2.6. Estudos de casos .1 Os estudos de caso expostos a seguir ilustram algumas possibilidades de utilização das cadeias de Markov, auxiliando na avaliação de cenários e descrevendo o comportamento de sistemas que estão sempre exigindo altos níveis de desempenho, confiabilidade e disponibilidade. 2.6.1. Planejamento de redundância em redes de computadores .1 Este estudo de caso é baseado em [12] e apresenta o planejamento de redundância em uma rede de computadores, com base na análise da disponibilidade prevista para cada arquitetura. Apenas roteadores e enlances entre os roteadores são considerados aqui. Duas cadeias de Markov de tempo contínuo foram construídas para analisar as arquiteturas de rede visadas. Na Figura 2.10, a cadeia de Markov representa o cenário mais simples, sem redundância. Há somente um enlace, rotulado como L0, conectando o roteador R0 ao roteador R1. Nesse modelo, a operação normal de um componente é denotada por um rótulo U (up/ativo), e um componente quebrado é representado por um rótulo D (down/inativo). Nessa cadeia de Markov, um estado é definido por uma sequência de rótulos, representado o roteador R0, roteador R1 e enlace L0, respectivamente. λR0 , λR1 e λL0 são as respectivas taxas de falha de R0, R1 e L0. De modo similar, µL0 , µR0 e µR1 são as taxas de reparo de cada componente do sistema. Uma vez que qualquer componente (R0, R1, ou L0) tenha falhado, o sistema completo está num estado de falha e consequentemente nenhuma falha adicional pode ocorrer até que o componente seja 47 reparado. Para esse modelo, o sistema está ativo e funcionando somente no estado UUU. Todos os outros estados estão acinzentados na Figura 2.10, representando os estados de falha do sistema. Figure 2.10. Cadeia de Markov para a disponibilidade de uma rede sem redundância 1 A Figura 2.11 mostra a cadeia de Markov para o sistema com redundância em nivel de enlace. A notação é similar à do modelo anteior. A condição ideal para esse sistema é denotada pelo estado UUUU, no qual todos os componentes estão ativos, em condições normais. A cadeia mostra que o sistema pode falhar devido à quebra de um dos roteadores, ou falhas em ambos os enlaces. Uma hipótese assumida nesse modelo é que há uma política de reparo, que prioriza o enlace L0 sobre o enlace L1, quando ambos falharem. Figure 2.11. Cadeia de Markov para a disponibilidade de uma rede com re1 dundância no enlace Os MTTFs dos componentes usados nas duas arquiteturas são: 131.000 horas para os roteadores e 11.988 horas para os enlaces. O tempo médio para reparo (MTTR) é igual a 12 horas, para todos os compoentes. Note que todos os λi nos modelos são iguais a 1/MT T Fi e todos os µi são iguais a 1/MT T Ri . As taxas são exibidas em vez dos tempos devido à sua forma compacta, que facilita o entendimento dos modelos. A análise do modelo sem redundância, em estado estacionário, provê um valor de 0,9980 para a disponibilidade desse sistema. Calculando a mesma métrica com o modelo 48 que possui redundância de enlace obtém-se 0,9995. Esse aumento na disponibilidade se traduz em uma redução do downtime anual de 7,53 horas para 1,88 horas, configurando-se num ganho importante para empresas e organizações que precisam manter sua infraestrutura de comunicação funcionando de forma ininterrupta, sob pena de degradação da imagem corporativa e grandes prejuízos financeiros por quebras de contrato e/ou perda de possíveis clientes. Além de servir para comparar as métricas estimadas para diferentes arquiteturas candidatas, as cadeias de Markov aqui poderiam ser usadas para verificar até que ponto aumentos na confiabilidade de um determinado componente podem suprir os requisitos do sistema sem a necessidade de comprar e instalar equipamentos para servir de réplicas dos servidores e dados críticos presentes no sistema. 2.6.2. Avaliação de desempenho e confiabilidade de composições de web services .1 Este estudo de caso aborda inicialmente a análise de um processo de agente de viagem. Tal sistema é composto de múltiplos serviços Web, exigindo passos específicos para completar a reserva de uma viagem. A Figura 2.12 mostra um diagrama de atividades UML (Unified Modeling Language) para este processo, adaptado de [19]. Inicialização Consulta - Aérea 2 Consulta - Aérea 1 Reserva - Aérea Reserva - Hotel Resp. ao cliente Figure 2.12. Processo de agente de viagem. 1 No início do processo, o sistema de reserva de viagem busca simultaneamente por vagas em duas companhias aéreas. Quando elas respondem, uma é escolhida com base em algum critéiro tal como preço ou calendário. Se uma das linhas aéreas falhar em responder, a outra é selecionada, e no caso de ambas falharem, o sistema agente de viagem desiste e aborta. Outros passos incluem a invocação efetiva para reserva do bilhete da companhia aérea, a reserva do hotel e a notificação de sucesso para o cliente. Qualquer um dos web services pode falhar em responder e, neste caso, o sistema tenta se recuperar através de reinicializações, exceto pela busca em companhias aéreas concorrentes. Uma CTMC para esse sistema é exibida na Figura 2.13. Esse modelo foi construído para calcular o desempenho e a confiabilidade da composição de Web services e 49 Figure 2. 13. CTMC para a composição de Web services. 1. assim planejar possíveis mudanças no sistema. A tradução das atividades do modelo UML para os estados da cadeia de Markov é quase direta, com exceção de poucos estados que foram adicionados. RIni, RAresv, RHt, e RRep representam os estados de reinicialização do Web service após uma falha. Por exemplo, quando o serviço de Inicialização falha, o sistema vai para o estado RIni, de onde há 2 próximos estados possíveis: a) Inicialização, se a falha que ocorreu for coberta, ou seja, se pode ser resolvida com a reinicialização; e b) Falha, se a falha não foi resolvida pela reinicialização, de forma que o sistema inteiro falha. Os outros estados de reinicialização tem significados similares. Os parâmetros mrspi , mrspa1 , mrspa2 , mrspai , mrspht and mrsprep correspondem ao tempo de resposta médio dos seguintes web services, respectivamente: inicialização, consulta à companhia área 1, consulta à companhia aérea 2, reserva da passagem áerea (invocação), reserva do hotel e resposta ao cliente. Os outros parâmetros seguem uma notação similar. Note que algumas taxas de transição são iguais ao inverso do tempo de resposta médio do Web service (mrspx ), ponderado pela probabilidade de que a transição ocorra, isto é, a confiabilidade do respectivo Web service (rx ). 1−rx . As taxas Se qualquer web service x falhar, a taxa da transição será igual a mrsp x de transição saindo dos estados de reinicialização são iguais ao inverso do tempo médio de reinicialização (mrx ), ponderado pelo fator de cobertura das falhas para aquele Web service (Cx ), no caso de uma reinicialização com sucesso. Se a reinicialização daquele web service não obtiver sucesso, o sistema faz uma transição para o estado Falha, com x uma taxa 1−C mrx . 50 Table 2.1. Valores dos parâmetros para o modelo do Agente de Viagem. 1 Componente Inicialização (init) Consulta - aérea 1 (a1) Consulta - aérea 2 (a2) Invocação - cia. aérea (ai) Reserva hotel (ht) Resp. ao cliente (rep) Conf. (r) 1,0 0,9 0,9 0,9 0,9 1,0 T resp. (mrsp) 1s 2s 2s 1s 2s 1s Cobertura (C) 1,0 1,0 0.0 - T reinic. (mr) 0,15 s 0,15 s 0,15 s 0,15 s Os valores usados como base para a análise da cadeia de Markov são mostrados na Tabela 2.1. Analisando o modelo da Figura 2.13, se obtém, entre outras métricas, a probabilidade do sistema encontrar-se no estado “Completo” no instante t (πCompleto (t)). O valor de πCompleto (t) é equivalente à probabilidade do tempo de resposta do sistema ser menor ou igual ao tempo t: P[Resp ≤ t]. Supondo que o projetista, ou desenvolvedor, desse sistema esteja interessado num tempo de resposta de 8 segundos, calculamos então πCompleto (8) = 0, 5173, ou seja, há 51,83% de probabilidade do sistema responder em 8 segundos ou menos. Calculando essa probabilidade para diferentes valores de tempo t, obtém-se a curva apresentada na Figura 2.14. Através dela, percebe-se que há uma probabilidade próxima a 100% de que o tempo de resposta seja no máximo 25 segundos. Esse tipo de análise pode ser utilizada como base para a definição de contratos de nível de serviço (SLA - Service Level Agreement) entre o provedor da aplicação de Agente de Viagem e seus clientes, fornecendo estimativas concretas sobre o desempenho da aplicação em questão, já levando em consideração aspectos de confiabilidade. 1 Probabilidade 0,8 0,6 0,4 0,2 0 0 5 10 15 20 25 30 Tempo de resposta (s) Figure 2.14. Curva de probabilidade de conclusão de acordo com o tempo. 1 2.7. Considerações finais e perspectivas .1 Este capítulo tratou sobre a teoria que envolve as cadeias de Markov assim como sua utilidade no planejamento de capacidade de sistemas computacionais. Os exemplos de modelos e análises aqui apresentados servem como uma introdução capaz de guiar a construção 51 e análise de outros modelos mais sofisticados, possibilitando a avaliação do desempenho e dependabilidade de vários tipos de sistemas. Espera-se que o leitor possa aproveitar o máximo do texto, e aplicá-lo em ambientes reais, na academia e na indústria, expandindo o conhecimento e a utilização deste formalismo e suas ferramentas associadas. References [1] BARROS, Mônica; Processos Estocásticos; Papel Virtual Editora; Rio de Janeiro; 2004. [2] BOLCH, G., GREINER, S., de MEER, H., and TRIVEDI, K. S.; Queuing Networks and Markov Chains: modeling and performance evaluation with computer science applications; John Wiley and Sons; 2 ed.; 2001. [3] BERTOLI, Marco and CASALE, Giuliano and SERAZZI, Giuseppe; JMT: performance engineering tools for system modeling; SIGMETRICS Perform. Eval. Rev. vol36; March, 2009. [4] GUNTER, B.; GREINER, S.; de MEER, H. and TRIVEDI, K. S.; Queueing Networks and Markov Chains: Modeling and Performance Evaluation with Computer Science Applications; 2nd edition; John Wiley & Sons, Inc.; 2006. [5] HAVERKORT, B. and MEEUWISSEN, A.; Sensitivity and uncertainty analysis of Markov-reward models. IEEE Transactions on Reliability, 44(1):147–154; 1995. [6] Isograph; Reliability Workbench 11 http://www.isograph-software.com/. Último acesso em 09 de Outubro de 2011. [7] KLEINROCK, L. Queueing Systems, volume 1. Wiley, New York; 1975. [8] MACIEL, P. R. M.. LINS, R. D. and CUNHA, P. R. F.; Introdução às Redes de Petri e Aplicações; Sociedade Brasileira de Computação; Campinas, São Paulo. 1996. [9] MACIEL, P. R. M.; TRIVEDI, K.; MATIAS JR R.; and KIM, D.; Performance and Dependability in Service Computing: Concepts, Techniques and Research Directions; Dependability Modeling. IGI Global, Hershey, Pennsylvania; 2011. [10] AJMONE MARSAN, M., CONTE, G., and BALBO, G.; A class of generalized stochastic petri nets for the performance evaluation of multiprocessor systems. ACM Trans. Comput. Syst., 2:93–122. 1984. [11] MARSAN, M. A. and CHIOLA, G.; On petri nets with deterministic and exponentially distributed firing times. In Advances in Petri Nets 1987, covers the 7th European Workshop on Applications and Theory of Petri Nets, pages 132–145, London, UK. Springer-Verlag. 1987. [12] MATOS JÚNIOR, R. de S., GUIMARÃES, A. P., CAMBOIM, K. M. A., MACIEL, P. R. M., TRIVEDI, K .S.; Sensitivity Analysis of Availability of Redundancy in Computer Networks. In Proceedings of the Third International Conference on Communication Theory, Reliability, and Quality of Service, CTRQ 2010, pages 115–121, Budapest. IARIA. 2011. 52 [13] MENASCÉ, D. A.; ALMEIDA, V. A.; and DOWDY, L. W.; Performance by Design: Computer Capacity Planning by Example. Prentice Hall PTR.; 2004. [14] MOLLOY, Michael Karl; On the integration of delay and throughput measures in distributed processing models; Phd Thesis; University of California, Los Angeles, 1981. [15] MOLLOY, M. K.; Performance analysis using stochastic petri nets. IEEE Trans. Comput., 31:913–917. 1982. [16] NORRIS, J. R.; Markov Chains; Cambridge Series in Statistical and Probabilistic Mathematics; 2009. [17] R Project Page The R Project for Statistical Computing; http://www.r-project.org/. Último acesso em 10 de Outubro de 2011. [18] SAHNER, R. A., TRIVEDI, K. S., and PULIAFITO, A. Performance and reliability analysis of computer systems: an example-based approach using the SHARPE software package. Kluwer Academic Publishers, Norwell, MA, USA; 1996. [19] SATO, N. and TRIVEDI, K. S.; Stochastic modeling of composite web services for closed-form analysis of their performance and reliability bottlenecks. In ICSOC, pages 107–118; 2007. [20] SHAMBLIN, James E.; Pesquisa Operacional: uma abordagem básica; Editora Atlas, São Paulo, 1979. [21] SMITH, M. R., TRIVEDI, S. K., and NICOLA, F. V.; The analysis of computer systems using markov reward processes. Technical report, Durham, NC, USA; 1987. [22] STEWART, William J.; Probability, Markov chains, queues and simulation: the mathematical basis of performance modeling. 2009. [23] TRIVEDI, K. S., MUPPALA, J. K., WOOLET, S. P., and HAVERKORT, B. R.; Composite performance and dependability analysis. Performance Evaluation, 14(23):197–215; 1992. [24] TRIVEDI, K. S., MALHOTRA, M., and FRICKS, R. M. Markov reward approach to performability and reliability analysis. In Proceedings of the Second International Workshop on Modeling, Analysis, and Simulation On Computer and Telecommunication Systems, MASCOTS ’94, pages 7–11, Washington, DC, USA. IEEE Computer Society. 1994. [25] TRIVEDI, K. S.; Probability and Statistics with Reliability, Queuing, and Computer Science Applications. John Wiley and Sons, 2nd. edition; 2002. [26] TRIVEDI, K. S. and SAHNER, R.; SHARPE at the age of twenty two; doi = http://doi.acm.org/10.1145/1530873.1530884; SIGMETRICS Perform. Eval. Rev.; vol 36; ACM; March, 2009. 53 Capítulo 3 1 Computação Autonômica aplicada a Segurança de Redes Ariel Soares Teles, Francisco José da Silva e Silva, Zair Abdelouahab Abstract The constant increase in the number of computers networks attacks attempts has pushed researchers community to devise better security strategies. However, the rapid growth both in quantity and complexity of components and services offered in today’s networks has increased the difficulty of administering these, making approaches based only on human interventions impracticable. In order to circumvent this problem, a modern approach called Autonomic Computing (AC) has gained attention from researchers related to network security management. AC has the essence of self-management and the implementation of its concepts for network security systems introduces the ability of self-assurance. This chapter aims to introduce the concepts of AC and shows their applicability to the context of security in computer networks. Resumo O constante aumento no número de tentativas de ataques às redes de computadores leva a uma busca por melhores estratégias de segurança. Porém, o grande crescimento tanto na quantidade como na complexidade de componentes e serviços oferecidos nas atuais redes tem aumentado a dificuldade de administração destas, tornando cada vez mais inviável abordagens baseadas apenas em intervenções humanas. Com o objetivo de contornar esta problemática, uma moderna abordagem denominada Computação Autonômica (CA) tem ganho destaque nas pesquisas relativas ao gerenciamento da segurança de redes. CA tem como essência o autogerenciamento e a aplicação de seus conceitos à segurança de redes introduz no sistema a capacidade de autosegurança. Este capítulo tem como objetivo introduzir os conceitos de CA e mostrar sua aplicabilidade ao contexto de segurança em redes de computadores. 54 3.1. Introdução 1 Segurança em redes de computadores compreende a área responsável pela proteção dos dados que a transitam contra alterações indevidas, acesso não autorizado e indisponibilidade. Desde o surgimento da Internet, a busca por melhores estratégias de segurança tem aumentado consideravelmente, tendo em vista a grande quantidade de tentativas de ataques que vem sendo realizados. Esses ataques, quando bem sucedidos, tem causado prejuízos financeiros e de imagem para empresas, instituições e pessoas físicas. Existem vários obstáculos a serem enfrentados para se alcançar redes realmente seguras, dentre eles pode-se destacar a existência de dependência dos sistemas de segurança por gerenciamento com intervenção humana, sendo este um processo que aumenta continuamente o nível de dificuldade. O fato dos ataques à sistemas computacionais estarem se tornando cada vez mais sofisticados e as várias deficiências encontradas nos atuais sistemas de segurança também são outros exemplos de obstáculos, como serão vistos neste capítulo. Tudo isso eleva a complexidade do problema da gerência de segurança e por isso é interessante a utilização de recursos oferecidos pela Computação Autonômica (CA). Sistemas de CA são capazes de gerenciarem a si próprios e se adaptarem dinamicamente às mudanças a fim de restabelecer seu equilíbrio de acordo com as políticas e os objetivos de negócio. Para isso, dispõem de mecanismos efetivos que os permitem monitorar, controlar e regular a si próprios, bem como recuperarem-se de problemas sem a necessidade de intervenções externas. A arquitetura e as propriedades de CA para a implementação de sistemas propõe uma abordagem com muitas vantagens para ser aplicada à segurança de redes. Além de apresentar características intrísecas de autogerenciamento, um elemento autonômico provê outras funcionalidades que podem ser utilizadas para resolver problemas particulares à segurança de redes, com o uso de técnicas de aprendizagem e cooperação entre as aplicações. Neste capítulo será discutido os conceitos de CA e mostrado sua aplicabilidade ao contexto de segurança em redes de computadores. A aplicação dos conceitos de CA à segurança de redes introduz no sistema a capacidade de autosegurança, através da qual serviços e funções de gerenciamento da segurança são executados sem a necessidade de um gerente humano, a partir da definição de objetivos e parâmetros iniciais fornecidos por seus administradores. O restante deste trabalho está organizado da seguinte forma. Todos os fundamentos de CA são descritos na Seção 2, mostrando suas propriedades, arquitetura e detalhes do ciclo autonômico. A Seção 3 irá descrever alguns conceitos de segurança em redes e mostrar os problemas enfrentados por elas neste quesito. A necessidade que a segurança em redes de computadores tem por mecanismos autonômicos e exemplos de trabalhos já realizados em segurança autonômica de redes serão detalhados na Seção 4. Para finalizar o capítulo, na Seção 5 serão apresentadas as conclusões. 55 3.2. Computação Autonômica 1 O termo Computação Autonômica surgiu em 2001, com um manifesto publicado por Paul Horn, pesquisador da IBM, que lançou um desafio sobre o problema da crescente complexidade de gerenciamento do software [17]. O termo autonômico vem da biologia e está relacionado às reações fisiológicas involuntárias do sistema nervoso [25]. No corpo humano, o sistema nervoso autonômico cuida de reflexos inconscientes, isto é, de funções corporais que não requerem nossa atenção como a contração e expansão da pupila, funções digestivas do estômago e intestino, a frequência e profundidade da respiração, a dilatação e constrição de vasos sanguíneos, etc. Esse sistema reage às mudanças, ou perturbações, causadas pelo ambiente através de uma série de modificações, a fim de conter as perturbações causadas ao seu equilíbrio interno. Em analogia ao comportamento humano, diz-se que um sistema computacional está em equilíbrio quando o seu ambiente interno (formado pelos seus subsistemas e pelo próprio sistema) está em proporção devida com o ambiente externo. Conforme observaram Parashar e Hariri em [27] e detalhado em [28], se o ambiente interno ou externo perturba a estabilidade do sistema, ele sempre atuará de modo a recuperar o equilíbrio original. Dessa forma, os sistemas de CA são sistemas capazes de se autogerenciarem e se adaptarem dinamicamente às mudanças a fim restabelecer seu equilíbrio de acordo com as políticas e os objetivos do negócio do sistema [17]. Para isso, devem dispor de mecanismos efetivos que os permita monitorar, controlar e regular a si próprios, bem como recuperarem-se de problemas sem a necessidade de intervenções externas. 3.2.1. Propriedades de Sistemas Autonômicos 1 A essência da CA é o autogerenciamento. Para implementá-lo, o sistema deve ao mesmo tempo estar atento a si próprio e ao seu ambiente. Desta forma, o sistema deve conhecer com precisão a sua própria situação e ter consciência do ambiente operacional em que atua. Do ponto de vista prático, conforme Hariri [13], o termo computação autonômica tem sido utilizado para denotar sistemas que possuem as seguintes propriedades: • Autoconsciência (self-awareness): O sistema conhece a si próprio: seus componentes e inter-relações, seu estado e comportamento; • Consciência do contexto (context-aware): O sistema deve ser ciente do contexto de seu ambiente de execução e ser capaz de reagir a mudanças em seu ambiente; • Autoconfiguração (self-configuring): O sistema deve ajustar dinamicamente seus recursos baseado em seu estado e no estado do ambiente de execução; • Auto-otimização (self-optimizing): O sistema é capaz de detectar degradações de desempenho e de realizar funções para auto-otimização; • Autoproteção (self-protecting): O sistema é capaz de detectar e proteger seus recursos de atacantes internos e externos, mantendo sua segurança e integridade geral; • Autocura (self-healing): O sistema deve possuir a habilidade de identificar potenciais problemas e de se reconfigurar de forma a continuar operando normalmente; 56 • Aberto (open): O sistema deve ser portável para diversas arquiteturas de hardware e software e, consequentemente, deve ser construído a partir de protocolos e interfaces abertos e padronizados; • Capacidade de Antecipação (anticipatory): O sistema deve ser capaz de antecipar, na medida do possível, suas necessidades e comportamentos considerando seu contexto e de se autogerenciar de forma pró-ativa. As propriedades de autoconfiguração, auto-otimização, autocura e autoproteção são suficientes para realizar a visão original do termo [25]. Para a incorporação das propriedades de auto-otimização e autocura, mecanismos de autoconsciência, consciência de contexto e autoconfiguração devem ser requisitos do sistema. 3.2.2. Arquitetura de um Sistema Autonômico 1 Arquiteturas de sistemas autonômicos visam formalizar um quadro de referência que identifica as funções comuns e estabelece os alicerces necessários para alcançar a autonomia. Em geral, essas arquiteturas apresentam soluções para automatizar o ciclo de gerenciamento de sistemas, conforme mostrado na Figura 3.1, o qual envolve as seguintes atividades: Figura 3.1. Ciclo de gerenciamento de sistemas [5]. 1 • Monitoramento ou Medição: Coleta, agrega, correlaciona e filtra dados sobre recursos gerenciados; • Análise e Planejamento: Analisa os dados coletados e determina se devem ser realizadas mudanças nas estratégias utilizadas pelo recurso gerenciado; • Controle e Execução: Escalona e executa as mudanças identificadas como necessárias pela função de análise e decisão. 3.2.2.1. MAPE-K 1 Em 2003 a IBM propôs uma versão automatizada do ciclo de gerenciamento de sistemas chamado de MAPE-K (Monitor, Analyse, Plan, Execute, Knowledge) [17], representado 57 na Figura 3.2. Este modelo está sendo cada vez mais utilizado para inter-relacionar os componentes arquiteturais dos sistemas autonômicos. De acordo com essa arquitetura, um sistema autonômico é formado por um conjunto de elementos autonômicos. Um elemento autonômico contêm um único gerente autonômico que representa e monitora um ou mais elementos gerenciados (componente de hardware ou de software) [19]. Cada elemento autonômico atua como um gerente responsável por promover a produtividade dos recursos e a qualidade dos serviços providos pelo componente do sistema no qual está instalado. Figura 3.2. MAPE-K: Ciclo de gerenciamento autonômico [16]. 1 No ciclo MAPE-K autonômico (Figura 3.2), o elemento gerenciado representa qualquer recurso de software ou hardware o qual é dado o comportamento autonômico através do acoplamento de um gerenciador autonômico, podendo ser por exemplo um servidor Web ou banco de dados, um componente de software específico em um aplicativo (por exemplo, o otimizador de consulta em um banco de dados), o sistema operacional, um conjunto de máquinas em um ambiente de rede, etc. Os sensores (sensors) são responsáveis por coletar informações do elemento gerenciado. Estes dados podem ser os mais diversos possíveis, por exemplo, o tempo de resposta das requisições dos clientes, caso o elemento gerenciado seja um servidor Web. As informações coletadas pelos sensores são enviadas aos monitores (monitors) onde são interpretadas, preprocessadas e colocadas em um nível mais alto de abstração, para então serem enviadas para a etapa seguinte no ciclo, a fase de análise e planejamento (analyze and planing). Nesta fase, tem-se como produto uma espécie de plano de trabalho, que consiste de um conjunto de ações a serem executadas pelo executor (execute). O componente responsável por fazer as alterações no ambiente é chamado de atuador (effectors). Somente os sensores e atuadores possuem acesso direto ao elemento gerenciado. Durante todo ciclo de gerenciamento autonômico, pode haver a necessidade de uma tomada de decisão, dessa forma, faz-se necessário também a presença de uma base de conhecimento (knowledge), sendo que esta é comumente mais explorada na fase de análise e planejamento [8]. Isso é implementado utilizando dois ou mais ciclos de gerenciamento autonômico, um ou mais ciclos de controle local e um global. Os ciclos de controle local tratam apenas 58 de estados conhecidos do ambiente local, sendo baseado no conhecimento encontrado no próprio elemento gerenciado. Por esta razão, o ciclo local é incapaz de controlar o comportamento global do sistema. O ciclo global, por sua vez, a partir de dados provenientes dos gerenciadores locais ou através de um monitoramento a nível global, podem tomar decisões e atuar globalmente no sistema. No entanto, a implementação das interações entres os diversos níveis existentes vai depender das necessidades e das limitações da aplicação. 3.2.3. Monitoramento 1 O monitoramento corresponde a primeira fase do ciclo autonômico MAPE-K. Nesta etapa, sensores são utilizados com a finalidade de obter dados que reflitam mudanças de comportamento do elemento gerenciado ou informações do ambiente de execução que sejam relevantes ao processo de autogerenciamento. Os sensores são dispositivos de hardware ou software que estão diretamente ligados ao componente que se deseja monitorar. O tipo de informação coletada bem como o modelo do(s) monitor(es) empregados são específicos para cada tipo de aplicação. Contudo, alguns requisitos são comuns entre eles, tais como a filtragem, a escalabilidade, a dinamicidade e a tolerância a falhas [23]. Um monitor deve prover filtragem, pois a quantidade de dados coletados pode crescer muito rapidamente e consumir recursos de transmissão, processamento e armazenamento. Grande parte desses dados podem ser de pouca relevância e, portanto, descartá-los não implicaria em prejuízo. Considerando-se que um sistema pode crescer indefinidamente, contendo inúmeros objetos sob monitoramento, a tarefa dos monitores não pode consumir recursos e reduzir o desempenho do sistema. Em contrapartida, as mudanças no ambiente ou no comportamento do sistema não podem afetar o serviço de monitoramento [9]. Além disso, falhas podem ocorrer e o monitor pode apresentar algum comportamento atípico ou mesmo parar de funcionar completamente. Dessa forma, devem ser levadas em consideração provisões como redundância e mecanismos de recuperação de falhas dos monitores. 3.2.3.1. Classificação de Sistemas de Monitoramento 1 Aplicações autonômicas possuem requisitos de monitoramento diferentes, variando os elementos que se deseja monitorar. De acordo com as características deste elementos podem ser implementadas formas de monitoramento específicas. Em [16], Huebscher e McCann identificaram dois tipos de monitoramento em sistemas autonômicos, classificados de acordo com o tipo de sensor utilizado: • Monitoramento Passivo: No monitoramento passivo os dados obtidos de monitoramento são fornecidos pelo sistema sem necessidade de nenhuma alteração na aplicação. Por exemplo, no Linux, informações sobre uso de CPU e memória podem ser coletados do diretório /proc; • Monitoramento Ativo: No monitoramento ativo para que se possa fazer a captura de dados é necessário alterar a implementação da aplicação ou sistema operacional. São exemplo desse tipo de monitoramento, sensores que são inseridos na forma de bytecodes em aplicações Java. 59 Conforme observado nos trabalhos de [23] e [16], foi possível constatar que o monitor também pode ser classificado quanto à estratégia utilizada: • Monitoramento orientado à eventos: Eventos são ações que acontecem no sistema e podem alterar o seu estado, por exemplo o “processo ocioso”, “processo em execução”, “início do processo”, “chegada de uma requisição”, etc. Os eventos acontecem instantaneamente e portanto nesse tipo de monitoramento cada evento corresponde a um dado que é transmitido para o monitor. Essa abordagem pode ser aplicada nos casos em que a taxa de ocorrência de eventos é muito baixa. A desvantagem é que o número de eventos gerados pode ser muito grande e informações redundantes e desnecessárias estariam sobrecarregando os recursos do sistema; • Monitoramento orientado ao tempo: Nessa estratégia, o monitor coleta os dados de forma periódica, ou seja, um intervalo de tempo é definido para que o monitor consulte as informações do ambiente em busca de eventos significativos. A grande dificuldade encontrada nessa estratégia está em definir o melhor intervalo para a realização da coleta. A desvantagem nesse caso seria que informações importantes seriam perdidas durante esse intervalo; • Monitoramento autonômico: As duas estratégias acima podem ser intercaladas conforme ocorrem as mudanças no ambiente. Por exemplo, se a taxa de ocorrência de eventos está elevada a melhor estratégia seria a orientada ao tempo. O tamanho do intervalo de tempo na segunda estratégia também pode ser refinado segundo uma abordagem autonômica de acordo com as características do ambiente. 3.2.4. Análise e Planejamento 1 A análise vem após o monitoramento no ciclo de gerenciamento MAPE-K, tendo como fase seguinte o planejamento. Em geral estas duas fases são geralmente implementadas em um único componente. O processo de análise e planejamento é essencial para autonomia do sistema, pois é nele que são geradas as decisões sobre quais modificações serão realizadas no sistema, que corresponde ao elemento gerenciado. A fase de análise e planejamento recebe como entrada os eventos e seus dados gerados pelo sistema de monitoramento e gera como saída um conjunto de ações, também chamado de plano de ações. Estas ações correspondem às operações de reconfiguração que, de acordo com o mecanismo de tomada de decisão, devem ser executadas no sistema a fim de manter o equilíbrio do sistema considerando seus objetivos. Muitas técnicas podem ser empregadas na fase de análise e planejamento, entre as quais se destacam o uso de funções de utilidade, aprendizado por reforço e regras Evento-Condição-Ação (ECA). Sendo esta última detalhada a seguir. 3.2.4.1. Análise e Planejamento Baseados em Regras ECA 1 Regras do tipo evento-condição-ação (ECA Rules) determinam as ações a serem realizadas quando um evento ocorre desde que certas condições sejam satisfeitas. De acordo com [5], ECA rules são especificações declarativas de regras ou regulamentações que 60 determinam o comportamento de componentes de aplicações. Para cada evento são definidos um conjunto de regras que podem gerar uma ou mais ações. Esses eventos também podem satisfazer as condições de diferentes regras e algumas dessas regras podem gerar ações que conflitam entre si. Nem sempre esses conflitos podem ser detectados durante a escrita das regras, sendo muitas vezes descobertos em tempo de execução. Em ECA rules o evento especifica quando as regras deverão ser acionadas. A condição é a parte a ser verificada, podendo esta ser satisfeita ou não. E por fim, a ação, a qual representa a operação a ser realizada caso a condição seja satisfeita. São definidas da seguinte forma: on event if condition do action; Figura 3.3. Definição de uma regra do tipo ECA. 1 3.2.5. Execução de Ações de Reconfiguração 1 Na etapa de execução do ciclo MAPE-K são realizadas reconfigurações no sistema de forma a restabelecer seu equilíbrio. A finalidade da reconfiguração é permitir que um sistema evolua (ou simplesmente mude) incrementalmente de uma configuração para outra em tempo de execução, introduzindo pouco (ou, idealmente, nenhum) impacto sobre a execução do sistema. Desta forma, os sistemas (ou aplicações) não necessitam ser finalizados, ou reiniciados, para que haja as mudanças. A autoconfiguração (self-configuring) é a característica do sistema autônomo que o permite ajustar-se automaticamente às novas circunstâncias percebidas em virtude do seu próprio funcionamento, de forma a atender a objetivos especificados pelos processos de autocura, auto-otimização ou autoproteção [5]. O processo de reconfiguração é realizado pelos executores através dos atuadores. Executores recebem como entrada um plano de ações gerado na etapa de análise e planejamento e utiliza os atuadores pertinentes para implementar as ações de reconfiguração descritas no plano. As reconfigurações devem ser realizadas dinamicamente, sem impor a necessidade de parar e/ou reiniciar o sistema. Segundo [24], duas abordagens gerais têm sido utilizadas para atingir adaptação: adaptação paramétrica e adaptação composicional. A adaptação paramétrica consiste na alteração de variáveis que determinam o comportamento do sistema. Já a adaptação composicional (ou estrutural) consiste na substituição de algoritmos ou partes estruturais de um software, permitindo que este adote novas estratégias (algoritmos) para tratar situações que não foram inicialmente previstas na sua construção. 3.2.5.1. Questões de Projeto de Mecanismos de Reconfiguração 1 O desenvolvimento de um mecanismo de reconfiguração dinâmica não é algo simples. Diversos requisitos devem ser considerados a fim de manter as características e funcionalidades do sistema [26], entre as quais destaca-se: 61 • Separação de responsabilidades: No desenvolvimento de sistemas autonômicos, a separação de responsabilidades permite que o código funcional da aplicação (responsável pelas regras de negócio) seja separado do código responsável pela adaptação. Isto simplifica o desenvolvimento, a manutenção e o reuso do código adaptativo; • Confiabilidade: Um problema quando se modifica um sistema em tempo de execução é a sincronização entre reconfigurações e execução funcional do sistema. A reconfiguração não deve prejudicar a funcionalidade do sistema. Léger et al., em [22], definiram um conjunto de propriedades a fim de garantir a confiabilidade no contexto das reconfigurações dinâmicas em sistemas baseados em componentes. Esse conjunto de propriedades foi baseado em transações e denominado ACID (Atomicidade, Consistência, Isolamento e Durabilidade); • Preservação de consistência: Quando a reconfiguração do sistema ocorre em tempo de execução, é importante que a reconfiguração preserve a consistência do sistema. O estado do sistema interno deve ser mantido e as informações trocadas entre os componentes não devem ser perdidas; • Custo da reconfiguração: Em [11] o custo de reconfiguração é definido como uma medida dos efeitos negativos introduzidos pela reconfiguração. Esses efeitos negativos incluem, por exemplo, a indisponibilidade temporária do serviço, ou a perturbação induzida em outros serviços após o possível aumento do consumo de recursos de rede durante a reconfiguração. A relação custo/benefício deve ser avaliada. 3.3. Segurança em Redes de Computadores 1 O grande crescimento na quantidade de componentes e serviços oferecidos nas atuais redes de computadores tem aumentado o nível de complexidade no trabalho de gerenciamento e administração destas. Cada vez mais são integrados novos dispositivos às redes, que requerem conectividade a qualquer momento e em qualquer lugar, além de se caracterizarem por sua heterogeidade que dificulta o processo de padronização das aplicações. Então se faz necessário aplicar a ideia de CA às Redes de Computadores, através da atribuição da capacidade para gerenciarem a si próprias, denominadas então como Redes Autonômicas [3]. Os serviços e funções de gerenciamento da rede são executados sem a necessidade de um gerente humano de maneira transparente para seus usuários, havendo apenas a necessidade de objetivos e parâmetros iniciais, os quais o sistema leva em consideração. A rede deve ser capaz de aprender com as ações dos seus componentes através da análise dos resultados obtidos. A capacidade de adaptação e aprendizagem são as características das redes autonômicas. Neste cenário, não se pode deixar de lado a segurança da informação, que é requisito fundamental para o bom funcionamento de qualquer sistema computacional, compreendendo o conjunto de medidas que visam preservar e proteger as informações e os sistemas de informação. Visto que essas redes estão conectadas quase sempre à Internet, as aplicações residentes nelas podem sofrer com atividades maliciosas originadas a partir de qualquer usuário conectado à rede. 62 É normal que o acesso à rede mundial de computadores ofereça a possibilidade de descoberta e exploração de vulnerabilidades de forma bem rápida, quase sempre mais do que a atualização das ferramentas de segurança e da emissão de correções pelos fabricantes de softwares. Então o número de incidentes de segurança cresce muito rapidamente juntamente com a quantidade de danos causados. No entanto, proporcionalmente tem crescido também as pesquisas de novos mecanismos e técnicas para aumentar o nível de segurança. Políticas de acesso, uso de firewalls, sistemas de detecção de intrusão, sistemas de prevenção de intrusão, honeypots, entre outros, tem sido essas contra medidas. Em segurança da informação é possível identificar os seguintes propriedades básicas, consideradas também os pilares para a segurança de redes [20]: • Confidencialidade: Proteção dos dados contra divulgação não autorizada. A informação só deve estar disponível para aqueles devidamente autorizados; • Integridade: Proteção dos dados contra modificação não autorizada. A informação não deve ser destruída ou comprometida e os sistemas devem ter um desempenho correto; • Disponibilidade: Proteção de acesso aos recursos da rede para que estejam disponíveis quando requisitados pelas entidades autorizadas. Os serviços e recursos do sistema devem estar disponíveis sempre que forem necessários; • Autenticidade: Proteção dos recursos da rede contra acesso não autorizado. É necessário a identificação dos elementos da transação; • Não-repúdio: Proteção contra negação falsa de ações que um usuário ou entidade tenha feito. Não é possível negar a existência ou autoria de uma operação que criou, modificou ou destruiu uma informação; • Auditoria: Implica no registro das ações realizadas no sistema, identificando os sujeitos e recursos envolvidos, as operações realizadas, seus horários, locais e outros dados relevantes. Essas propriedades guiam para a determinação de um foco no qual uma determinada aplicação deve ser desenvolvida, a fim de atender os requisitos de segurança especificados pela própria necessidade do ambiente em questão. No entanto, não é apenas isto que dita o escopo de uma aplicação para segurança de redes, sendo importante delimiar a(s) fase(s) de proteção em que ela irá atuar. 3.3.1. Fases para Proteção de Redes 1 É possível destacar quatro fases para proteger a rede contra ataques [41]: Prevenção (Prevetion), Detecção (Detection), Defesa (Defense) e Forense (Forensics), como pode ser visto na Figura 3.4. A prevenção compreende todos os métodos aplicados para evitar ataques, a fim de garantir a confidencialidade e integridade dos dados, com a utilização de controles de acesso aos recursos da rede. Isso inclui técnicas de autorização e autenticação (serviços de login), estabelecimento de confiança, bem como criptografia e filtragem 63 de tráfego (uso de firewall). É importate ressaltar que prevenção só é possível para ataques conhecidos, mas há trabalhos sendo desenvolvidos no sentido de prever a ocorrência de ataques desconhecidos, como em [33] e [37]. Figura 3.4. Quatro fases de proteção [41]. 1 Mecanismos de prevenção são considerados sistemas de defesa em primeira linha, ou seja, são responsáveis pela primeira etapa na segurança de uma rede de computadores. Normalmente essa defesa em primeira linha é feita na fase de projeto da rede, a fim de desenvolvê-la já de maneira segura e obter melhores resultados quando colocada em operação. Esses mecanismos são tipicamente implantados para controlar acesso a recursos e à informação que transita na rede. Se a prevenção falhar, detecção é a próxima fase a lidar com um incidente. Detecção é então o processo de descoberta de um ataque ou da preparação para sua realização, ou quaisquer outras atividades maliciosas, desde um port scan até indícios de quebra de senhas por técnica de força bruta. Isso normalmente é feito pela análise de dados capturados por um sniffer, sendo este a interceptação e registro de dados que trafegam pela rede. Sistemas de detecção de intrusão (IDS - Intrusion Detection Systems) são utilizados na fase de detecção. Eles realizam o monitoramente da rede, chamado de NIDS (Network Based Intrusion Detection System), ou de um dispositivo conectado a ela, bem conhecido como HIDS (Host Based Intrusion Detection System), com objetivo de encontrar a ocorrência de algum ataque ou atividade maliciosa. Um IDS pode utilizar várias abordagens para identificar um ataque, sendo as mais conhecidas: Baseado em Assinatura (Signature Based) e Baseado em Anomalia (Anomaly Based), como descrito em [18]: • Baseado em Assinatura: Monitora as atividades da rede, ou de um dispositivo específico, procurando por ocorrências de alguma intrusão que é comparada com uma base de assinaturas, esta última contendo informações de características de ataques e atividades maliciosas; • Baseado em Anomalia: Inicialmente é montado um perfil que representa o comportamento normal da rede ou host, fase conhecida como treinamento. Posteriormente o sistema entra em fase de monitoramento, no qual utiliza várias métricas para determinar se está havendo algum comportamento diferente do perfil inicial, caracterizando então uma intrusão. 64 Outro mecanismo encontrado na fase de detecção é visto através das chamadas Metodologias de Decepção, definidas em [15] como a criação de um ambiente falso para enganar usuários mal intencionados. Elas são usadas quando aplicadas técnicas no qual o atacante interage com um recurso colocado como uma armadilha, propositalmente vulnerável, que emula os serviços ou sistemas que realmente deveriam ser alvos de sua ação. Técnicas bastante utilizadas são com o uso de honeypots, definido por Spitzner [38] como um recurso de rede cuja função é de ser sondado, atacado ou comprometido. Significa dizer que poderá ser invadido, sendo uma máquina configurada a fim de obter informações sobre o invasor. A intenção é que o intruso ao realizar uma tentativa de invasão, na qual a rede possua um honeypot em funcionamento, tenha a sensação de está interagindo com uma máquina que exerce alguma funcionalidade e poderá conseguir algum recurso. Se um tráfego malicioso é detectado, então é iniciado o processo ou fase de defesa. Para que isso ocorra é necessário que o sistema implemente essas duas fases (detecção e defesa), integrando mais de uma aplicação de segurança. Um exemplo é o Sistema de Prevenção de Intrusão (IDS - Intrusion Prevention System), no qual gera alguma resposta a fim de neutralizar o ataque, podendo integrar um IDS e um firewall, por exemplo. Em alguns casos quando as fases anteriores de prevenção e detecção falham e o ataque foi bem sucedido, se faz necessário uma análise de todos os logs, no sentido de aprender como os métodos utilizados nos processos de detecção e defesa podem ser melhorados para futuros incidentes. Sendo esta a fase chamada de forense, que tem como objetivo realizar uma investigação para descobrir detalhes específicos de ataques, apresentando resultados que contribuam de alguma maneira para a melhoria da proteção da rede. Outro objetivo desta fase é fornecer provas suficientes para permitir que o autor do incidente de segurança seja processado. Para Pilli ES et al., em [31], as técnicas de forense de rede fornecem recursos para os investigadores rastrearem os atacantes. Diante destas fases é possível caracterizar a atuação das aplicações de segurança de redes. Por sua vez, muitas das aplicações utilizadas atualmente possuem problemas. Junto a isso, os ataques à redes de computadores vem se tornando cada vez mais sofisticados. Esses problemas serão detalhados a seguir. 3.3.2. Problemas Enfrentados 1 Zseby et al., em [41], afirma que a segurança de redes necessita crucialmente de uma maior atenção por parte do administrador e quase sempre de mais esforços e custos, pois novos protocolos e aplicações introduzem novas vulnerabilidades. Infelizmente, os mecanismos utilizados para aumentar o nível de segurança atualmente possuem alguns problemas, a saber: • Estrutura sólida: Sistemas desenvolvidos para segurança possuem pouca flexibilidade, pois são desenvolvidos apenas para situações isoladas. Não possuem as características de um sistema aberto. Também não são capazes de se integrar com outros mecanismos de segurança existentes no ambiente; • Habilidade de defesa baixa: O escopo das atuais aplicações de segurança é limitado. São desenvolvidas apenas para atender alguma das fases de proteção, sem ter 65 a habilidade para prover um mecanismo integrado que forneça maior capacidade de segurança; • Sem autoadaptação: Aplicações não tem a capacidade de mudar componentes internos para se adaptarem às necessidades do ambiente em que se encontram a fim de conseguir provê segurança à rede. Todo um processo de reconfiguração manual é preciso para manter a segurança; • Sem autoaprendizagem: Aplicações não possuem a habilidade para aprender com o tempo que se encontram em funcionamento, necessitando de intervenção para se atualizarem. Não conseguem aprender com suas próprias ações, verificando os resultados destas que foram providos à rede, para uma análise e melhorias futuras; • Sem autoevolução: Além de não possuirem capacidade para aprenderem, ainda não implementam meios para que novos conhecimentos sejam agregados às técnicas de defesa. Para Atay e Masera, em [2], todos os métodos de análise de ameaças, vulnerabilidades e riscos necessitam atualizarem continuamente os seus conhecimentos sobre as novas fraquezas encontradas nos ativos da rede. Isso servirá para identificar como estas fraquezas podem ser exploradas, para posteriormente definir e aplicar as contramedidas necessárias. Esse é um ciclo contínuo, pois novas avaliações serão necessárias ao longo do tempo. No entanto, sabe-se que informações sobre novos ataques não são imediatamente divulgadas, pelos fabricantes ou pela comunidade que desenvolve o software, devido à sua sensibilidade. Isso ocorre devido essas informações poderem ser utilizadas para explorar mais ainda as vulnerabilidades, visto que usuários mal intecionados podem obtê-las. Elas são publicadas então logo após os fabricantes liberarem as correções. Os métodos de análise de riscos citado por [2] devem refazer a avaliação de segurança dos sistemas levando em consideração informações de novos ataques quando forem divulgadas, ou com a utilização de técnicas de inteligência para detecção antes mesmo da divulgação. Diante desta situação, Wang et al. [40] afirma que a direção correta para o desenvolvimento de aplicações de defesa e segurança é com a adoção de duas características: a integração e inteligência. Integração para possibilitar o gerenciamento de vários recursos de proteção em um ambiente de rede distribuído e a inteligência para provê adaptação ao ambiente, aumentar a eficiência de proteção baseado em seu conhecimento e no que adquiriu e, por fim, alcançar um equilíbrio entre a aplicação de segurança e o ambiente de rede. Aliado aos problemas enfrentados pelos atuais sistemas de segurança, os ataques a sistemas computacionais estão se tornando cada vez mais sofisticados, imprevisíveis, frequentes e com uma maior quantidade de origens [2]. Como exemplo destes ataques pode-se destacar os realizados com a utilização de Botnets e os ataques de último dia, detalhados a seguir. 66 3.3.2.1. Botnets 1 De acordo com Rajab et al., em [35], Botnet é uma rede de computadores comprometidos controlados remotamente por um operador humano chamado de botmaster. Um bot é uma aplicação residente em um nó integrante de uma Botnet com capacidade de se autopropagar infectando outros hosts vulneráveis. Botnets são plataformas de computação distribuída predominantemente usadas para atividades ilegais com o objetivo de lançamentos de ataques de negação de serviço distribuído (DDoS - Distributed Denial of Service), envio de spam, trojan e e-mails phishing, distribuição ilegal de mídias e softwares piratas, roubo de informações e de recursos computacionais, realização de fraudes e falsidade de identidade [7]. 3.3.2.2. Ataques de Último Dia 1 Um ataque de último dia, do inglês Zero-day Attack ou 0-Day Attack, também conhecido como ataque de dia zero ou zero hora, aproveita vulnerabilidades que atualmente não tem uma solução ou ainda não são conhecidas [33] [37]. Normalmente, é descoberto um bug ou um problema em um software depois que ele foi liberado, sendo então em seguida oferecida uma correção destinada ao problema original. Um ataque de último dia vai aproveitar esse problema antes da correção ser criada. Na maioria dos casos, esse tipo de ataque vai tirar proveito de um problema não conhecido pelos criadores do software e seus usuários. É importante ressaltar que nem todos os ataques de último dia realmente acontecem antes dos produtores de software estarem cientes da vulnerabilidade. Em alguns casos eles conhecem a vulnerabilidade, mas o desenvolvimento da correção pode demandar um tempo elevado, em frente a complexidade do problema enfrentado. Neste sentido, proteção de último dia é a capacidade de um sistema de segurança fornecer proteção contra ataques de último dia. 3.4. Segurança Autonômica em Redes de Computadores 1 Os sistemas de proteção das redes atuais estão baseados no paradigma da computação interativa, ou seja, fica a cargo dos administradores humanos decidirem o que fazer e como proteger os sistemas no caso de ocorrências de ataques maliciosos ou erros em cascata imprevistos [3]. Os sistemas que incorporam mais de uma fase para proteção de rede, objetivando aumentar ainda mais o nível de segurança, são mais complexos. Isso se deve ao fato de possuirem mais componentes para atenderem os requisitos de cada fase. Essa complexidade necessita de uma constante intervenção humana especializada para a correta utilização do sistema. Dessa maneira, é interessante a utilização de mecanismos autonômicos para automatizar os processos de gerência. A aplicação dos conceitos da CA à segurança de redes introduz no sistema a capacidade de autosegurança, através da qual serviços e funções de gerenciamento da segurança são executados sem a necessidade de um gerente humano, a partir da definição de objetivos e parâmetros iniciais fornecidos por seus administradores. 67 É possível destacar também que para tentar propror soluções aos problemas descritos anteriormente (Seção 1.3.2), a arquitetura de sistemas autonômicos pode ser aplicada para o desenvolvimento de software voltado para defesa e segurança. O modelo MAPE-K (Seção 1.2.2.1) oferece uma visão conceitual de como sistemas autonômicos podem ser desenvolvidos para atender necessidades de segurança. Sensores podem ser qualquer programa que verifique ocorrências de tráfego malicioso, independente de qual fase de proteção seja, coletando informações relevantes da rede para serem enviadas aos monitores. Exemplo de dados coletados podem ser, por exemplo, o tráfego destinado a honeypots, alertas de IDS, logs de firewall, etc. Os monitores irão receber essas informações e tratá-las a fim de extrair o que vem a ser relevante, por exemplo, o endereço ip de origem do intruso, o protocolo utilizado pelo serviço no qual foi realizado o ataque, horário/data da intrusão, etc. Os monitores enviarão as informações necessárias para os componentes de análise e planejamento, que as utilizarão para seu processamento. Como se sabe, geralmente as fases de análise e planejamento são implementadas em um único componente. O processamento realizado por esse componente varia de acordo com a estratégia autonômica adotada pelo administrador que inseriu os objetivos para o sistema. Um exemplo de processamento utilizando ECA rules pode ser visto na Figura 3.5. Neste exemplo, na ocorrência de um alerta de IDS (IDSAlert), se o IP de origem que gerou o alerta não estiver na lista negra do firewall (BlackList !Contains SrcIpAddr), será adicionado (Add SrcIpAddr in BlackList). on IDSAlert if BlackList !Contains SrcIpAddr do Add SrcIpAddr in BlackList; Figura 3.5. Exemplo de reconfiguração. 1 A base de conhecimento pode ser utilizada pelo componente de análise e planejamento na adoção de estratégias para, por exemplo, não realizar ações anteriormente já feitas. Na Figura 3.5 não serão adicionados endereços IP na lista negra que já estiverem contidos. O componente de análise e planejamento terá como saída um plano de ação a ser executado pelo componente executor, que na Figura 3.5 é a adição do endereço IP de origem que gerou o alerta na lista negra. O executor irá aplicar as ações no elemento gerenciado através dos atuadores. Atuadores são responsáveis por fazerem alterações de configuração no elemento gerenciado, ou seja, em alguma aplicação de segurança da rede. O objetivo em realizar as mudanças nas configurações é aumentar o nível de segurança. No exemplo da Figura 3.5 o atuador irá interagir diretamente com a aplicação responsábel pela lista negra, o firewall. Além da arquitetura de sistemas oferecida pela CA, visando o estado em que se encontram os atuais sistemas de segurança e também o crescimento e evolução dos ataques às redes de computadores, é possível relacionar a necessidade que este tipo de sistema tem pelas propriedades dos sistemas autonômicos, destacando as propriedades de autoproteção (self-protecting), autocura (self-healing), autoconfiguração (self-configuring) e autoaprendizagem (self-learning) [30], como visto a seguir. 68 • Autoproteção: Refere-se à propriedade do sistema de defender-se de ataques acidentais ou maliciosos. Para tanto, o sistema deve ter conhecimento sobre potenciais ameaças, bem como provê mecanismos para tratá-las [5]. Para atingir essa propriedade o sistema deve ter habilidade para antecipar, detectar, identificar e proteger o elemento gerenciado contra ameaças; • Autocura: Responsável por identificar e corrigir erros ou falhas. No contexto da segurança de redes, o sistema autonômico deve ser capaz de detectar, diagnosticar e consertar problemas resultantes de ataques a ativos de produção da rede. Utilizando conhecimento sobre a configuração dos recursos da rede, o sistema deve possuir um componente de diagnóstico que deve analisar informações que mostram a ocorrência de falhas ou danos causados por um ataque à rede, para posteriormente buscar uma solução a ser tomada, aplicá-la e depois testar se foi satisfatória. Essa solução poderá ser, por exemplo, a substituição dinâmica de recursos por suas réplicas. É importante ressaltar que o processo de cura deve ser realizado com máxima transparência para usuários legítimos da rede; • Autoconfiguração: Nesta propriedade é fornecido a capacidade para os sistemas se configurarem e reconfigurarem automaticamente de acordo com políticas de negócio fornecidas por seus administradores, os quais definem o que deve ser feito e não como fazer [5]. A fim de automatizar o gerenciamento de configuração, um sistema de segurança deve possuir capacidade de reconfiguração dinâmica com o mínimo de intervenção humana; • Autoaprendizagem: Propriedade que fornece capacidade de aprendizado a partir de dados sensoriados e também através de experiências e resultados obtidos em ações anteriores. Propriedade fundamental para sistemas de segurança, visto que ela oferece a capacidade para o sistema aprender a se defender de ataques antes desconhecidos, ou, pelo menos, reconhecer tráfego malicioso na rede para posterior defesa. Um exemplo pode ser visto no trabalho desenvolvido em [32] (Seção 1.4.1.5), no qual é atualizado a base de assinaturas de um IDS quando são descobertos novos ataques. Mesmo diante de várias qualidades oferecidas pela CA, aplicá-la às redes de computadores e sua segurança não é um processo trivial, há desafios a serem enfrentados para alcançar o autogerenciamento. Segundo Agoulmine et al., em [1], o desafio é simplificar as tarefas de gerência para o administrador, automatizando o processo de decisão. Questões de segurança é outro desafio para o desenvolvimento de sistemas autonômicos, visto que o emprego de autoaprendizagem e autoevolução poderá ocasionar a perda do domínio humano sob a gerência das decisões tomadas pelo próprio sistema, havendo a possibillidade de desvio dos objetivos iniciais inseridos. Com isso, no processo de desenvolvimento de software autonômico a fase de validação deve ser aplicada de maneira rigorosa. 3.4.1. Sistemas de Segurança baseados em Computação Autonômica 1 Para dar uma visão com mais alto grau de realismo, serão vistos alguns trabalhos que utilizam a CA como base para o desenvolvimento de suas propostas ou implementam 69 alguma das propriedades oferecidas pela CA. Inicialmente será mostrado exemplos de sistemas que tem fundamentação na CA ou que fornecem algumas propriedades da CA. 3.4.1.1. ISDS 1 Em [40] foi desenvolvido o ISDS (Intelligent Security Defensive Software), um modelo de software de segurança baseado em CA. A estratégia do ISDS é fazer o processo de construção do software de segurança fornecendo a ele inteligência. Em outras palavras, construir um software utilizando o modelo ISDS é fazer com que seus componentes se modifiquem dinamicamente de acordo com a situação de segurança atual da rede. Para isto, o ISDS fornece consciência do contexto. O ISDS é um modelo de sistema de defesa distribuído caracterizado por ser flexível e autoadaptativo. Os domínios nos quais ele pode ser aplicado são principalmente no gerenciamento de segurança em redes locais e na gerência de segurança da informação na Internet. A ideia do modelo é que o sistema possa analisar as informações do ambiente e ajustar sua estrutura e maneira de agir dinamicamente. Ele consiste de alguns componentes básicos, são eles: componente de comando, componente de execução, componente sensor, componente de políticas, componente de equipamento e outros auxiliares como visto na Figura 3.6. Figura 3.6. Framework conceitual do ISDS [40]. 1 O componente de comando é a principal parte do ISDS, tendo como funções: a ativação dos componentes de políticas, sensor e equipamento, a execução da tomada de decião baseada no componente sensor, o gerenciamento do conjunto de componentes de segurança e política, atualização de novos componentes de segurança e políticas, verificando e resolvendo conflitos de políticas. Já o componente de execução trabalha como um canal de comunicação entre as entidades de segurança e o componente de comando. Ele se encarrega de filtrar, tratar e transmitir a mensagem para todos os outros componentes, os fazendo trabalharem corretamente. 70 O componente de políticas se encarrega principalmente em fazer a manutenção do conjunto de políticas, a tomada de decisão das políticas adequadas e também a passagem do resultado da decisão. As informações do resultado das decisões tomadas serão passadas para o componente de equipamento que então irá executar alguma ação. Por fim, o componente sensor coleta informações do ambiente e as envia para o componente de comando, levando em consideração o custo do sensoriamento de dois modos: emergência e timing, sendo o primeiro com maior prioridade em relação ao segundo. 3.4.1.2. Autonomic Security and Self-Protection based on Feature-Recognition with Virtual Neurons 1 No trabalho desenvolvido em [6] foi apresentado um mecanismo de segurança autonômico baseado em neurônios virtuais e no reconhecimento de características. Sua abordagem trabalha para detectar automaticamente vários problemas de segurança que atualmente são difíceis de realizar defesa. Através de simulação e diferentes estudos de caso, resultados mostram que esta solução é viável. Os neurônios virtuais são desenvolvidos de forma análoga aos neurônios dos animais, como visto na Figura 3.7. A ideia é que eles sejam distribuídos de maneira a perceber modificações no ambiente e reagir a estímulos. Esse neurônio virtual é na realidade um simples software com capacidade de ciência de contexto, a fim de analisar informações do contexto e estar ciente de eventuais surgimentos de ataques. Figura 3.7. Neurônio Virtual [6]. 1 No neurônio virtual, o componente Information Collector captura várias informações de contexto, tais como uso de memória e processador, status dos processos e informações de tráfego da rede. São considerados vizinhos os neurônios que se conectam diretamente, através do Neighbor Communicator. Existe um outro componente chamado de Feature Recognizer, seu funcionamento é baseado no conhecimento sobre infomações que caracterizam um ataque (baseado em assinaturas). Essas informações são passados para o Feature Recognizer pelo Information Collector do próprio neurônio e dos seus vizinhos, através do Neighbor Communicator. Os neurônios virtuais podem ser facilmente distribuídos, pois o pacote de instalação é compacto (tamanho pequeno), eles exigem poucos recursos computacionais e são de fácil instalação nos hosts. 71 Esses neurônios virtuais são distribuídos em uma arquitetura hierárquica e Par-apar (P2P - Peer-to-peer), como visto na Figura 3.8. A estrutura P2P opera baseada nas relações com neurônios vizinhos, tendo a comunicação entre eles através de passagem de mensagens. A arquitetura hierárquica é utilizada para aumentar a eficiência na propagação das mensagens por grupo de neurônios, chamados de células. Em cada célula um neurônio é eleito como neurônio-chefe, o algoritmo de eleição leva em consideração os recursos computacionais e velocidade de comunicação do host. Figura 3.8. Estrutura de organização hierarquica e P2P dos neurônios virtuais [6]. 1 Na organização hierárquica, um neurônio-chefe é eleito como o chefe de alto nível para outros neurônios-chefes. Este procedimento é repetido até existir um único neurôniochefe de maior nível sobre os demais. Se ocorrer alguma falha em algum neurônio-chefe de uma célula, um outro neurônio desta mesma célula irá assumir o papel de neurôniochefe, através de uma nova eleição. Nessa organização hierárquica as mensagens são entregues de maneira mais eficiente. O mecanismo de segurança empregado por este sistema distribuído é feito na detecção de mensagens com dados ou mensagens ilegais. Primeiramente as origens dos ataques são rastreadas para identificar e localizar o atacante. Depois o tráfego ilegal originado por ele é bloqueado, ou seja, todos os neurônios irão receber mensagens para descartarem tráfego que tenha como origem um usuário malicioso identificado. A reconfiguração de recursos é feita neste momento para realização da defesa pelo sistema. Para realizar a detecção e posterior defesa foi desenvolvido um mecanismo (característica ou assinatura) para cada tipo de ataque, dentre esses: Eavesdropping, Replay, Masquerading, Spoofing e Negação de Serviço (DoS - Denial of Service). 3.4.1.3. Self-Configuration of Network Security 1 Este trababalho [4] apresenta uma abordagem de autoconfiguração para controlar e gerenciar os mecanismos de segurança em redes de grande porte. Nela o sistema automaticamente configura os mecanismos de segurança e modifica as configurações dos recursos e suas políticas em tempo de execução. Para mostrar sua viabilidade foi implementado o AND (Autonomic Network Defense System). AND é um sistema que pode detectar ataques de rede conhecidos ou desconhecidos (ataques de último dia) e proativamente prevenir ou minimizar impactos causados em serviços da rede. 72 O AND foi desenvolvido sobre o framework AUTONOMIA [13], sendo uma extensão focada em estratégias e mecanismos para detectar e proteger-se de ataques de rede. O AUTONOMIA possui dois módulos de software, são eles: Component Management Interface (CMI) e Component Runtime Manager (CRM). O CMI é utilizado para especificar as configurações e políticas associadas com cada componente (de hardware ou software). E o CRM que gerencia as operações dos componentes usando as políticas definidas no CMI. Mais detalhes sobre o framework AUTONOMIA podem serem vistos em [14]. As principais extensões do AND são os módulos de Monitoramento Online (Online Monitoring), Seleção de Característica (Feature Selection), Análise de Anomalia (Anomaly Analysis) e o de Ação (Action Module), como visto na Figura 3.9. É possível observar que essa arquiteura é baseada no MAPE-K. Figura 3.9. Arquitetura do AND [4]. 1 O monitoramento online coleta os dados trafegados na rede e operações realizadas em computadores através de ferramentas específicas e arquivos de log gerados por sistemas operacionais. O modelo de seleção de característica irá filtrar os dados monitorados a fim de encontrar informações ou características relevantes para serem passadas para o módulo seguinte. No módulo de análise de anomalias é executada uma função para quantificar se existe um desvio do perfil padrão da rede ou de algum sistema, caso haja, é considerado uma anomalia e o módulo de ação irá executar ações adequadas. O módulo de ação irá, resumidamente, restringir o acesso aos recursos atacados e posteriosmente tentar desinstalar códigos maliciosos que estão executando em computadores comprometidos da rede. 3.4.1.4. Qualidade de Proteção 1 Trabalho iniciado em [10] teve como resultado um novo termo em [12] chamado de Qualidade de Proteção (QoP - Quality-of-Protection). Ele é um framework proativo para defesa de redes que pode ser integrado com os já existentes protocolos de Qualidade de Serviço (QoS - Quality-of-Service). O objetivo é provê serviços diferenciados para fluxos de tráfegos de acordo com seu grau de anormalidade. Para isso foi criado uma nova métrica chamada de Distância de Anormalidade (DA), como visto na Figura 3.10, que pode ser usada para classificar o tráfego em normal, incerto (tráfego suspeito) e anormal (tráfego malicioso). Existe uma função Delta 73 que mostra o quanto mais próximo o tráfego está de normal ou anormal, podendo ser classificado então como provavelmente normal ou provavelmente anormal. Figura 3.10. Distância de Anormalidade [10]. 1 A ideia é que a métrica AD seja usada em conjunto com protocolos de QoS para aumentar a prioridade de tráfegos considerados normais e diminuir a prioridade de tráfegos anormais. Testes foram realizados com ataques de DDoS e propagação de Worms. Com isso foi possível demonstrar o quanto a abordagem proposta pode detectar e reduzir o impacto causado pelos ataques. 3.4.1.5. Propriedades de Computação Autonômica em Sistemas de Segurança 1 Alguns trabalhos se cacterizam por fornecer alguma(s) das propriedades autonômicas de maneira isolada, mesmo não tendo como base a CA. Ou seja, foram desenvolvidos sem seguir os conceitos da CA, mas provem algum mecanismo que se qualifica dentre alguma das propriedades autonômicas. Sistemas como estes não são considerados autonômicos. Foi desenvolvido em [39] um esquema de segurança para atualização de regras de firewall baseado no tráfego de uma honeynet (autoconfiguração e autoaprendizagem). Para [38] honeynets são múltiplos honeypots juntamente com um firewall para limitação e log de tráfego. No esquema há um módulo de análise de dados que oportunamente descobre novos ataques. Este módulo analisa o tráfego gerado pelos logs da honeynet e com a utilização de mineração de dados ele cria dinamicamente novas regras de firewall passando-as para o módulo de aprendizagem de regras, que as filtra eliminando incoerências entre as regras e, por fim, ele as aplica. Dessa maneira o firewall continua enriquecendo suas estratégias melhorando sua capacidade para se defender de novos ataques, inclusive ataques de último dia. A ferramenta Honeycomb [21] tem como objetivo automatizar a geração de assinaturas de ataques para sistemas de detecção de intrusão a partir de logs gerados por honeypots (autoaprendizagem). Na verdade esta ferramenta trata-se de um plugin a ser utilizado junto ao honeyd [34], que é um framework de honeypots de baixa interatividade [38]. A implementação do Honeycomb gera assinaturas no formato Bro [29] e Snort [36]. Sua intenção é ser utilizado para criar as assinaturas de ataques em tempo de execução 74 (autoconfiguração), atividade que normalmente é realizada manualmente por especialistas de segurança após o registro das atividades maliciosas nos logs. No caso do SweetBait [32], foi desenvolvido um sistema automatizado de proteção que utiliza honeypots de baixa e alta interatividade para reconhecer e capturar tráfego malicioso. Com base nos logs gerados, após descartar padrões da lista branca, ele automaticamente cria assinaturas de ataques (autoaprendizagem), componente implementado utilizando o Honeycomb. Para provê um baixo tempo de resposta ao ataque ele distribui imediatamente assinaturas para IDS’s (autoconfiguração), após sua geração. Paralelamente a isto, as assinaturas são refinadas continuamente a fim de aumentar sua precisão e diminuir falsos positivos (auto-otimização). Este trabalho é extendido e surge então o Argos [33] que emprega uma característica mais ampla, a propriedade de autoproteção, por reagir proativamente contra ataques. 3.5. Conclusão 1 Em 2001 a IBM produziu um manifesto no qual alertou a dificuldade de gerenciamento dos sistemas computacionais atuais e apontou a autonomia dos sistemas como a alternativa para a solução deste problema. Neste capítulo foi apresentado a definição e as principais características de uma nova abordagem para o desenvolvimento de sistemas que provem autonomia, a Computação Autonômica. Na qual envolve uma mudança no modo de projetar sistemas computacionais. A ideia por traz dessa abordagem é desenvolver software autogerenciável, tendo pouca ou nenhuma intervenção humana para manter-se, baseado apenas em políticas de alto nível definidas pelo supervisor e no conhecimento adquirido ao longo do tempo. Uma série de propriedades autonômicas são utilizadas por esta abordagem para diminuir ou eliminar a intervenção humana sob a gerência dos sistemas computacionais, tais como: autocura, autoproteção, auto-otimização, autoaprendizagem, autoconfiguração, etc. Neste sentido será colocado sob responsabilidade das próprias máquinas a tarefa de gerenciamento. Foi mostrado também neste capítulo que as redes de computadores são cenários onde a CA pode ser facilmente aplicada, principalmente pelo crescimento resultante da Internet. Em particular, apresentou-se a aplicabilidade de CA em um ambiente bem específico, a gerência da segurança de redes. Para isto foi explicitado a necessidade da segurança de redes por mecanismos autonômicos e maneiras de como é possível implementálos. Trabalhos já realizados dentro da temática foram descritos a fim de oferecer uma visão mais prática. Finalizando, pesquisas mostram que a direção correta para as aplicações de segurança é a adoção de integração e inteligência, então isso mostra que os recursos fornecidos pela CA é o caminho mais viável para solucionar problemas existentes nas redes de computadores e de maneira mais específica, como visto, na segurança destas. Agradecimento: O desenvolvimento deste trabalho foi viabilizado pelo apoio financeiro concedido pela Fundação de Amparo à Pesquisa e ao Desenvolvimento Científico e Tecnológico do Maranhão (FAPEMA). 75 Referências [1] Agoulmine, N., Balasubramaniam, S., Botvich, D., Strassner, J., Lehtihet, E. e Donnelly, W. (2006), “Challenges for Autonomic Network Management”, In 1st IEEE International Workshop on Modelling Autonomic Communications Environments MACE. [2] Atay, S. e Masera, M. (2009), “Challenges for the security analysis of Next Generation Networks”, In Proceedings of the Sixth International Conference on Broadband Communications, Networks, and Systems - BROADNETS. [3] Braga, T. R. M., Silva, F. A., Ruiz, L. B., and ao, H. P. A. (2006), “Redes Autonômicas”, In XXIV Simpósio Brasileiro de Redes de Computadores e Sistemas Distribuídos - SBRC, Sociedade Brasileira de Computação (SBC). [4] Chen, H., Al-Nashif, Youssif B., Qu, G. e Hariri, S. (2007), “Self-Configuration of Network Security”, In Proceedings of the 11th IEEE International Enterprise Distributed Object Computing Conference, p. 97-108. [5] Corrêa, S. e Cerqueira, R. (2009), “Computação Autônoma: Conceitos, Infraestruturas e Soluções em Sistemas Distribuídos”, In XXVII Simpósio Brasileiro de Redes de Computadores e Sistemas Distribuídos - SBRC, Sociedade Brasileira de Computação (SBC). [6] Dai, Y., Hinchey, M., Qi, M. e Zou, X. (2006), “Autonomic Security and SelfProtection based on Feature-Recognition with Virtual Neurons”, In Proceedings of the 2nd IEEE International Symposium on Dependable, Autonomic and Secure Computing. [7] Feily, M., Shahrestani, A. e Ramadass, S. (2009), “A Survey of Botnet and Botnet Detection”, In Third International Conference on Emerging Security Information, Systems and Technologies. [8] Gonçalves, J. F. (2010), “Introdução à Computação Autonômica e sua Aplicação em Ambientes Computacionais Distribuídos”, Graduate thesis, Universidade Federal do Maranhão, Bacharelado em Ciência da Computação. [9] Goodloe, A. e Pike, L. (2010), “Monitoring distributed real-time systems: A survey and future directions”, NASA Langley Research Center. [10] Guangzhi Qu, Hariri, S., Jangiti, S., Rudraraju, J., Seungchan Oh, Fayssal, S., Guangsen Z. e Parashar, M. (2004), “Online Monitoring and Analysis for SelfProtection against Network Attacks”, In Proceedings of the International Conference on Autonomic Computing, p. 324-325. [11] Hallsteinsen, S. (2010), “Madam-theory of adaptation”, Technical report, SINTEF ICT. [12] Hariri, S., Guangzhi Qu, Modukuri, R., Chen, H. e Yousif, M. (2005), “Quality-ofProtection (QoP) - An Online Monitoring and Self-Protection Mechanism”, IEEE Journal on Selected Areas in Communications, v. 23, n. 10, p. 1983-1993. 76 [13] Hariri, S., Khargharia, B., Chen, H., Yang, J., Zhang, Y., Parashar, M. e Liu, H. (2006), “The autonomic computing paradigm”, Cluster Computing, Springer, v. 9, p. 5-17. [14] Hariri S., Xue L., Chen, H., Zhang, M., Pavuluri, S. e Rao, S. (2003), “AUTONOMIA: an autonomic computing environment”, In Proceedings of the IEEE International Performance, Computing, and Communications Conference, p. 61-68. [15] Hongli, Z. e Qassrawi, Mahmound T. (2010), “Deception Methodology in Virtual Honeypots”, In Second International Conference on Networks Security, Wireless Communications and Trusted Computing, Wuhan, China, p. 462-467. [16] Huebscher, Markus C. e McCann, Julie A. (2008), “A survey of autonomic computing - degrees, models, and applications”, ACM Computing Surveys, v. 40, n. 3. [17] IBM (2003), “An architectural blueprint for autonomic computing”, IBM Press Prentice-Hall. [18] Kabiri, P. e Ghorbani, Ali A. (2005), “Research on Intrusion Detection and Response: A Survey”, International Journal of Network Security, v. 2, n. 1, p. 84-102. [19] Kephart, J.O. e Chess, D.M. (2003), “The Vision of autonomic computing”, Computer, IEEE Computer Society, p. 41-50. [20] Krause, M. and Tipton, H. F. (1999), “Handbook of Information Security Management”, Auerbach Publications, http://www.cccure.org/Documents/HISM/. [21] Kreibich, C. e Crowcroft, J. (2004), “Honeycomb - Creating Intrusion Detection Signatures Using Honeypots”, SIGCOMM Computer Communication Review, ACM, v. 34, ed. 1. [22] Léger, M., Ledoux, T. e Coupaye, T. (2007), “Reliable dynamic reconfigurations in the fractal component model”, In Proceedings of the 6th international workshop on adaptive and reflective middleware: held at the ACM/IFIP/USENIX International Middleware Conference - ARM, New York, NY, USA, p. 1-6. [23] Mansouri-samani, M. (1995), “Monitoring of Distributed Systems”, PhD thesis, University of London, Imperial College of Science, Technology and Medicine. [24] McKinley, P. K., Sadjadi, S. M., Kasten, E. P. e Cheng, B. H. C. (2004), “Composing adaptive software”, IEEE Computer Society, p. 56-64. [25] Murch, R. (2004), “Autonomic computing”, IBM Press Prentice-Hall. [26] Palma, N. D., Laumay, D. P. e Bellissard, L. (2001), “Ensuring dynamic reconfiguration consistency”, In 6th International Workshop on Component-Oriented Programming - WCOP, p. 18-24. [27] Parashar, M. e Hariri, S. (2005), “Autonomic computing: An overview”, Unconventional Programming Paradigms, Springer Verlag, p. 247-259. 77 [28] Parashar, M. e Hariri, S. (2007), “Autonomic computing: concepts, infrastructure, and applications”, CRC Press. [29] Paxson, V. (1999), “Bro: A System for Detecting Network Intruders in Real-Time”, Computer Networks, v. 31, n. 23-24, p. 2435-2463. [30] Poslad, S. (2009), “Autonomous Systems and Artificial Life”, Ubiquitous Computing: Smart Devices, Environments and Interactions, John Wiley & Sons, Ltd., p. 317-341. [31] Pilli, E. S., Joshi, R.C. e Niyogi, R. (2010), “Network forensic frameworks: Survey and research challenges”, Digital Investigation, Elsevier, p. 1-14. [32] Portokalidis, G. e Bos, H. (2007), “SweetBait: Zero-hour worm detection and containment using low- and high-interaction honeypots”, Computer Networks, Elsevier, v. 51, ed. 5, p. 1256-1274. [33] Portokalidis, G., Slowinska, A. e Bos, H. (2006), “Argos: an Emulator for Fingerprinting Zero-Day Attacks for advertised honeypots with automatic signature generation”, In Proceedings of the EuroSys, p. 15-27. [34] Provos, N. (2004), “A virtual honeypot framework”, In Proceedings of the 13th conference on USENIX Security Symposium, v. 13, Berkeley, CA, USA. [35] Rajab, M., Zarfoss, J., Monrose, F. e Terzis, A. (2006), “A multifaceted approach to understanding the botnet phenomenon”, In Proceedings of the 6th ACM SIGCOMM Conference on Internet Measurement - IMC, p. 41-52. [36] Roesch, M. (1999), “Snort: Lightweight Intrusion Detection for Networks”, In Proceedings of the 13th Conference on Systems Administration, p. 229-238. [37] Song, J., Kwon, Y. e Takakura, H. (2008), “ A Generalized Feature Extraction Scheme to Detect 0-Day Attacks via IDS Alerts”, In International Symposium on Applications and the Internet - SAINT, Turku, Finlândia. [38] Spitzner, L. (2002), “Honeypots: Definitions and Value of Honeypots”, SANS Annual Conference. [39] Wang, B., Zhu, P., Wen, Q. e Yu, X. (2009), “A Honeynet-based Firewall Scheme with Initiative Security Strategies”, In International Symposium on Computer Network and Multimedia Technology - CNMT, p. 1-4. [40] Wang, K., Wang, J., Shen, L. e Han, Z. (2010), “An Intelligent Security Defensive Software Scheme and Realization”, In Third International Symposium on Intelligent Information Technology and Security Informatics - IITSI, p. 793-796. [41] Zseby, T. Pfeffer, H. e Steglich, S. (2009), “Concepts for Self-Protection”, Autonomic Computing and Networking, Springer Science, p. 355-380. 78 Capítulo 4 Linked Data: da Web de Documentos para a Web de Dados Danusa R. B. Cunha, Bernadette F. Lóscio, Damires Souza Abstract The term Linked Data refers to a set of best practices for publishing and connecting structured data on the Web with the purpose of creating a Web of Data. Recently, a significant number of datasets have been published adhering to the Linked Data principles and numerous efforts are underway to build applications for exploit this Web of Data. In this context, during this course, we present the fundamental concepts of Linked Data, as well as the theoretical basis of how to publish and to consume data available on the Web of Data. We also present some applications for visualization and consume of Linked Data and we discuss the main difficulties and challenges in this research area. Resumo O termo Linked Data refere-se a um conjunto de práticas para publicar e conectar dados estruturados na Web, com o intuito de criar uma “Web de Dados”. Recentemente, um grande volume de dados definidos de acordo com o padrão Linked Data tem sido publicado, com isso, muitos esforços estão sendo feitos para construir aplicações com o objetivo de facilitar a exploração de tais dados. Neste cenário, este minicurso apresenta os conceitos relacionados ao termo Linked Data, bem como provê aos participantes um embasamento prático de como publicar e consumir dados na Web de Dados. Além disso, são apresentadas as aplicações usadas para visualização e consumo dos dados Linked Data e, por fim, discute as principais dificuldades e desafios nessa área de pesquisa. 79 4.1 Introdução A Internet contemporânea, nos moldes da World Wide Web, vive um constante processo de evolução e tem revolucionado a forma como criamos conteúdo e trocamos informações. A Web organiza as informações disponíveis na Internet por meio de hipertexto e torna a interação do usuário com a rede mundial mais amigável. Com isso, possibilita um ambiente de compartilhamento de documentos oriundos de diversas áreas do conhecimento. Entretanto, tais conteúdos geralmente seguem regras apenas sintáticas, com objetivos de apresentação, não permitindo que se consiga facilmente extrair semântica dos mesmos, sem que para isso seja feito um grande esforço de implementação. Considerando isso, a Web atual pode ser classificada como sintática e o processo de interpretação dos conteúdos disponibilizados fica geralmente a cargo dos usuários [Costa & Yamate 2009]. Em sua maior parte, os dados na Web ainda são organizados para serem lidos ou compreendidos por humanos e não por agentes de software. Para que um agente de software possa entender e interpretar um dado, é necessário processar a semântica envolvida naquele dado, num determinado contexto. Neste escopo, semântica diz respeito à atribuição de significado a elementos, dados ou expressões que precisam ser interpretados numa dada situação [Souza 2009]. No cenário da Web, isso representa atribuir significado aos dados interligando-os com outros conjuntos de dados ou outros domínios de conhecimento, conseguindo, assim, criar uma relação de significância entre os conteúdos publicados na Internet de modo que seja perceptível tanto pelo usuário quanto pelos agentes de software. Essa nova visão da Web vem sendo denominada de Web Semântica (Semantic Web) [Lee et al. 2001]. A Web Semântica é considerada uma extensão da Web atual cujo objetivo principal é facilitar a interpretação e integração dos dados na Web. Como parte do desenvolvimento da Web Semântica, surgiu o conceito de Linked Data (dados ligados) que pode ser definido como um conjunto de boas práticas para publicar e conectar conjuntos de dados estruturados na Web, com o intuito de criar uma “Web de Dados” [Bizer et al. 2009]. Estas práticas são fundamentadas em tecnologias Web, como HTTP (Hypertext Transfer Protocol) e URI (Uniform Resource Identifier), com o objetivo de permitir a leitura dos dados conectados semanticamente, de forma automática, por agentes de software. A Web de Dados cria inúmeras oportunidades para a integração semântica de dados, motivando o desenvolvimento de novos tipos de aplicações e ferramentas, como navegadores e motores de busca. Este capítulo apresenta os conceitos básicos para publicação e consumo de dados na Web de acordo com os padrões de Linked Data e está organizado como segue. A seção 2 traça um paralelo entre a Web clássica e a Web de Dados. A Seção 3 apresenta uma visão geral sobre o tema Linked Data, introduzindo os princípios que regem esse conjunto de práticas. Além disso, são explicados os seguintes aspectos: (i) a nomeação de recursos através de URIs; (ii) como é possível utilizar URIs para prover navegação entre os recursos; (iii) como disponibilizar os dados através do modelo RDF, mostrando os benefícios oriundos desse modelo e (iv) como links entre os recursos são criados e identificados. A Seção 4 discute os aspectos básicos que devem ser considerados para a publicação de dados segundo os 80 princípios do Linked Data. Para tal, aborda questões de formatação e estruturação de dados e, por fim, apresenta um conjunto de padrões e diretrizes a serem seguidos com o intuito de tornar possível a publicação e interligação dos dados. A Seção 5 apresenta exemplos de aplicações existentes e, finalmente, a Seção 6 tece considerações sobre o tema exposto, apontando benefícios de seu uso e dificuldades ainda existentes. 4.2. Web de Documentos versus Web de Dados Para um melhor entendimento sobre a Web de Dados, pode-se estabelecer um paralelo entre a Web de Documentos (a Web atual) e a Web de Dados. A primeira faz uso de navegadores HTML (HyperText Markup Language) para acessar dados na enquanto que na segunda os dados são acessados a partir de navegadores RDF. Na Web de Documentos hiperlinks são usados para navegar entre as páginas, enquanto que na Web de Dados os links RDF são usados para acessar dados de diversas fontes. A Web de Documentos é baseada em um conjunto de padrões, incluindo: um mecanismo de identificação global e único, as URIs; um mecanismo de acesso universal, o HTTP e um formato padrão para representação de conteúdo, o HTML. De modo semelhante, a Web de Dados tem por base alguns padrões, como: o mesmo mecanismo de identificação e acesso universal usado na Web de documentos (as URIs e o HTTP); um modelo padrão para representação de dados, o RDF e uma linguagem de consulta para acesso aos dados, a linguagem SPARQL. A seguir esses padrões são apresentados. URIs – Uniform Resource Identifiers URI é uma cadeia de caracteres compacta usada para identificar ou denominar um recurso na Internet, onde um recurso pode ser um documento html, uma figura ou uma pessoa. O principal propósito desta identificação é permitir a interação com representações do recurso através de uma rede, tipicamente a Web, usando protocolos específicos. Uma URI pode ser classificado como um localizador URL (Uniform Resource Locator) ou um nome URN (Uniform Resource Name), ou ainda como ambos. O URN define a identidade de um item, enquanto que o URL nos dá um método para encontrá-lo. Tecnicamente URL e URN funcionam como IDs de recursos, no entanto, muitos conjuntos não podem ser categorizados como somente um ou outro, por que todas as URIs podem ser tratados como nomes, e alguns conjuntos integram aspectos de ambas ou de nenhuma das categorias. No contexto Linked Data, URIs são usadas para identificar objetos e conceitos, permitindo que eles sejam dereferenciados para obtenção de informações a seu respeito. Assim, o dereferenciamento de uma URI resulta em uma descrição RDF do recurso identificado. Por exemplo, a URI http://www.w3.org/People/Berners-Lee/card#i identifica o pesquisador Tim Bernes-Lee. HTTP – HyperText Transfer Protocol HTTP é um protocolo de aplicação responsável pelo tratamento de pedidos e respostas entre cliente e servidor na Web. Ele surgiu da necessidade de distribuir informações pela Internet e, para que essa distribuição fosse possível, foi 81 necessário criar uma forma padronizada de comunicação entre os clientes e os servidores da Web. Com isso, o protocolo HTTP passou a ser utilizado para a comunicação entre computadores na Internet e a especificar como seriam realizadas as transações entre clientes e servidores, através do uso de regras básicas. O HTTP utiliza o modelo cliente-servidor, como a maioria dos protocolos de rede, baseando-se no paradigma de requisição e resposta. Um programa requisitante (cliente) estabelece uma conexão com um outro programa receptor (servidor) e envia-lhe uma requisição, contendo a URI, a versão do protocolo, uma mensagem contendo os modificadores da requisição, informações sobre o cliente e, possivelmente, o conteúdo no corpo da mensagem. O servidor responde com uma linha de status (status line) incluindo sua versão de protocolo e com os códigos de erro informando se a operação foi bem sucedida ou não, seguido pelas informações do servidor, informações da entidade e possível conteúdo no corpo da mensagem. Após o envio da resposta pelo servidor, encerra-se a conexão estabelecida. RDF – Resource Description Framework A utilização de um modelo padrão para representação de dados, como o RDF, torna possível a implementação de aplicações genéricas capazes de operar sobre o espaço de dados global [Heath & Bizer 2011]. O modelo RDF é baseado no conceito de grafo, é extensível e possue um alto nível de expressividade, facilitando, dessa forma, a interligação entre dados de diferentes fontes. Em RDF, um recurso pode estar relacionado com dados ou com outros recursos através das sentenças, as quais são estruturadas no formato sujeito + predicado + objeto, onde: Sujeito: Tem como valor o recurso sobre o qual se quer escrever uma sentença. Todo recurso deve ser capaz de ser identificado unicamente. Predicado: Especifica um relacionamento entre um sujeito e um objeto. O predicado é especificado por meio de propriedades, que são relações binárias geralmente nomeadas por um verbo e permitem relacionar um recurso a dados ou a outros recursos. Uma propriedade também é um recurso e, portanto, deve ter um identificador único. Objeto: Denomina o recurso ou dado que se relaciona com o sujeito. O valor de um objeto pode ser um recurso ou um literal, que pode ser um valor numérico ou uma cadeia de caracteres. A Figura 4.1 mostra alguns exemplos de triplas RDF. Figura 4.1. Triplas RDF. 82 As triplas representadas na Figura 4.1 contêm informações sobre dois recursos. A primeira tripla informa que o recurso “p91002043177” possui nome Berna Farias. A segunda tripla descreve um segundo recurso “CK120”, cujo nome é “Banco de Dados I”. A terceira tripla descreve um relacionamento entre os dois recursos criados através do predicado “EnsinadoPor”. Cada tripla faz parte da Web de Dados e pode ser usada como ponto de partida para explorar esse espaço de dados. Triplas de diferentes fontes podem ser facilmente combinadas para formar um único grafo. Além disso, é possível usar termos de diferentes vocabulários para representar os dados. O modelo RDF ainda permite a representação de dados em diferentes níveis de estruturação, sendo possível representar desde dados semiestruturados a dados altamente estruturados. SPARQL Assim como os sistemas de bancos de dados relacionais fazem uso do SQL para consultar registros nas suas bases de dados, SPARQL é a linguagem de consulta padrão recomendada pelo W3C para recuperação de informações contidas em grafos RDF. Apesar de existirem outras linguagens de consulta (SeRQL, RQL, etc..) mais antigas, maduras e com maior poder de expressividade que o SPARQL, estas ou foram projetadas para se trabalhar em um domínio específico ou são interpretadas apenas por algumas poucas ferramentas, o que acaba resultando em uma baixa interoperabilidade. onde: Semelhante ao SQL, o SPARQL possui uma estrutura Select-From-Where • Select: Especifica uma projeção sobre os dados como a ordem e a quantidade de atributos e/ou instâncias que serão retornados. • From: Declara as fontes que serão consultadas. Esta cláusula é opcional. Quando não especificada, assumimos que a busca será feita em um documento RDF/RDFS particular. • Where: Impões restrições na consulta. Os registros retornados pela consulta deverão satisfazer as restrições impostas por esta cláusula. O resultado de uma consulta SPARQL pode ser encarado como um subgrafo resultante da execução da consulta sobre o grafo que representa o modelo. Considere por exemplo o grafo apresentado na Figura 4.2 extraído de [Allemang & Hendler 2008]. 83 Figura 4.2. Representação das instâncias de um domínio O grafo da Figura 4.2 representa a relação entre as instâncias de uma ontologia cujo domínio é focado na descrição e formalização de escritores. O subgrafo destacado em negrito pode ser encarado, por exemplo, como o resultado de uma consulta que retorna o escritor que escreveu o livro King Lear e é casado com AnneHathaway. 4.3. Princípios Linked Data O termo Linked Data refere-se ao conjunto de melhores práticas para a publicação de dados estruturados na Web. Essas práticas foram introduzidas por Tim BernersLee em [Lee et al 2006] e resumem-se em quatro princípios básicos: 1.Usar URIs como nome para recursos. 2.Usar URIs HTTP para que as pessoas possam encontrar esses nomes. 3.Quando alguém procura por uma URI, garantir que informações úteis possam ser obtidas por meio dessa URI, as quais devem estar representadas no formato RDF. 4.Incluir links para outras URIs de forma que outros recursos possam ser descobertos. O primeiro princípio defende o uso de referências URI para identificar, não apenas documentos Web e conteúdos digitais, mas também objetos do mundo real e conceitos abstratos. 84 O segundo princípio defende o uso de URIs HTTP para identificar os objetos e os conceitos abstratos definidos pelo princípio 1, possibilitando essas URIs serem dereferenciáveis sobre um protocolo HTTP. Neste contexto, dereferenciar é o processo de recuperar uma representação de um recurso identificado por uma URI, onde um recurso pode ter várias representações como documentos HTML, RDF, XML entre outros. A fim de permitir que uma ampla gama de aplicações diferentes possam processar dados disponíveis na Web, é importante que exista um acordo sobre o formato padrão para disponibilização dos dados. O terceiro princípio Linked Data defende o uso de RDF como modelo para a publicação de dados estruturados na Web [Klyne et al. 2004]. Com o RDF, é possível descrever significado sobre recursos, habilitando agentes de software a explorar os dados de forma automática, muitas vezes, agregando, interpretando ou mesclando dados. O quarto princípio diz respeito ao uso de hiperlinks para conectar não apenas os documentos da Web, mas qualquer tipo de recurso. Por exemplo, uma ligação pode ser fixada entre uma pessoa e um lugar, ou entre um local e uma empresa. Em contraste com a Web clássica onde os hiperlinks são em grande parte não tipados, hiperlinks que conectam os recursos em um contexto de Linked Data são capazes de descrever a relação entre eles. Hyperlinks no contexto de Linked Data são chamados de links RDF, a fim de distingui-los dos hiperlinks existentes na Web convencional [Heath & Bizer 2011]. O exemplo mais visível da adoção e aplicação dos princípios Linked Data tem sido o projeto Linking Open Data1 fundado em janeiro de 2007 e apoiado pelo W3C Semantic Web Education and Outreach Group2. O objetivo principal desse projeto é identificar conjuntos de dados disponíveis sob licenças abertas e convertêlos para RDF de acordo com os princípios Linked Data. Os participantes nas fases iniciais do projeto foram os pesquisadores e desenvolvedores de laboratórios universitários e empresas de pequeno porte. Desde então, o projeto tem crescido consideravelmente, conseguindo um envolvimento significativo de grandes organizações como a BBC3. Este crescimento é possível graças à natureza aberta do projeto, onde qualquer um pode participar, sendo necessário apenas publicar um conjunto de dados de acordo com os princípios Linked Data e interligá-lo aos conjuntos de dados já existentes. Na Figura 4.3 podem-se observar os diversos conjuntos de dados publicados no contexto do projeto Linking Open Data. 1 http://www.w3.org/wiki/SweoIG/TaskForces/CommunityProjects/LinkingOpenData http://www.w3.org/2001/sw/sweo/ 3 http://www.bbc.co.uk/ 2 85 Figura 4.3. Visão geral de conjuntos de dados publicados e seus relacionamentos em Setembro de 2011. No grafo da Figura 4.3, cada nó representa um conjunto de dados publicado seguindo os princípios Linked Data, os quais estão interligados com outros conjuntos de dados na nuvem. O tamanho de cada nó corresponde ao número de triplas RDF do conjunto de dados. As setas indicam a existência de pelo menos 50 ligações entre dois conjuntos, podendo ser unidirecionais, indicando que um certo conjunto contem triplas RDF de um outro conjunto, ou bidirecionais, indicando que ambos os conjuntos contem triplas RDF um do outro. O conteúdo da nuvem possui natureza diversa, incluindo dados sobre localizações geográficas, empresas, livros, publicações científicas, filmes, música, televisão e rádio, ensaios clínicos, comunidades online, dados estatísticos entre outros [Heath & Bizer 2011]. 4.4. Diretrizes para Publicação de dados em Linked Data De acordo com [Heath & Bizer 2011], a adoção das melhores práticas de publicação de dados ligados facilita a descoberta de informações relevantes para a integração de dados entre diferentes fontes. A seguir são apresentados alguns passos para publicar dados na Web de Dados, como definido em [Heath & Bizer 2011]. 4.4.1. Criação de URIs adequadas Como mencionado anteriormente, recursos são nomeados a partir de URIs. Dessa forma, ao publicar dados Linked Data é preciso fazer um esforço para criar boas URIs para identificação dos recursos. Para isso, devem ser feitas algumas considerações, incluindo: Usar URIs HTTP para tudo, tornando-as passíveis de serem dereferenciadas. Evitar URIs com detalhes de implementação ou do ambiente em que estão publicadas. Por exemplo, a URI http://www.lia.ufc.br:8080/~danusarbc 86 /index.php é um exemplo do que deve ser evitado, pois possui detalhes da porta 8080 usada em seu ambiente de publicação e do script implementado em PHP necessário para sua execução. Manter as URIs estáveis e persistentes, pois a alteração das URIs pode levar à quebra de links já estabelecidos, criando um problema para a localização de recursos. Para evitar esse tipo de alteração, recomenda-se um planejamento das URIs que serão usadas e também que o responsável pela publicação detenha a propriedade do espaço de nomes Muitas vezes será preciso usar algum tipo de chave primária dentro das URIs, para se certificar de que cada uma delas é única. Se possível, usar uma chave que é significativa dentro do domínio para o qual está sendo criada a URI. Por exemplo, quando se trata de livros, pode-se usar o ISBN como identificador único. Essas são apenas algumas características desejáveis para serem consideradas quando se for criar uma URI. Maiores detalhes podem ser encontrados no tutorial Cool URIs for the Semantic Web4 disponibilizado pela W3C. 4.4.2. Usar URIs dereferenciáveis Qualquer URI HTTP deve ser capaz de ser dereferenciada, o que significa que clientes HTTP podem procurar por uma URI usando um protocolo HTTP e recuperar uma descrição do recurso que é identificado pela URI. A recuperação da representação mais adequada para o usuário é feita por meio da negociação de conteúdo. Assim, clientes HTTP enviam, por meio dos cabeçalhos HTTP (Get, Head, Post, Put, Delete, Trace, Options, Connection), o pedido para indicar qual tipo de conteúdo deseja obter. Após isso, ao receber uma requisição, o servidor analisa o cabeçalho e seleciona a resposta adequada. Há duas estratégias para fazer URIs serem dereferenciáveis: 303 URI e Hash URI. 303 URI: 303 é um código de status de redirecionamento no qual o servidor pode dar a localização de um documento que contém informações sobre um recurso. Por exemplo, a Figura 4.4 mostra como ocorre o dereferenciamento quando um usuário deseja obter informações sobre a cidade de Berlin da fonte DBpedia5. Primeiramente, em 1, é especificado que o conteúdo desejado é no formato RDF/XML através do comando Accept: text/html;q=0.5, application/rdf+xml. Após isso, em 2, o servidor enviará para o cliente uma URI http://dbpedia.org/data/Berlin, mostrando a localização exata do conteúdo requerido e o código 303 See Other, indicando que a requisição será redirecionada para essa URI. Com essa URI em mãos, em 3, o cliente reenvia sua solicitação e, por fim, em 4, recebe do servidor um arquivo RFD com o conteúdo conforme solicitado. 4 5 http://www.w3.org/TR/cooluris/ http://dbpedia.org/ 87 Figura 4.4. Dereferenciamento de URI utilizando a estratégia 303 URI. Hash URI: Nessa estratégia, a URI contém um fragmento, uma parte especial que é separada do resto da URI pelo símbolo #. Quando um cliente deseja recuperar uma hash URI, ele remove tudo que vem após o símbolo # e envia o restante da URI para o servidor. Como resposta, o cliente recebe um documento completo com o conteúdo solicitado. Para um melhor entendimento, a Figura 4.5 mostra como ocorre o dereferenciamento utilizando Hash URI para o seguinte exemplo: suponha um arquivo que contém um vocabulário definido para uma empresa fictícia chamada Small and Medium-sized Enterprises representado pela seguinte URI: http://biglynx.co.uk/vocab/sme. As seguintes URIs foram criados para identificar termos distintos que podem ser encontrados no documentos previamente criado: http://biglynx.co.uk/vocab/sme#Small MediumEnterprise e http://biglynx.co.uk/vocab/sme#Team. Para dereferenciar qualquer uma dessas duas URIs, o cliente removerá o fragmento especial (#Team por exemplo) e então enviará sua requisição para o servidor, especificando que o conteúdo desejado é no formato RDF/XML através do comando Accept: application/rdf+xml conforme visto em 1. Em seguida, o servidor retornará como resposta, em 2, um documento contendo todo o conteúdo expresso em http://biglynx.co.uk/vocab/sme. 88 Figura 4.5. Dereferenciamento de URI utilizando a estratégia Hash URI. 4.4.3. Criação de links RDF De modo a permitir a navegação entre as fontes de dados, devem ser criados links para outras fontes, seja de forma manual ou automatizada. Links no contexto de Linked Data fazem referência aos de links RDF. Links RDF podem ser classificados em dois tipos: links RDF internos e externos. O link RDF interno conecta recursos dentro de uma única fonte de dados Linked Data. Links externos conectam recursos os quais são provenientes de diferentes fontes de dados Linked Data. No caso de links externos, as URIs referentes ao sujeito e predicado do link pertencem a diferentes namespaces (os quais são URIs que referenciam o esquema do qual o elemento faz parte). Links externos são cruciais para a Web dos Dados visto que eles permitem juntar as fontes de dados dispersas em um espaço global de dados [Heath & Bizer 2011]. A Figura 4.6 apresenta dois exemplos de links RDF. O primeiro exemplo interliga o perfil FOAF6 do pesquisador Tim Berners-Lee localizado em um arquivo RDF ao recurso que o identifica na fonte de dados do DBLP7. No segundo exemplo, o recurso que identifica Tim Berners-Lee na fonte DBpedia8 também é ligado ao recurso na fonte DBLP que o identifica. A propriedade http://www.w3.org/2002/07/owl#sameAs define que os recursos interligados representam a mesma entidade do mundo real. Sujeito: http://www.w3.org/People/Berners-Lee/card#i 6 http://www.foaf-project.org/ http://www.informatik.uni-trier.de/~ley/db/ 8 http://dbpedia.org/About 7 89 Predicado: http://www.w3.org/2002/07/owl\#sameAs Objeto: http://www4.wiwiss.fu-berlin.de/dblp/resource/person/100007 Sujeito: http://dbpedia.org/resource/Tim\_Berners-Lee Predicado: http://www.w3.org/2002/07/owl\#sameAs Objeto: http://www4.wiwiss.fu-berlin.de/dblp/resource/person/100007 Figura 4.6. Exemplos de Links RDF. Ao se criar links RDF é preciso estabelecer relações entre os termos dos vocabulários entre as fontes que estão sendo interligadas. Embora não haja restrições para seleção de vocabulários, é considerada uma boa prática o reuso de termos de vocabulários RDF amplamente usados para facilitar o processamento de dados ligados pelas aplicações clientes. Novos termos só devem ser definidos se não forem encontrados em vocabulários já existentes. Alguns vocabulários bastante conhecidos são: Friend-of-a-Friend (FOAF), Semantically-Interlinked Online Communities9 (SIOC), Simple Knowledge Organization System10 (SKOS), Description of a Project11 (DOAP), Creative Commons12 (CC) e Dublin Core13 (DC). Propriedades como: owl:equivalentClass, owl:equivalentProperty, rdfs:subClassOf, rdfs:subPropertyOf podem ser usadas para estabelecer equivalências entre os termos dos vocabulários citados. Maiores detalhes sobre as linguagens OWL e RDFS podem ser encontrados em [Filho & Lóscio 2009]. 4.4.4. Explicitar formas de acesso adicional aos dados Para acessar os dados das fontes Linked Data é preciso realizar consultas SPARQL sobre as fontes. Para enviar consultas e recuperar resultados através da linguagem SPARQL é usado o protocolo SPARQL14. Fontes Linked Data tipicamente fornecem um SPARQL endpoint que é um serviço Web com suporte ao protocolo SPARQL. Esse serviço possui uma URI específica para receber requisições HTTP com consultas SPARQL e retornar os resultados dessas consultas em diferentes formatos como XML, JSON15, texto, RDF/XML, NTriples16, Turtle17 ou N318 e HTML [Magalhães et al 2011]. O framework Jena19 disponibiliza duas implementações de SPARQL endpoints: o Joseki20 e o Fuseki21. Ambos suportam o protocolo e a linguagem SPARQL, permitem a realização de consultas sobre arquivos RDF e RDF 9 http://sioc-project.org/ http://www.w3.org/2009/08/skos-reference/skos.html 11 http://trac.usefulinc.com/doap 12 http://creativecommons.org/ 13 http://dublincore.org/ 14 http://www.w3.org/TR/rdf-sparql-query/ 15 http://www.json.org/ 16 http://www.w3.org/2001/sw/RDFCore/ntriples/ 17 http://www.w3.org/TR/turtle/ 18 http://www.w3.org/TeamSubmission/n3/ 19 http://incubator.apache.org/jena/ 20 http://www.joseki.org/ 21 http://openjena.org/wiki/Fuseki 10 90 Triple Stores, e implementam SPARQL Update22 (linguagem para atualizar RDF store). A diferença principal entre eles é que o Fuseki é mais simples de configurar e usar, além de já possuir a RDF Store Jena TDB23 embutida. No entanto o Fuseki é um projeto mais recente, iniciado em 2011, que ainda possui limitações quanto ao gerenciamento de múltiplas fontes de dados e também quanto ao uso de mecanismos de segurança próprios [Magalhães et al 2011]. 4.4.5.Padrões para Publicar Dados Linked Data Para a publicação de dados RDF podem ser usadas ferramentas específicas de conversão de planilhas, arquivos CSV, arquivos XML, dados relacionais e outros documentos para o formato RDF como, por exemplo, a ferramenta ConvertToRDF24. Após a geração do arquivo em formato RDF, os dados podem ser carregados em um banco de dados que armazena as triplas RDF, chamado de RDF Store. A publicação de dados de uma RDF Store, seguindo os princípios Linked Data, tipicamente envolve a disponibilização de uma interface para acesso ao dados Linked Data e de um SPARQL endpoint para acesso aos dados. Uma vantagem de se converter dados para o formato RDF é a melhoria de desempenho que pode ser obtida ao usar formas de armazenamento especificamente otimizadas para realizar a persistência de triplas RDF. No entanto, o armazenamento das triplas requer espaço extra em relação aos dados originais. Além disso, a conversão demanda um certo tempo para ser realizada e os dados em RDF podem ficar desatualizados em relação aos dados originais. Um outra forma de acessar dados que não estão no modelo RDF consiste em fornecer uma visão RDF. Isso é feito através de um RDF Wrapper. Nesse caso, a conversão é realizada por um RDF Wrapper por meio de mapeamentos estabelecidos entre o modelo nativo e o modelo RDF, devendo haver um wrapper específico para cada tipo de modelo. Um RDF Wrapper também pode prover uma visão RDF para dados que precisam ser acessados através de uma Web API. Criar uma visão RDF tende a ter um desempenho inferior à conversão de dados para RDF devido às traduções dinâmicas entre os modelos que deve ser realizada a cada uso da visão RDF. No entanto, o uso de RDF Wrappers traz algumas vantagens, pois como o acesso ocorre sobre os dados originais, a visão RDF não requer espaço de armazenamento extra e não corre o risco de apresentar dados desatualizados. Um exemplo comum de utilização de visões RDF é a publicação de dados armazenados em banco de dados relacionais. O RDB-to-RDF25 Wrappers é uma solução que cria visões RDF a partir de mapeamentos entre as estruturas relacionais e os grafos RDF. A plataforma D2RQ26 é um exemplo de RDB-to-RDF Wrappers, que fornece toda a infraestrutura necessária para acessar bancos de dados relacionais como grafos RDF virtuais. Esta plataforma possui os seguintes componentes: Linguagem de mapeamento D2RQ é uma linguagem declarativa para descrever as correspondências entre o modelo relacional e o modelo RDF. Os mapeamentos escritos em D2RQ são documentos RDF. 22 http://www.w3.org/Submission/SPARQL-Update/ http://openjena.org/TDB/ 24 http://www.w3.org/wiki/ConverterToRdf 25 http://www.w3.org/TR/r2rml/ 26 http://www4.wiwiss.fu-berlin.de/bizer/d2rq/spec/ 23 91 Mecanismo D2RQ é um plug-in para os frameworks Jena e Sesame que usa os mapeamentos escritos na linguagem D2RQ para converter chamadas às APIs desses frameworks em consultas SQL ao banco de dados para obtenção dos resultados. Servidor D2R27 é um servidor HTTP que usa o mecanismo D2RQ para prover uma interface Linked Data e um SPARQL endpoint sobre o banco de dados relacional [Bizer & Cyganiak 2006]. Além do D2R, duas outras ferramentas destacam-se como RDB-to-RDF Wrappers: o Virtuoso RDF Views [Erling & Mikhailov 2006] e o Triplify [Auer et al. 2009]. Este último é um plugin para aplicações Web que permite mapear os resultados de consultas SQL em RDF e JSON. Depois disso, os dados podem ser compartilhados e acessados na Web de dados. Após gerar os dados no modelo RDF, é necessário verificar se o resultado está de acordo com os princípios Linked Data. Essa verificação pode ser feita através de ferramentas de validação como, por exemplo, Sindice Web Data Inspector28, Eyeball29 e W3C Validation Service30. A próxima seção apresentará uma série de aplicações Web que vem sendo desenvolvidas para consumir os dados publicados nos padrões Linked Data. 4.5. Consumo de dados ligados Nos últimos três anos, um número significativo de dados vem sendo disponibilizado de acordo com os princípios Linked Data. Como resultado, uma série de aplicações Web estão sendo desenvolvidas para explorar a Web de Dados. Segundo [Bizer et al 2009], essas aplicações podem ser classificadas em três categorias: browsers, motores de buscas e aplicações para domínios específicos. Essa seção examinará cada uma dessas categorias. 4.5.1. Browsers Linked Data Assim como os tradicionais browsers da Web clássica permitem aos usuários navegarem por páginas HTML, os browsers Linked Data permitem aos usuários navegar por fontes de dados seguindo os links expressos nas triplas RDF. Por meio destes browsers é possível percorrer os links RDF, explorando e descobrindo novas informações na Web. A seguir, serão apresentados alguns exemplos de browsers usados para acessar dados ligados. Tabulator31 permite ao usuário percorrer a Web de Dados e expor parte dos dados de uma forma controlada, onde o usuário pode dividir a informação por tópicos. Os resultados das consultas podem ser analisados através de vários métodos, que vão desde a apresentação de dados de maneira convencional até a apresentação por meio de mapas. A utilização do modo de exploração inicia a partir da submissão de uma URI. Depois disso, o Tabulator obtém informações sobre o recurso e as 27 http://www4.wiwiss.fu-berlin.de/bizer/d2r-server/ http://inspector.sindice.com/ 29 http://jena.sourceforge.net/Eyeball/ 30 http://www.w3.org/RDF/Validator/ 31 http://www.w3.org/2005/ajar/tab 28 92 exibe como um grafo RDF em uma visão de árvore. A expansão de um nó permite a obtenção de mais informações sobre ele. Para passar ao modo de análise, o usuário pode selecionar predicados para definir um padrão e requisitar que o Tabulator encontre todos os exemplos daquele padrão. Os resultados podem ser exibidos através das visões de tabela, mapas, calendários ou linhas de tempo. Pode-se iniciar uma nova exploração pela seleção de um detalhe de uma das visões. O Tabulator pode ser usado como um complemento do navegador Firefox ou como uma aplicação Web que atualmente é compatível apenas com o Firefox [Lee et al. 2006]. Marbles32 é um aplicativo do lado do servidor que formata o conteúdo da Web Semântica para clientes XHTML. Pontos coloridos são usados para correlacionar a origem dos dados apresentados com as fontes de dados de onde foram encontrados. Os dados são recuperados de múltiplas fontes e integrados em um único grafo que é mantido através das sessões do usuário. Além de dereferenciar a URI, Marbles consulta os mecanismos de busca Sindice e Falcons em busca de fontes que contenham informações sobre o recurso referenciado pela URI dada como entrada para a busca. Além disso, os predicados owl:sameAs e rdfs:seeAlso são seguidos para obtenção de informações adicionais sobre o recurso. A Figura 4.7 apresenta como o Marbles disponibiliza os dados para o usuário após uma consulta. Os pontos coloridos indicam as fontes de dados que forneceram os dados para o usuário. Dessa forma, o usuário pode clicar sobre as fontes e encontrar mais informações para a sua busca. Figura 4.7. Visualização de Informações sobre recursos do Browser Marbles. 32 http://marbles.sourceforge.net/ 93 Disco Hiperdata Browser33 é uma aplicação Web usada como navegador simples para visualizar informações sobre um recurso por meio de uma página HTML. Para iniciar a navegação, o usuário digita a URI do recurso em uma caixa de texto e pressiona o botão “Go”. A partir daí, o browser recupera as informações sobre o recurso e as exibe em uma tabela contendo propriedades, valores e as fontes das quais os dados foram recuperados. Os links exibidos permitem a navegação entre os recursos, de modo que ao selecionar um novo recurso, o navegador dinamicamente recupera informações sobre ele através do dereferenciamento de sua URI. À medida que a navegação é feita, Disco armazena os grafos RDF recuperados em um cache de sessão. Ao clicar sobre o link “Display all RDF graphs”, uma nova janela é aberta contendo a lista dos grafos RDF recuperados e das URIs que não foram dereferenciadas com sucesso. Disco pode ser usado de forma online ou executado localmente. Além das aplicações descritas acima, destacam-se: Dipper34, Piggy Bank35, URI Burner36, LinkSailor37 e Graphite RDF Browser38 como browsers simples e rápidos para obter detalhes sobre uma determinada URI após dereferenciá-la. 4.5.2. Motores de Busca Linked Data Um grande número de motores de busca tem sido desenvolvido para rastrear dados no padrão Linked Dados. Esses mecanismos de busca permitem localizar recursos de diferentes fontes por meio de palavras-chave. A consulta pode ser realizada pelo usuário através de uma interface Web ou através de serviços Web oferecidos pelos mecanismos de busca. A seguir são apresentados alguns dos motores de busca mais utilizados. Falcons permite tanto uma busca por palavras-chave, como oferece ao usuário a opção de buscar objetos, conceitos e documentos, possibilitando uma apresentação dos resultados um pouco diferente dos demais motores de busca. A busca por objeto é adequado para a busca por pessoas, lugares e outros itens considerados concretos, enquanto que a busca por conceito é orientada para a localização de classes e propriedades em ontologias publicadas na Web. O recurso de pesquisa por documento segue uma abordagem mais tradicional, onde os resultados apontam para documentos RDF que contêm os termos de pesquisa especificados [Cheg & Qu 2011]. Sindice39 coleta dados estruturados na Web (RDF, RDFa e microformatos) e os indexa por URIs, propriedades funcionais inversas (IFPs) e palavras-chave, oferecendo uma interface Web para que os usuários possam fazer buscas a partir dos itens indexados. Sindice também fornece um SPARQL endpoint que permite a realização de consultas sobre todos os seus dados e uma API que permite a utilização 33 http://www4.wiwiss.fu-berlin.de/rdf_browser/ http://api.talis.com/stores/iand-dev1/items/dipper.html 35 http://simile.mit.edu 36 http://linkeddata.uriburner.com 37 http://linksailor.com/ 38 http://graphite.ecs.soton.ac.uk/browser/ 39 http://sindice.com/ 34 94 de seus serviços por outras aplicações [Oren et al 2008]. Na Figura 4.8 pode-se observar como o motor de busca Sindice apresenta o resultado de uma consulta ao usuário. Figura 4.8. Resultado mostrado pelo Sindice sobre a pesquisadora Bernadette Farias Lóscio. Sig.ma40 busca dados estruturados a partir de uma palavra-chave e os exibe em uma única página, integrando os dados de múltiplas fontes. A visão criada pelo Sig.ma baseia-se em resultados fornecidos pelo Sindice. O usuário pode aprovar, rejeitar ou acrescentar fontes para estabelecer uma visão dos dados relevantes. Ao selecionar uma entidade da lista de resultados, uma nova visão é apresentada ao usuário. Um link permanente pode ser criado para futuros acessos ou compartilhamento dessa visão. Os filtros aplicados nas fontes pelos usuários ajudam a classificar melhor a relevância das fontes e aperfeiçoar a qualidade dos resultados futuros. Além da interface Web do usuário, Sig.ma ainda fornece uma API destinada aos desenvolvedores de aplicações. A Figura 4. 9 ilustra o resultado de uma consulta sobre a pesquisadora Damires Yluska envolvendo três fontes nas quais ela é referenciada. 40 http://sig.ma/ 95 Figura 4.9. Visão criada pelo Sig.ma sobre a pesquisadora Damires Yluska. Watson41 e Swoogle42 são mecanismos de busca mais voltados para a descoberta de informações sobre ontologias. Podem ser usados, por exemplo, para obter ontologias que possuem determinados conceitos e descobrir relacionamentos entre termos. Yahoo e Google também deram início ao uso de Linked Data. Yahoo provê acesso aos dados através da BOSS API43, enquanto o Google usa os dados para a Social Graph API44. 4.5.3. Aplicações para Domínios Específicos Enquanto os browsers Linked Data e motores de busca descritos anteriormente fornecem funcionalidades muito genéricas, uma série de serviços, chamadas de Linked Data Mashups, foram desenvolvidos com o objetivo de prover funcionalidades mais específicas e de acordo com determinados domínios de dados. A seguir descreveremos algumas dessas aplicações [Lee et al 2006]. Revyu45 é uma aplicação Web para crítica e classificação de qualquer item passível de avaliação. Revyu também disponibiliza uma API e um SPARQL endpoint para serem usados pelos desenvolvedores de aplicações. DBpedia Mobile46 é uma aplicação cliente para dispositivos móveis consistindo de uma visão com um mapa e do navegador Linked Data Marbles. Baseado na localização geográfica de um dispositivo móvel, a aplicação exibe um mapa indicando localizações próximas a partir de dados extraídos das fontes DBpedia, Revyu e Flickr. O acesso ao Flickr é realizado através de um Wrapper. O usuário pode explorar informações sobre essas localizações e navegar em conjuntos de dados interligados. Também é possível a publicação de informações como Linked Data, de modo que possam ser usadas por outras aplicações [Becker & Bizer 2008]. Talis Aspire47 é uma aplicação Web voltada para que alunos e professores e possam encontrar os principais recursos educacionais em universidades do Reino 41 http://watson.kmi.open.ac.uk/WatsonWUI/ http://swoogle.umbc.edu/ 43 http://developer.yahoo.com/search/boss/boss_api_guide/ 44 http://code.google.com/intl/pt-BR/apis/socialgraph/ 45 http://revyu.com/ 46 http://beckr.org/DBpediaMobile/ 47 http://www.talisaspire.com/ 42 96 Unido. O serviço é gratuito e provê ferramentas para criar e editar listas de leitura, além da produção e publicação de materiais educativos. Quando o usuário publica conteúdo, a aplicação cria triplas RDF em uma RDF store. Itens publicados são interligados de forma transparente a itens correspondentes de outras instituições. BBC Programmes48 e BBC Music49 são projetos desenvolvidos pela BBC Audio and Music Interactive. A aplicação Web BBC Programmes disponibiliza informações detalhadas sobre tipos, séries e episódios de todos os programas de TV e rádio transmitidos pela BBC. BBC Music fornece informações sobre artistas, vinculando-os aos programas da BBC. Assim é possível escolher um artista e obter todos os episódios de programas relacionados a ele. As aplicações mencionadas usam Linked Data como tecnologia de integração de dados, inclusive fazendo uso de vocabulários amplamente conhecidos como DBpedia e MusicBrainz50. LinkedDataBr é um projeto brasileiro que visa construir uma infra-estrutura de suporte à criação de repositórios de dados governamentais públicos utilizando os padrões de Linked Data. A ideia consiste em utilizar e ligar dados governamentais disponíveis na Web, gerados a partir das iniciativas de e-gov e open-government, de forma a possibilitar a publicação padronizada e o acesso por parte dos cidadãos e organizações [Campos 2010]. 4.6. Considerações Finais O imenso emaranhado de documentos acessíveis na Web é composto de dados e informações de praticamente todas as áreas do conhecimento humano. Contudo, ainda é árdua a tarefa de prover meios eficientes que permitam aproveitar todo esse conteúdo, que pode ser composto tanto por dados estruturados, como os dados provenientes de bancos de dados relacionais, quanto por dados não estruturados, como textos e dados multimídia. No cenário da Web atual, o grande volume de dados e a falta de metadados dificultam o acesso à informação útil, específica e relevante. Neste contexto, espera-se que o uso dos princípios do Linked Data possibilite a transformação de uma Web na qual os recursos são documentos HTML para uma Web de Dados, onde os dados estarão interligados através de metadados. O tema Linked Data traz novos desafios para o desenvolvimento de aplicações Web de uma maneira geral, bem como para o gerenciamento da grande nuvem de dados que vem se formando como resultado da crescente adoção dos princípios do Linked Data. Tendo em vista a relevância deste assunto para a comunidade de Computação e o grande potencial de pesquisa desta área, Linked Data tem sido o foco de diversas conferências internacionais, bem como o foco de estudo de diversos grupos de pesquisa. Dessa forma, torna-se de fundamental importância que este tema seja amplamente abordado e discutido por pesquisadores, alunos e profissionais da área de Computação. 48 http://www.bbc.co.uk/programmes http://www.bbc.co.uk/music 50 http://musicbrainz.org/ 49 97 Referências [Allemang & Hendler 2008] Allemang, D., Hendler, D. (2008) Semantic Web for the Working Ontologist, 1st edition. Morgan Kaufmann publ., Amsterdam, Netherlands. [Auer et al. 2009] Auer, S., Dietzold, S., Lehmann, J., Hellmann, S., and Aumueller, D. (2009) Triplify: Light-weight linked data publication from relational databases. In Quemada, J., León, G., Maarek, Y. S., and Nejdl, W., editors, Proceedings of the 18th International Conference on World Wide Web, WWW 2009, Madrid, Spain, April 20-24, 2009, pages 621–630. ACM. [Becker & Bizer 2008] Becker, C., Bizer, C. (2008) DBpedia Mobile: A LocationEnabled Linked Data Browser. In Linked Data on the Web (LDOW2008). [Bizer & Cyganiak 2006] Bizer, C., Cyganiak, R. (2006) D2R Server – Publishing Relational Databases on the Semantic Web. In 5th International Semantic Web Conference. [Bizer et al 2009] Bizer C., Heath T., Berners-Lee T. (2009) Linked data - the story so far. Int. J. Semantic Web Inf. Syst., 5(3):1–22, 2009. [Campos 2010] Campos M. L. (2010) GT-LinkedDataBR – Exposição, compartilhamento e conexão de recursos de dados abertos na Web (Linked Open Data). Disponível em http://www.rnp.br/pd/gts2010-2011/gt_linkeddatabr.html [Cheg & Qu 2011] Cheng, G., Qu, Y. (2011) Searching Linked Objects with Falcons: Approach, Implementation and Evaluation. International Journal on Semantic Web and Information Systems, Special Issue on Linked Data. [Costa & Yamate 2009] Costa A., Yamate F. (2009) Semantic Lattes: uma ferramenta de consulta baseada em ontologias. Trabalho de Grduação em Engenharia de Computação - Escola Politécnica. IME/USP. [Erling & Mikhailov 2006] Erling, O., Mikhailov, I. (2006) Mapping Relational Data to RDF in Virtuoso. http://virtuoso.openlinksw.com/dataspace/dav/wiki/ Main/VOSSQLRDF. [Filho & Lóscio 2009] Filho, F. W. B. H , Lóscio B. F. (2009) Web Semântica: Conceitos e Tecnologias. In Anais do ERCEMAPI (Escola Regional de Computação Ceará – Maranhão – Piauí). [Freitas 2003] Freitas, F. L. G. (2003) Ontologias e a Web Semântica. XXIII Congresso da Sociedade Brasileira de Computação. JAI. Campinas, São Paulo, Junho de 2003. [Gruber 1995] Gruber T. (1995) Toward principles for the design of ontologies used for knowledge sharing. 1995. International Journal Human-Computer Studies Vol. 43, Issues 5-6, November 1995, p.907-928. [Heath & Bizer 2011] Heath, T., Bizer, C. (2011) Linked Data: Evolving the Web into a Global Data Space (1st edition). Synthesis Lectures on the Semantic Web: Theory and Technology, 1:1, 1-136. Morgan & Claypool, 2011. 98 [Klyne et al 2004] Klyne, G., Carroll, JJ., McBride., B. (2004) Resource description framework (RDF): Concepts and abstract syntax. Disponível em: http://www.w3.org/TR/rdf-concepts/ [Lee et al 2006] Lee, B. T., Chen, Y., Chilton, L., Connolly, D., Dhanaraj, R., Hollenbach, J., Lerer, A., and Sheets, D. (2006) Tabulator: Exploring and Analyzing Linked Data on the Semantic Web. In In Procedings of the 3rd International Semantic Web User Interaction Workshop (SWUI06, page 06. [Lee et al 2001] Lee, B. T., Hendler J., Lassilia O. (2001) The semantic web. Scientific American, 284(5):34–44, Mai 2001. http://dx.doi.org/10.1038/scientificamerican0501-34DOI: 10.1038/scientificamerican0501-34 [Magalhães et al 2011] Magalhães, R. P., Macedo, J. A. F., Vidal, V. M. P. (2011) Linked Data: Construindo um Espaço de Dados Global na Web. In Anais do XXIV Simpósio Brasileiro de Banco de Dados. Outubro de 2011. [Oren et al 2008] Oren, E., Delbru, R., Catasta, M., Cyganiak, R., Stenzhorn, H., and Tumma-rello, G. (2008) Sindice.com: a document-oriented lookup index for open linked data. Int. J.Metadata Semant. Ontologies, 3:37–52. [Souza 2009] Souza D. (2009) Using Semantics to Enhance Query Reformulation in Dynamic Distributed Environments. PhD Thesis, Federal University of Pernambuco (UFPE), Recife, PE, Brazil. 99 Capítulo 5 Desenvolvimento de Aplicações para Plataforma Google Android Fábio de Jesus Lima Gomes, Manoel Taenan Ferreira de Souza, Rafael Madureira Lins de Araújo Abstract Com a evolução da tecnologia móvel, os dispositivos móveis tornaram-se uma importante fonte de transmissão e recepção de informações, gerando a necessidade de sistemas operacionais mais robustos e uma considerável demanda para o desenvolvimento de serviços e aplicações. A plataforma Google Android surgiu para preencher esta lacuna. Dessa forma, este mini-curso pretende disseminar conceitos envolvidos no desenvolvimento de serviços e aplicações para a plataforma Google Android. Também será abordado como aplicações para dispositivos móveis podem consumir serviços web através da arquitetura REST. Resumo With the evolution of mobile technology, mobile devices have become an important source of transmission and reception of information, creating the need for more robust operating systems and a considerable demand for the development of services and applications. The Google Android platform was created in order to fill this gap. Thus, this mini-course aims disseminate concepts about developing services and applications for the Google Android platform. Also we will describe how mobile applications can consume web services via REST architecture. 5.1. Introdução O Google Android OS, também chamado apenas de Android, é um sistema operacional de código aberto para dispositivos móveis e utiliza uma versão modificada do Sistema Operacional Linux. Permite a desenvolvedores criarem aplicações Java que controlam o dispositivo através de bibliotecas desenvolvidas pela Google. O Android também provê uma infra-estrutura robusta de execução de aplicações Java. Apesar de ser recente (seu 100 lançamento foi em 2008), o Android foi adotado rapidamente por diversos fabricantes de dispositivos móveis e atualmente é a plataforma que mais cresce no mundo. Atualmente, a plataforma Google Android é mantida pela OHA (Open Handset Alliance), um grupo formado por mais de 40 empresas que se uniram para inovar e acelerar o desenvolvimento de aplicações e serviços para dispositivos móveis, trazendo aos consumidores uma experiência mais rica em termos de recursos e menos onerosa financeiramente para o mercado. Pode-se dizer que a plataforma Google Android é a primeira plataforma completa, aberta e livre para dispositivos móveis. O Android SDK é o kit de desenvolvimento que disponibiliza as ferramentas e APIs necessárias para desenvolver aplicações para a plataforma Google Android, utilizando a linguagem Java. Este mini-curso visa abordar conceitos envolvidos sobre desenvolvimento de aplicações para a plataforma Google Android, apresentando os principais aspectos do desenvolvimento de aplicações para dispositivos móveis, com enfoque para o desenvolvimento de aplicações que consomem serviços web utilizando a linguagem Java. Pretende-se capacitar os participantes no desenvolvimento de aplicações Java para plataforma Google Android com base no Android SDK e demonstrar a arquitetura REST (Representational State Transfer) de desenvolvimento de aplicações web. O curso procura explorar as funcionalidades dessas tecnologias através do desenvolvimento passo a passo de aplicações-exemplos. 5.2. Arquitetura do Google Android Android é uma pilha de software para dispositivos móveis que inclui sistema operacional, middleware e aplicações-chave. Esta pilha possui 4 níveis (Google, 2011): Figura 5.1 Arquitetura da Plataforma Google Android (Google, 2011) 101 1. LINUX KERNEL: a base da pilha é o kernel. O Google usou a versão 2.6 do Linux para construir o kernel do Android, que inclui serviços essenciais do sistema, tais como, gerenciamento de memória, gerenciamento de processos, gerenciamento de energia, configurações de segurança, configurações de rede e drivers. O kernel também atua como uma camada de abstração entre o hardware do dispositivo e os outros níveis da pilha de software. 2. RUNTIME ANDROID e LIBRARIES: acima do kernel estão as bibliotecas do Android e o android runtime. Android runtime consiste de um conjunto de bibliotecas que fornece a maioria das funcionalidades disponíveis nas principais bibliotecas da linguagem de programação Java e de uma Máquina Virtual Dalvik (DVM). Uma aplicação Android roda em seu próprio processo, com a sua própria instância da máquina virtual Dalvik. Dessa forma, nenhuma aplicação é dependente de outra; se uma aplicação pára, ela não afeta quaisquer outras aplicações executando no dispositivo e isso simplifica o gerenciamento de memória. Dalvik foi escrito de modo que um dispositivo possa executar várias VMs eficientemente. Android possui um conjunto de bibliotecas C/C ++ usado por diversos componentes da plataforma. As principais bibliotecas são listadas abaixo: System C library – uma implementação da biblioteca padrão C (libc), derivada do sistema operacional BSD, alterada para dispositivos embarcados baseados no Linux; Media Libraries – baseada no OpenCORE da PacketVideo; estas bibliotecas suportam reprodução e gravação de muitos formatos populares de áudio, vídeo e imagem, tais como, MPEG4, H.264, MP3, AAC, AMR, JPG, e PNG. Surface Manager – gerencia acesso ao sub-sistema de exibição e compõe as camadas gráficas 2D e 3D para as aplicações. LibWebCore – um moderno engine para um navegador web que alimenta o navegador do Android. SGL – engine de gráficos 2D. 3D libraries – uma implementação baseada nas APIs OpenGL ES 1.0; estas bibliotecas usam aceleração de hardware 3D (quando disponível) ou o software embutido no sistema. FreeType – renderizador de bitmap e fontes vetorizadas. SQLite – engine leve e poderoso de banco de dados relacional para as aplicações. 3. APPLICATION FRAMEWORK: O próximo nível é o framework de aplicação que consiste nos programas que gerenciam as funções básicas do telefone, tais como, alocação de recursos, aplicações de telefone, mudança entre processos ou programas e informações sobre a localização física do aparelho. Os desenvolvedores de aplicações têm acesso total ao framework de aplicação do Android. Isso possibilita tirar vantagem das capacidades de processamento e do suporte de recursos do Android. 102 4. APPLICATIONS: No topo da pilha estão as aplicações em si. Aqui se encontram as funções básicas do dispositivo, como fazer chamadas telefônicas, acessar o navegador web ou acessar sua lista de contatos. Esta é a camada do usuário comum, que utiliza a interface de usuário. Apenas os programadores do Google, os desenvolvedores de aplicação e os fabricantes de hardware acessam as camadas inferiores da pilha. O Android contém um conjunto de aplicativos, implementados em Java, como um cliente de e-mail, programa para SMS (Short Message Service), calendário, mapas, navegador e gerenciador de contatos. 5.3. Componentes de uma aplicação Android As aplicações Android podem ser divididas em quatro tipos de componentes básicos que são definidos pela própria arquitetura (ABLESON,2007), são eles: 5.3.1. Activities Funcionam como mediadores que definem como as informações serão apresentadas ao usuário, além de controlar o fluxo da aplicação. Elas podem interagir com o usuário e trocar informações com outras activities ou services (MEIER,2009). A maioria do código que escreveremos para uma aplicação Android irá executar no contexto de uma activity. Activities normalmente correspondem a telas: cada activity mostra uma tela para o usuário. Quando esta não está em execução, o sistema operacional pode eliminá-la para liberar memória. 5.3.1.1. Ciclo de vida de uma Activity Ao longo de sua criação até o momento de sua eliminação da memória, uma activity atravessará seis estados, podemos referenciar cada estado pelos métodos: OnCreate É chamado quando a activity é criada. Ela é obrigatória e chamada apenas uma vez, deve referenciar a tela que será apresentada ao usuário. OnStart É chamado quando a activity está ficando visível e já tem uma tela definida. OnResume É chamado quando a activity foi parada temporariamente e está retornando à execução. OnPause É chamado quando a activity está sendo tirada do topo da execução. Geralmente é utilizado para salvar o estado da aplicação. OnStop É chamado quando a activity não está mais visível e está em segundo plano. OnDestroy Executa os últimos processamentos antes da activity ser literalmente encerrada. 103 Figura 5.2. Ciclo de vida de uma Activity. 5.3.2. Services São programas que executam em segundo plano. Não interagem diretamente com o usuário e podem ficar executando por tempo indefinido. 5.3.3. Broadcast e Intent Receivers São componentes que ficam aguardando a ocorrência de um determinado evento, podese entender como evento a inicialização do sistema operacional, uma chamada de voz, a chegada de um SMS, um evento disparado por uma aplicação (MEIER,2009). Intents são elementos chave no Android, porque facilitam a criação de novas aplicações a partir de aplicações já existentes. Precisaremos utilizar Intents para interagir com outras aplicações e serviços que proporcionarão informações necessárias para nossa aplicação. 5.3.4. Content Providers São os compartilhadores de conteúdo entre as aplicações, uma aplicação pode requisitar informações de outra, por exemplo, uma aplicação pode receber dados da lista de contatos que é nativa do Android, e com base nesses dados, realizar algum processamento (LECHETA,2010). 104 5.4. Android SDK e seus pacotes para implementação de aplicações O SDK é um conjunto de ferramentas utilizadas para desenvolver aplicações para a plataforma Android. Possui um emulador para simular o dispositivo móvel e uma API completa para a linguagem Java, com todas as classes necessárias para desenvolver as aplicações (BURNETTE, 2008). Existem atualmente três versões do SDK para atender a maior parte dos desenvolvedores: versão para Windows, Linux e Mac OS. O ambiente de desenvolvimento que nos utilizaremos nos exemplos seguintes é composto, além do JDK e Android SDK, pelo Eclipse IDE versão Galileo e o Android Development Plugin (ADT), um plugin que ajudará na integração da IDE com o emulador. Os componentes do ambiente de desenvolvimento podem ser encontrados nos links a seguir: JDK: http://www.oracle.com/technetwork/java/javase/downloads/index.html Android SDK: http://developer.android.com/sdk/ Eclipse IDE: http://www.eclipse.org/downloads/ ADT: http://developer.android.com/sdk/eclipse-adt.html 5.4.1. Conceitos básicos do Android 5.4.1.1 Criando uma Activity A classe android.app.activity é utilizada para criar uma tela na aplicação. Essa tela é composta de vários elementos visuais, os quais no Android são representados pela classe android.view.View (LECHETA, 2010). A classe android.view.View pode representar algo simples como um botão, um checkbox ou imagem, como também pode representar algo complexo como um gerenciador de layout, a qual pode conter várias views aninhadas para organizar na tela. Figura 5.3. Exemplo de uma Activity. O método setContentView(view) é o que faz a ligação entre a activity e a view e recebe como parâmetro a view que será exibida na tela. 105 5.4.1.2 A classe R A classe R é criada automaticamente pelo ADT e não pode ser alterada manualmente. Nela existem constantes para os recursos do projeto. Cada constante é nomeada com o nome do recurso, que deve ser escrito com letra minúscula e sem espaço, e recebe um valor inteiro. 5.4.1.3 O arquivo AndroidManifest.xml Toda aplicação Android deve ter um arquivo AndroidManifest.xml em seu diretório raiz. Esse arquivo apresenta informações essenciais sobre a aplicação para o sistema operacional, que deve possuir informações do sistema antes que possa executar qualquer solicitação do código do aplicativo (MEDNIEKS,2009). Ele armazena informações como o nome do pacote da aplicação, descreve os componentes da aplicação, determina qual processo da aplicação vai armazenar os componentes, declara de que formas as solicitações devem ter permissões para acessar partes protegidas da API e interagir com outras aplicações. Declara também as permissões que os outros processos serão obrigados a ter, fim de interagir com os componentes da aplicação, enumera classes e perfis e fornece outras informações sobre como a aplicação será executada, declara qual o nível mínimo da API que o aplicativo exige e enumera bibliotecas que estarão relacionadas com a aplicação. Figura 5.4. Exemplo de arquivo AndroidManifest.xml 5.4.1.4 Criação de uma interface visual O Android fornece um sofisticado e poderoso modelo, baseado em componentes, para construir sua interface, baseado no esquema de classes fundamentais: android.view.View e android.view.ViewGroup, e inclui também as suas classes filhas chamadas de widgets e layouts respectivamente(LECHETA, 2010). Podemos citar alguns exemplos de widgets como Button, TextView, EditText, ListView, CheckBox, RadioButton, Gallery, Spinner, e outros. Podemos citar, também, exemplos de layouts como LinearLayout, FrameLayout, RelativeLayout entre outros. Para exemplificar a criação da interface visual criaremos uma tela de login, a qual conterá os campos de nome de usuário e senha, e um botão para submetê-los. 106 Figura 5.5. Representação gráfica da tela de login. Figura 5.6. Código da tela de login 107 Podemos observar que foram utilizados três tipos de widgets e um layout. Dois TextView que são utilizados para renderizar strings na tela e dois EditText que são caixas para receber texto. Foi utilizado, também, um “Button” para enviar os dados e um LinearLayout para organizá-los na tela. Podemos observar, também, alguns atributos como o “id” que serve como identificador de cada componente, o text que tem o funcionamento semelhante ao value do HTML e o atributo password que esconde os caracteres digitados no nosso EditText. 5.4.1.5 O método findViewById() Ao construir uma tela usando um arquivo XML de layout, surge a necessidade de recuperar os objetos definidos no arquivo dentro do código-fonte da aplicação para obter seus valores ou definir atributos (LECHETA, 2010). Podemos recuperar um objeto de visão através do seu identificador único (android:id), passando-o como parâmetro no método findViewById(id). Esse método recebe o id do componente desejado e retorna uma subclasse de android.view.View, como as classes Button, TextView e EditText. Na figura a seguir é mostrado como recuperar a senha inserida pelo usuário através do método findViewById(id) e uma pequena ajuda da classe R. Figura 5.7. Exemplo da utilização do método findViewById() 5.4.2 Intent Uma intent representa uma intenção da aplicação em realizar alguma ação. Ela envia uma mensagem ao sistema operacional chamada de broadcast. Ao receber essa mensagem, o sistema operacional tomará as decisões necessárias (LECHETA, 2010). 108 Uma intent é representada pela classe android.content.Intent e pode ser utilizada para enviar uma mensagem ao sistema operacional, abrir uma nova tela da aplicação, utilizando o método startActivity(intent), solicitar ao sistema operacional que ligue para determinado número de celular, abrir o browser em um determinado endereço da internet, exibir algum endereço, localização ou rota no Google Maps dentre outros. 5.4.2.1 Navegação entre telas com passagem de parâmetros Existem dois métodos de se iniciar uma nova tela (activity), através dos métodos startActivity(intent) e startActivityForResult(intent,codigo) que apenas inicia uma nova activity ou inicia uma nova activity e cria um vínculo para ser utilizado ao retornar respectivamente (PEREIRA, 2009). Para que o sistema operacional possa reconhecer nossa nova activity, é necessário adicionar seu endereço no arquivo AndroidManifest.xml. Figura 5.8. Trecho do arquivo AndroidManifest.xml que contém a nova activity. Podemos enviar informações para outras telas através de uma intent. Figura 5.9. Exemplo de passagem de parâmetro e troca de tela através de uma intent Podemos observar na figura 5. 8, que é criada uma intent com a activity da telaalvo, é passado como parâmetro através do método putExtra() o texto contido no EditText referente ao usuário para a próxima tela, e finalmente, a nova activity é iniciada através do método startActivity(intent). 109 Figura 5.10. Código da classe SegundaTela. Observando essa classe, vemos que a intent é capturada através do método getIntent() e recebemos o parâmetro do login através do método getStringExtra(string). O conteúdo da tela é apenas um TextView com uma mensagem de boas vindas. 5.4.2.2 Intents Nativas do Android Vimos no exemplo anterior que é possível iniciar uma nova activity através das intents. O Android possui alguns tipos de intents pré-definidas que podemos utilizar para enviar mensagens ao SO, porém, algumas delas necessitam de permissões para executar, tais permissões precisam ser registradas no arquivo AndroidManifest.xml (PEREIRA,2009). Várias intents como a ACTION_VIEW, que serve para iniciar o navegador, a ACTION_CALL, que é utilizada para realizar chamadas, a ACTION_PICK, que serve para visualizar todos os contatos, dentre outras, são utilizadas em aplicações Android. A seguir veremos um exemplo de como chamar o navegador através de uma intent pré-definida no Android. Figura 5.11. Exemplo da iniciação do navegador através de uma Intent. O exemplo demonstra a utilização da intent ACTION_VIEW, mas, para usar essa intent é necessária a permissão INTERNET que deve ter sido registrada previamente. Figura 5.12. Inserindo a permissão INTERNET no arquivo AndroidManifest.xml 110 5.4.3 Intent Filter Podemos utilizar intents para enviar mensagens ao sistema operacional, definindo uma ação que identifique essa intent. Então quando a mensagem for enviada ao sistema operacional ela seja identificada por essa ação, e somente uma activity que esteja mapeada para aquela ação será executada (MEDNIEKS, 2009). Esse tipo de rotina é bem prática quando queremos que mais de um programa esteja configurado para receber uma ação (LECHETA, 2010). Para definir essa ação, basta criar uma Intent usando seu construtor, que recebe uma string que identifica a ação, como, por exemplo: Figura 5.13. Exemplo de uma chamada de ação por uma Intent. Logicamente, alguém tem que responder por essa ação. Para isto, precisamos mapear um Intent Filter no arquivo AndroidManifest.xml, para escutar esse chamado e delegar a execução à uma activity. Figura 5.14. Código fonte da classe SegundaTela. Figura 5.15. Exemplo de mapeamento de uma Intent Filter. 5.4.4 BroadcastReceiver A classe BroadcastReceiver é utilizada para responder a determinados eventos enviados por uma intent. Ela sempre é executada em segundo plano durante pouco tempo, normalmente dez segundos. Não deve ter interface gráfica ou interação com o usuário (LECHETA, 2010). 111 É utilizada normalmente para executar algum processamento sem que o usuário perceba, em segundo plano. Assim como uma activity, devemos declará-lo no arquivo AndroidManifest.xml através da tag <receiver>, deve ser declarada, também, um Intent Filter para o broadcast, ou podemos registrá-lo dinâmicamente, utilizando o método registerReceiver(receiver,filtro), que tem como parâmetros uma classe-filha de IntentReceiver e uma instância da classe IntentFilter que possui a configuração da ação e a categoria (LECHETA, 2010). O método para enviar uma mensagem para um broadcast é diferente do utilizado para uma intent que chama uma activity. O método utilizado é o sendBroadcast(intent) que envia uma mensagem para todas as aplicações instaladas no celular. Para implementar um BroadcastReceiver deve-se extender a classe BroadcastReceiver e implementar o método onReceive() que será executado assim que o IntentFilter receber a mensagem. Figura 5.16. Utilizando o método sendBroadcast(intent). 112 Figura 5.17. Exemplo de um BroadcastReceiver. Apenas por motivos didáticos foi utilizado a classe Toast para verificar o funcionamento do BroadcastReceiver. Recomenda-se que não tenha nenhum tipo de interação com o usuário. Figura 5.18. Exemplo do mapeamento de um BroadcastReceiver. 5.4.5 Notification A classe Notification é utilizada para exibir informações ao usuário sem que este seja interrompido se estiver executando alguma atividade. O usuário pode escolher visualizar as informações neste momento, ou depois (LECHETA, 2010). A notificação é exibida na barra de status do celular para chamar a atenção do usuário. Ao ser visualizada, a intent configurada pode uma abrir uma nova activity ou pode ser usada para iniciar um serviço por exemplo (MEIER, 2009). Um exemplo de notificação é a recepção de uma nova SMS, onde usuário pode decidir visualizá-la ou não. Para criar uma notificação é necessário capturar um serviço do Android chamado de NOTIFICATION_SERVICE, será necessário, também utilizar a classe PendingIntent que criará uma intent que ficará pendente até o usuário decidir visualizar a notificação: 113 Figura 5.19. Exemplo da criação de uma notificação. Podemos observar, que o construtor da classe Notification recebe três parâmetros: o ícone que deverá ser exibido, o título da notificação e a hora que aparecerá do lado da notificação. Observamos, também, que deve-se informar através do método setLatestEventInfo, a mensagem que aparecerá na barra de status assim que a notificação for reconhecida pelo serviço de notificações do android, o título da notificação e a intent que deverá ser chamada quando o usuário visualizar a notificação. Figura 5.20. Classe que será instanciada quando a notificação for visualizada. A figura anterior mostra a classe que será instanciada quando o usuário visualizar a notificação. Capturamos o serviço de notificações do android, e pedimos 114 para que a notificação não apareça mais na barra de status através do método cancel(int) que recebe como parâmetro o id da notificação. 5.4.6 Service Têm as mesmas características dos serviços dos sistemas operacionais de computador. São utilizados quando queremos executar algo por tempo indeterminado em segundo plano e que exija um alto consumo de recursos, memória e CPU (LECHETA, 2010). Geralmente são iniciados por um BroadcastReceiver para executar algum processamento demorado, pois um BroadcasReceiver tem um tempo determinado para executar (PEREIRA, 2009). É interessante que os serviços tenham suas próprias threads para que fiquem independentes do programa hospedeiro. Eles também possuem um ciclo de vida próprio, semelhante ao de uma Activity, mas possuem apenas três estágios: o onCreate, onStart e onDestroy, que desempenham o mesmo papel que o de uma Activity. Para iniciar um serviço é necessário criar uma activity que chame o método startService(intent). Para parar um serviço existem duas maneiras: a primeira é chamar o método stopService(intent), a mesma intent utilizada para iniciar o serviço deve ser usada para pará-lo, e a segunda forma é o próprio serviço chamar o método stopSelf(). Figura 5.21. Chamando um serviço. Para implementar um serviço é necessário estender a classe android. 115 Figura 5.22. Exemplo de um serviço. Para exemplificar o funcionamento de um serviço, utilizamos esta classe, que quando iniciada, cria uma série de logs. Podemos observar que mesmo fechando a aplicação que iniciou o serviço, ele continua criando logs até chamar o método stopSelf(). É necessário mapear o serviço no arquivo AndroidManifest.xml e configurar um IntentFilter com a ação que iremos passar como parâmetro pela nossa Intent. 116 Figura 5.23. Exemplo do mapeamento de um serviço. 5.4.7 AlarmManager São eventos agendados no sistema operacional para serem executados no futuro (LECHETA, 2010). É utilizado quando é necessário executar algo uma vez em determinado horário ou ficar repetindo de tempos em tempos. Quando agendados, ficam ativos no sistema até que sejam explicitamente cancelados, ou o sistema for reiniciado. Para agendar um alarme, primeiro temos que definir uma intent com o BroadcastReceiver que irá responder pelo nosso alarme. Depois temos que capturar o serviço do Android responsável pelo gerenciamento dos alarmes, o AlarmManager. Figura 5.24. Agendando um alarme. 117 Figura 5.25. BroadcastReceiver que será chamado pelo nosso alarme. 5.5. Arquitetura REST de desenvolvimento de aplicações Web Vivemos hoje uma febre de Apps – pequenos aplicativos auto-contidos que tem uma única função e comumente são interfaces para sistemas Web. A maioria desses aplicativos é extremamente dependente de dados para serem úteis e esses dados podem vir dos mais variados lugares – por exemplo, a plataforma Android disponibiliza ao desenvolvedor uma pequena base dados SQLite onde ele pode criar suas tabelas, armazenar e buscar dados, mas cada vez mais esses dados vêm de serviços web. A Web é uma plataforma “orientada a recursos”. Um Recurso pode ser definido como qualquer coisa que é exposta a Web através de um identificador e que possamos manipular (ler e/ou escrever) (WEBBER; PARASTATIDIS, 2010).. Desde sua formalização REST1 vem sendo um termo e, mais adequadamente, uma arquitetura de software de sistemas Web cada vez mais utilizado, estudado e discutido. Esta arquitetura foi proposta pelo Dr. Roy T. Fielding em 2000 e desde então vem sendo adotada em vários sistemas de grande porte como Twitter, Facebook, Flickr e todas as APIs de serviços públicos do Google como Google Agenda, Google Health, Google Data e Google Maps. Veremos a seguir o que é REST e, em seguida, formas de se trabalhar com REST em Java. 5.5.1 Definição O protocolo HTTP, e consequentemente servidores HTTP, é um protocolo simples e sem muitos recursos. Em sua primeira versão ele apresentou endereçamento e statelessness: duas características de seu projeto que o tornou um avanço perante seus rivais e que ainda o mantém escalável mesmo nos mega-sites de hoje (RICHARDSON; RUBY, 2007). Sistemas com esta arquitetura são sistemas Clientes-Servidores comuns, entretanto suas requisições e respostas são construídas ao redor da transferência da representação de recursos. Recursos são os blocos fundamentais de sistemas baseados na web, ao ponto que a Web é considerada como “orientada a recursos” (WEBBER; PARASTATIDIS, 2010). A abstração chave de informação do REST são os recursos. Qualquer informação que pode ser nomeada pode ser um recurso: documentos, imagens, informações sobre o tempo, uma pessoa, e assim sucessivamente (FIELDING, 2000). 1 Representational State Transfer – Transferência de Estado Representacional 118 Um dos recursos mais comuns da Web são páginas HTML, que em sistemas são comumente a representação de um recurso interno do sistema, por exemplo, uma página de exibição de um vídeo do YouTube é a representação do recurso “vídeo” do sistema. Um mesmo recurso pode ter várias representações diferentes. Utilizando o exemplo do vídeo do YouTube, uma representação é a página HTML onde é mostrado o vídeo, comentários, etc. outra representação é vista quando o vídeo é incorporado em outra página. Nessa representação vemos apenas um player que carrega o vídeo em questão. Outra representação, um pouco menos conhecida, é a representação em XML2 ou mais recentemente JSON3, ambas tecnologias de transição de dados hierárquicos. Devido a essas características REST também requer que as aplicações façam total distinção entre cliente e servidor através da implementação das seguintes características: 1. Cliente-Servidor: Clientes são separados dos Servidores por uma interface uniforme. Essa divisão faz com que o cliente não se preocupe com, por exemplo, armazenamento de dados ao passo que o servidor não se preocupa com interface com o usuário; 2. Stateless (Sem Estado): A comunicação cliente-servidor ocorre sem que nenhum contexto relativo ao cliente seja armazenado no servidor entre as requisições. Cada requisição de cada cliente contém toda informação necessária para atender aquela requisição e a reposta deve conter toda a informação para satisfazer a requisição; 3. Cache: Como é comum na Web, o cliente pode manter um cache das respostas do servidor, portanto é recomendado que as respostas contenham informações sobre se podem e como deve ser feito o cache a fim de evitar que clientes requisitem um mesmo recurso repetidamente. Um bom cache elimina várias interações clienteservidor desnecessárias o que proporciona escalabilidade e performance; 4. Camadas: Um cliente não pode normalmente distinguir se ele está ou não conectado ao servidor final ou apenas a um intermediário. Um sistema com vários servidores permite o balanceamento de carga, caches compartilhados e ajudam a manter uma boa segurança; 5. Código sob demanda (opcional): Servidores podem ser capazes de estender a funcionalidade de um cliente transferindo lógica para que ele execute. Exemplos disso são componentes compilados como Applets Java ou scripts no lado cliente como JavaScript; 6. Interface uniforme: Todo cliente obedece ao mesmo formato de requisição e recebe o mesmo formato de resposta. 2 3 XML – eXtensible Markup Language – Linguagem de marcação extensível. JavaScript Object Notation – Notação de Objetos JavaScript. 119 A arquitetura REST está fundamentada sob o protocolo HTTP e seus métodos. Clientes que acessam recursos informando sua intenção através do método que executam sob o recurso. A maioria dos sistemas hoje em dia segue a seguinte convenção de métodos HTTP, concebidos para simular operações de CRUD: GET: Leitura; POST: Escrita; PUT: Alteração ou Atualização; DELETE: Remoção. Muitas aplicações novas estão utilizando os princípios REST a fim de obterem um bom nível de escalabilidade. Normalmente essas aplicações disponibilizam a seus usuários uma API dos métodos REST disponíveis, suas entradas e suas saídas. Para compreender melhor, vejamos a API de um sistema referência em tecnologia REST: o Twitter. O Twitter disponibiliza em seu website uma extensa e detalhada documentação da API de seu sistema4. uma plataforma de apoio aos desenvolvedores que desejam criar clientes para o Twitter com páginas de ajuda onde são descritos os métodos da API, um guia ao desenvolvedor e ferramentas para ajudá-lo. Chamadas a alguns métodos da API podem ser feitas, para testes, através de um navegador web qualquer. Durante a escrita deste texto, a API do Twitter aceita a seguinte chamada: http://api.twitter.com/1/users/show.xml?screen_name=fat. Para chamar este método da API basta abrir esta URL em qualquer navegador web. Ao abrir esta URL em um navegador, o que é feito é uma chamada a API através do método HTTP GET. Chamadas a qualquer API REST na Web são compostas de 3 partes: 1. A URL correspondente ao método da API http://api.twitter.com/1/users/show 2. Os parâmetros da chamada ao método screen_name = fat 3. O método HTTP utilizado para chamada aquela URL Por padrão, toda transação HTTP dos navegadores atuais utiliza o método GET. A URL é uma chamada ao método “1/users/show” da API do Twitter. Uma particularidade dessa API é a possibilidade de escolha do formato de representação do recurso: XML ou JSON, representado pela “extensão do arquivo” na URL. 4 Disponível em http://dev.twitter.com/ - Acesso em 12 outubro 2011 120 5.5.2. REST em Java Em Java chamadas a APIs REST são feitas utilizando chamadas HTTP simples, normalmente com objetos java.net.URL e java.net.HttpURLConnection. Nos exemplos a seguir utilizaremos a API do Twitter como exemplo. A forma mais simples de se executar uma chamada é a seguinte: String apiCall = "<url de chamada a API>"; URL url = new URL(apiCall); HttpURLConnection conn; conn = (HttpURLConnection) url.openConnection(); Ao invocar o método openConnection() uma requisição HTTP GET é feita a URL especificada em apiCall. Após essa chamada, é importante verificar o código de resposta dado pelo servidor. Normalmente um código de resposta 200 significa que tudo ocorreu como esperado. Outros códigos de resposta podem ser especificados pela API. if (conn.getResponseCode() == 200) { // Tudo ocorreu como esperado. } Dessa forma podemos tomar alguma providência caso ocorra algum erro, e garantir um bom funcionamento da aplicação. Após a confirmação do sucesso da chamada o próximo passo é ler os dados da resposta do servidor. É necessário um cuidado especial com o tipo de dado esperado pois a resposta da API pode ser uma imagem, ou um documento, ou até mesmo um Stream de dados. Vamos considerar que a resposta a nossa chamada está em formato textual, caso qual podemos utilizar a seguinte técnica para captar o resultado num formato mais cômodo para manipulação: BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuilder str = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { str.append(line); } reader.close(); Após o recebimento da resposta, é uma boa prática sempre fechar a conexão junto ao servidor invocando o método disconnect(): conn.disconnect(); Dessa forma podemos fazer chamadas a quaisquer API REST utilizando Java. 121 5.5.2.1 Interpretando Respostas Em Java podemos quase sempre considerar um recurso disponibilizado pelo servidor com um Objeto. Comumente recebemos respostas em XML o qual é uma serialização de um objeto que está no servidor. Devido a isso um ponto que requer especial atenção é a interpretação das respostas. APIs REST utilizam representações do estado de um recurso (um Objeto) do sistema. Nestes casos a forma mais comum de representação é textual, segundo um padrão determinado pela API, XML ou JSON. Existem várias bibliotecas disponíveis para se trabalhar com os formatos XML e JSON, algumas bastante sofisticadas e simples de usar. Para XML temos, por exemplo, uma biblioteca chamada jDOM5 que interpreta XML e dá ao desenvolvedor uma representação do documento XML na forma de um grafo de objetos com uma interface nativa Java que podem ser facilmente percorridos e manipulados. Para JSON há uma biblioteca chamada Gson6 disponibilizada pelo Google que pode transformar Objetos Java em sua representação JSON e vice-versa: Gson gson = new Gson(); String json = gson.toJson(meuObjeto); MeuObjeto obj = gson.fromJson(json, MeuObjeto.class); Através do uso de bibliotecas auxiliares é possível criar objetos concretos Java a partir de representações textuais dos mesmos. A utilização de JSON é recomendada nestes casos pois grande parte dos sistemas REST atuais são voltados para a interatividade entre sistemas de Websites, os quais são em sua maioria feitos utilizando JavaScript, linguagem-mãe da notação JSON e que dá suporte nativo a interpretação e construção de objetos a partir de sua representação textual e vice-versa. Referências bibliográficas ABLESON, W. Frank; Unlocking Android - A Developer’s Guide. Ed. Manning, 2007. BURNETTE, Ed.Hello, Android:Introducing Google`s Mobile Development Plataform. Pragmatic Bookshelf, 2008. Google Android. Disponível em <http://developer.android.com/guide/basics/what-isandroid.html>. Acesso em 12 outubro 2011. LECHETA, Ricardo R. Google Android. São Paulo: Novatec, 2010, 2 ª ed. MEDNIEKS, Zigurd; MEIKE, Blake. Desenvolvimento de Aplicações Android. São Paulo: Novatec, 2009, 1ª ed. MEIER, Reto. Professional Android Application Development. Indianapolis: Wiley Publishing, 2009, 1ª. ed. 5 6 http://www.jdom.org/ - Acesso em 12 outubro 2011 http://code.google.com/p/google-gson/ - Acesso em 12 outubro 2011 122 PEREIRA, Lúcio C. Oliva. Android para Desenvolvedores. São Paulo: Brasport, 2009, 1ª ed. WEBBER, Jim; PARASTATIDIS, Savas; ROBINSON Ian. REST in Practice: Hypermidia and Systems Architecture. O’Reilly Media, 2010. RICHARDSON, Leonard; RUBY Sam. RESTful web services. O’Reilly Media, 2007. FIELDING, Roy; Architectural Styles and the Design of Network-based Software Architectures. Dissertação de Doutorado, University of California, 2000. 123 Capítulo 6 Desenvolvendo aplicações multi-tenancy para computação em núvem Josino Rodrigues Neto, Vinícius Cardozo Garcia e Wilton dos Santos Oliveira Abstract This course focuses on presenting the main concepts of multi-tenancy and use a practical approach to demonstrate the implementation of an application based on a multi-tenancy architecture. Thus, this chapter presents the definitions and key characteristics of cloud computing, SaaS service model and a multi-tenancy approach for SaaS. Resumo Este minicurso tem como foco principal apresentar os principais conceitos de multitenancy e utilizar uma abordagem prática para demonstrar a implementação de uma aplicação baseada em uma arquitetura multi-tenancy. Assim, este capítulo apresenta as definições e as características essenciais da computação em nuvem, o modelos de serviço SaaS e uma abordagem multi-tenancy para SaaS. 6.1 Introdução Com o ritmo de desenvolvimento da sociedade humana moderna, serviços básicos e essenciais são quase todos entregues de uma forma completamente transparente. Serviços de utilidade pública como água, eletricidade e telefone tornaram-se fundamentais para nossa vida diária e são explorados através de um modelo de pagamento baseado no uso. As infraestruturas existentes permitem entregar os serviços em qualquer lugar e a qualquer hora, de forma que possamos simplesmente acender a luz, abrir a torneira ou fazer uma ligação para qualquer lugar. O uso desses serviços é cobrado de acordo com as diferentes políticas para o usuário final. A mesma idéia de 124 utilidade tem sido aplicada na área da informática e uma mudança consistente neste sentido tem sido feita com a disseminação da computação em nuvem. Segundo o NIST, Cloud Computing(Computação em núvem) é composta por cinco características essenciais: self-service sob demanda; amplo acesso a rede; pooling de recursos; elasticidade rápida. Ainda segundo o NIST, Cloud Computing é dividido em três modelos de serviço: Software como serviço (SaaS); Plataforma como serviço (PaaS); Infraestrutura como Serviço (IaaS). Nesse contexto, este minicurso tem como foco principal apresentar os principais conceitos de multi-tenancy e utilizar uma abordagem prática para demonstrar a implementação de uma aplicação baseada em uma arquitetura multi-tenancy. Assim, este capítulo apresenta as definições e as características essenciais da computação em nuvem, o modelos de serviço SaaS e uma abordagem multi-tenancy para SaaS. Este capítulo está organizado no seguinte formato: A seção 1.2apresenta os conceitos fundamentais de cloud computing, Software como serviço e Multi-tenancy. A seção 1.3 apresenta os componentes de uma arquitetura multi-tenancy. A seção 1.4 apresenta o passo a passo para a implementação de um protótipo de aplicação multi-tenancy com Grails. Finalmente, a seção 1.5 apresenta as conclusões finais. 6.2 Conceitos Fundamentais 6.2.1 Cloud Computing .1 Na computação em nuvem os recursos de TI são fornecidos como um serviço, permitindo aos usuários o acessa-los sem a necessidade de conhecimento sobre a tecnologia utilizada. Assim, os usuários e as empresas passaram a acessar os serviços sob demanda e independente de localização, o que aumentou a quantidade de serviços disponíveis. Computação em nuvem pretende ser global e prover serviços para todos, desde o usuário final que hospeda seus documentos pessoais na Internet até empresas que terceirizarão toda a parte de TI para outras empresas. Mas o que é Cloud Computing? Segundo Taurion o termo computação em nuvem surgiu em 2006 em uma palestra de Eric Schmidt, da Google, sobre como sua empresa gerenciava seus data centers. Hoje, computação em nuvem, se apresenta como o cerne de um movimento de profundas transformações do mundo da tecnologia (TAURION, 2009). Segundo o NIST(National Institute of Standards and Technology) , Cloud Computing é composta por cinco características essenciais: 125 Self-service sob demanda: O usuário pode adquirir unilateralmente recurso computacional, como tempo de processamento no servidor ou armazenamento na rede na medida em que necessite e sem precisar de interação humana com os provedores de cada serviço. Amplo acesso a rede: recursos estão disponíveis através da rede e acessados por meio de mecanismos que promovam o padrão utilizado por plataformas heterogêneas (por exemplo, telefones celulares, laptops e PDAs). Pooling de recursos: o provedor de recursos de computação são agrupados para atender vários consumidores através de um modelo multi-tenant, com diferentes recursos físicos e virtuais atribuídos dinamicamente e novamente de acordo com a demanda do consumidor. Há um senso de independência local em que o cliente geralmente não tem nenhum controle ou conhecimento sobre a localização exata dos recursos disponibilizados, mas pode ser capaz de especificar o local em um nível maior de abstração (por exemplo, país, estado ou data center). Exemplos de recursos incluem o armazenamento, processamento, memória, largura de banda de rede e máquinas virtuais. Elasticidade rápida: recursos podem ser adquiridos de forma rápida e elástica, em alguns casos automaticamente, caso haja a necessidade de escalar com o aumento da demanda, e liberados, na retração dessa demanda. Para os usuários, os recursos disponíveis para uso parecem ser ilimitados e podem ser adquiridos em qualquer quantidade e a qualquer momento. Serviço medido: sistemas em nuvem automaticamente controlam e otimizam a utilização dos recursos, alavancando a capacidade de medição em algum nível de abstração adequado para o tipo de serviço (por exemplo, armazenamento, processamento, largura de banda, e contas de usuários ativos). Uso de recursos pode ser monitorado, controlado e relatado a existência de transparência para o fornecedor e o consumidor do serviço utilizado. Ainda segundo o NIST, Cloud Computing é dividido em três modelos de serviço: Software como Serviço (Software as a Service - SaaS): A capacidade fornecida ao consumidor é usar as aplicações do fornecedor que funcionam em uma infraestrutura da nuvem. As aplicações são acessíveis dos vários dispositivos do cliente através de uma relação do thin client tal como um browser web. O consumidor não administra ou controla a infraestrutura básica, incluindo nuvens de rede, servidores, sistemas operacionais, armazenamento, ou mesmo capacidades de aplicação individual, com a possível exceção de limitada aplicação específica e definições de configuração de usuários da aplicação. Plataforma como Serviço (Plataform as a Service - PaaS): A capacidade fornecida ao consumidor é desdobrar na nuvem a infraestrutura consumidor criada ou as aplicações adquiridas criadas usando linguagens de programação e as ferramentas suportadas pelo fornecedor. O consumidor não administrar ou 126 controlar a infraestrutura básica, incluindo nuvens de rede, servidores, sistemas operacionais, ou armazenamento, mas tem controle sobre os aplicativos utilizados e eventualmente hospedagem de aplicativos e configurações de ambiente. Infraestrutura como Serviço (Infraestructure as a Service - IaaS) : A capacidade prevista para o consumidor é a prestação de transformação, armazenamento, redes e outros recursos computacionais fundamental que o consumidor seja capaz de implantar e executar programas arbitrários, que podem incluir sistemas operacionais e aplicativos. O consumidor não administra ou controla a infraestrutura de nuvem subjacente, mas tem controle sobre os sistemas operacionais, armazenamento, aplicativos implantados, e, eventualmente, o controle limitado de componentes de rede selecionar (por exemplo, firewalls host). Esse trabalho não pretende abordar exaustivamente todos os modelos de serviço de Cloud Computing, esse trabalho tem como foco principal SaaS, especificamente a aplicação da arquitetura multi-tenancy. 6.2.2 Software como Serviço(SaaS) .1 Por décadas, as companhias utilizavam seu software em sua própria infraestrutura e eram responsáveis por todas as atividades de manutenção, integridade, escalabilidade, disponibilidade e uma série de encargos relacionados ao gerenciamento de TI na empresa. Além de custos relacionados à compra de licenças e atualizações, as empresas tinham que adequar sua infraestrutura e contratar pessoas especializadas para as atividades de gerenciamento. Foi nesse contexto que surgiu o conceito de SaaS. Neste modelo, a funcionalidade da aplicação é oferecida através de um modelo de assinatura pela Internet. O cliente não se torna dono do software, ao invés disso, ele aluga a solução total que é oferecida remotamente. Podemos dividir as aplicações de software como serviço em duas categorias (CHONG e CARRARO, 2006): Serviços para linha de negócios (line-of-business): oferecidos a empresas e organizações de todos os tamanhos. Os serviços de linha de negócios geralmente são soluções de negócios grandes e personalizáveis direcionadas para facilitar processos de negócios como finanças, gerenciamento da cadeia de suprimentos e relações com o cliente. Normalmente esses serviços são vendidos aos clientes como assinatura. Um exemplo desse tipo de serviço são as soluções personalizáveis do Salesforce (SALESFORCE, 2000). Serviços orientados ao consumidor: oferecidos ao público em geral. Os serviços orientados a cliente às vezes são vendidos como assinatura, mas geralmente são fornecidos sem custo e financiados por anúncios. Um outro 127 exemplo desse tipo de serviço são os serviços oferecidos pelo Google(Gmail, Google docs, Google Agenda, etc). Apesar do conceito de SaaS não ser uma coisa nova, esse conceito ganhou força somente por volta de 2005 e 2006 com o surgimento e viabilidade de uso de novas tecnologias como web 2.0, rich internet application(RIA), SOA, cloud computing, virtualização, etc. Implementar o conceito de SaaS nem sempre é tão simples como parece. Chong (CHONG e CARRARO, 2006) propõe 4 níveis de maturidade para aplicações que utilizam o modelo de SaaS: Figura 6.1 - Níveis de Maturidade SaaS (CHONG e CARRARO, 2006) Nível 1 – Ad-Hoc/Personalizado: O primeiro nível de maturidade é semelhante ao modelo de entrega de software do provedor de serviços de aplicativos (ASP) tradicional, que data da década de 1990. Nesse nível, cada cliente tem a sua própria versão personalizada do aplicativo hospedado e executa a sua própria instância do aplicativo nos servidores do host. Pensando em arquitetura, software nesse nível de maturidade é muito semelhante ao software de linha de negócios vendido tradicionalmente, em que diferentes clientes em uma organização conectam a uma instância única em execução no servidor, mas essa instância é totalmente independente de quaisquer outras instâncias ou processos que o host esteja executando para os seus outros clientes. Nível 2 – Configurável: No segundo nível de maturidade, o fornecedor hospeda uma instância separada do aplicativo para cada inquilino enquanto que no 128 primeiro nível cada instância é personalizada individualmente para o inquilino, neste nível todas as instâncias utilizam a mesma implementação de código e o fornecedor atende as necessidades dos clientes fornecendo opções de configuração detalhadas que permitem ao cliente alterar a aparência e o comportamento do aplicativo para os seus usuários. Apesar de serem idênticas umas às outras no nível do código, cada instância permanece totalmente isolada de todas as demais. Nível 3 – Configurável e eficiente para vários tenants: No terceiro nível de maturidade, o fornecedor executa uma única instância que serve a todos os clientes, com metadados configuráveis fornecendo uma experiência de usuário e conjunto de recursos exclusivos para cada um. Políticas de autorização e de segurança garantem que os dados de cada cliente sejam mantidos separados dos de outros clientes e que, da perspectiva do usuário final, não exista qualquer indicação de que a instância do aplicativo esteja sendo compartilhada entre vários tenants. Nível 4 – Escalonável, configurável e eficiente para vários tenants: No quarto e último nível de maturidade, o fornecedor hospeda vários clientes em um ambiente com carga balanceada de instâncias idênticas, com os dados de cada cliente mantidos separados e com metadados configuráveis fornecendo uma experiência do usuário e um conjunto de recursos exclusivos para cada cliente. Um sistema de SaaS é escalonável para um número de clientes arbitrariamente grande, uma vez que a quantidade de servidores e instâncias no lado do fornecedor pode ser aumentada ou diminuída conforme necessário para corresponder à demanda sem a necessidade de remodelar a arquitetura aplicativo, além do que as alterações ou correções podem ser transmitidas para milhares de tenants tão facilmente quanto para um único tenant. Normalmente se esperaria que o quarto nível fosse a meta definitiva para qualquer aplicativo de SaaS, mas não é sempre assim. É necessário verificar as necessidades operacionais, arquiteturais e de negócio relacionadas à aplicação. Uma abordagem single-tenant faz sentido financeiramente? O seu aplicativo pode ser feito para executar em uma única instância lógica? Você pode garantir os seus contratos de nível de serviço (SLAs) sem isolamento das aplicações? Essas são questões que devem ser respondidas quando se pretende adotar o modelo SaaS. 6.2.3 Multi-tenancy .1 Multi-tenancy é uma abordagem organizacional para aplicações SaaS. Bezemer (BEZEMER e ZAIDMAN, 2010) define multi-tenancy como aplicações que permitem o compartilhamento dos mesmos recursos de hardware, através do compartilhamento da aplicação e da instância do banco de dados, enquanto permite configurar a aplicação para atender às necessidades do cliente como se estivesse executando em um ambiente dedicado. Tenant é uma entidade organizacional que aluga uma aplicação multi-tenancy. Normalmente, um tenant agrupa um número de usuários que são os stakeholders da organização. 129 A definição acima foca no que nós consideramos aspectos em aplicações multi-tenancy: Possibilidade de compartilhamento de recursos de hardware, permitindo a redução de custos (HU WANG, JIE GUO, et al., 2008) (WARFIELD, 2007). Alto grau de configurabilidade, permitindo que cada consumidor customize sua própria interface e seu workflow na aplicação (NITU, 2009)(JANSEN, HOUBEN e BRINKKEMPER, 2010). Uma abordagem arquitetural na qual os tenants fazem uso de uma única aplicação e banco de dados (KWOK, NGUYEN e LAM, 2008). Como multi-tenancy é um conceito relativamente novo, é muito comum as pessoas confundirem multi-tenancy com outros conceitos já existentes. Multi-tenancy não é multi-usuário. Em uma aplicação multi-usuário nós assumimos que os usuários estão usando a mesma aplicação com opções de acesso limitadas. Em uma aplicação multi-tenancy nós assumimos que os tenants tem um algo grau de configuração, dependendo da definição dessas configurações, dois tenants pode possuir aparência e workflows diferentes. Um argumento adicional para essa distinção é que o SLA(Service Level Agreement) para cada tenant pode ser diferente (LIN, SUN, et al., 2009). Outra abordagem que contrasta com multi-tenancy é a abordagem multiinstance. Com o aumento da popularidade de das tecnologias de virtualização e de cloud Computing, multi-instance é a forma fácil de criar aplicações parecidas com multitenancy. É necessário apenas criar uma imagem de maquina virtual com a aplicação préconfigurada e, logo em seguida criar instâncias apartir dessa imagem. A abordagem multi-instance é uma melhor abordagem quando o número de tenants for pequeno (JIE GUO, SUN, et al., 2007). 6.3 Arquitetura Multi-tenancy 1 Nesse minicurso iremos tomar como base a arquitetura apresentada na Figura 6.2. Aqui vemos que multi-tenancy afeta quase todas as camadas de uma aplicação típica, e como tal, possui um grande potencial para se tornar um interesse transversal. Para manter o impacto sobre o código (complexidade), as implementações de componentes multitenancy devem ser separadas da lógica dos tenants o tanto quanto possível. Caso contrário, a manutenção pode se tornar um pesadelo, porque: • Implementar código de requisitos da arquitetura multi-tenancy juntamente com a lógica de negócio dos tenants em todas as camadas de aplicação, o que exige que todos os desenvolvedores sejam reeducados sobre multi-tenancy; • Misturar multi-tenancy com código de tenants leva ao aumento da complexidade do código, pois é mais difícil manter o controle de onde o código multi-tenancy é introduzido. Estes dos problemas podem ser superados integrando cuidadosamente multi-tenancy na arquitetura. No restante desta seção, descrevemos os componentes da arquitetura 130 abordada por Bezemer (BEZEMER e ZAIDMAN, 2010) para implementação de multitenancy como um interesse transversal. A. Autenticação Pelo motivo de uma aplicação multi-tenant ter apenas uma aplicação e instancia de banco de dados, todos os tenants usam o mesmo ambiente físico. A fim de ser capaz de oferecer customização do ambiente e ter certeza de que os tenants podem acessar somente os seus próprios dados, tenants devem ser autenticados. Enquanto autenticação de usuário é, possivelmente, já presente na aplicação de destino, um mecanismo separado de autenticação de tenants específicos pode ser necessário, por duas razões: (1) geralmente é muito mais fácil introduzir um mecanismo de autenticação adicional, do que mudar um já existente, e (2) autenticação de tenants permite que um único usuário faça parte de mais do que uma organização lógica, o que estende a idéia de autenticação de usuários com “grupos”. B. Configuração Em uma aplicação multi-tenancy a customização deve ser possível através de configuração. A fim de permitir que o usuário tenha uma experiência como se ele estivesse trabalhando em um ambiente dedicado, é necessário permitir pelo menos os tipos de configuração seguintes: Estilo de Layout(Layout Style) – O componente de configuração de estilo de layout permite o uso de temas e estilos específicos. Configuração Geral (General Configuration) – O componente de configuração geral permite a especificação de configurações específicas, como configurações de chave de criptografia e detalhes do perfil pessoal. Entrada e saída de arquivo (File I/O) – O componente de configuração de I/O de arquivo permite a especificação de caminhos de arquivos, que podem ser usados para, por exemplo, geração de relatório. Fluxo de trabalho (Workflow) – O componente de configuração de fluxo de trabalho permite a configuração de fluxos específicos. Por exemplo, configuração de fluxos é necessária em uma aplicação de planejamento de recursos empresariais – ERP, em que o fluxo de requisições pode variar significativamente para diferentes companhias. C. Banco de dados (Database) Em uma aplicação multi-tenancy há uma grande exigência para o isolamento dos dados. Porque todos os tenants usam a mesma instância de um banco de dados é necessário ter certeza de que eles podem acessar somente seus próprios dados. Atualmente sistemas de gerenciamento de dados (Data Base Management Systems – DBMS) de prateleira não são capazes de lidar com multi-tenancy, isso deve ser feito em uma camada entre a 131 camada lógica de negócios e o pool de banco de dados de aplicações. As principais tarefas dessa camada são as seguintes: Criação de novos tenants no banco de dados – Se a aplicação armazena ou recupera dados que podem ser de tenants específicos, é tarefa da camada de banco de dados criar os registros do banco de dados correspondente quando um novo tenant se inscreveu para a aplicação. Adaptação de consulta – A fim de prover um isolamento de dados adequado, a camada de banco de dados deve ter certeza que consultas são ajustadas de forma que cada tenant possa acessar somente seus próprios registros. Balanço de carga – Para melhorar o desempenho de uma aplicação multi-tenancy é necessário um balanceamento de carga eficiente para o pool de banco de dados. Note que qualquer acordo feito no SLA de um tenant e quaisquer restrições impostas pela legislação do país onde o tenant está localizado deve ser satisfeita. Além disso, a aplicação pode ter requisitos de onde os dados de um tenant estão sendo armazenados, por exemplo, para a geração de relatórios. Estes requisitos dificultam o uso de algoritmos de balanceamento de carga existentes. Por outro lado, nossa expectativa é a de que é possível criar algoritmos de balanceamento de carga mais eficientes usando as informações que possuímos sobre os tenants. Figura 6.2- Arquitetura Multi-tenancy (BEZEMER e ZAIDMAN, 2010) 132 6.4 Implementando um protótipo de aplicação multi-tenancy com Grails .1 6.4.1 Introdução ao Framework Grails .1 Para implementar um protótipo que demonstre a aplicabilidade da arquitetura multitenancy será utilizado o framework Grails. O Grails é um framework web open-source que utiliza a linguagem Groovy, e outros frameworks consagrados como Hibernate, Spring e Sitemesh, como mostrado na Figura 6.3. O Grails foi projetado para desenvolver aplicações CRUD (Create, Read, Update e Delete) de forma simples e ágil, utilizando o modelo de “escrever código por convenção” introduzido pelo Ruby on Rails. O Grails está se propondo a trazer a produtividade do Ruby on Rails para a plataforma Java, porém ele possui uma grande vantagem, sendo que a linguagem Groovy roda sobre uma JVM, e pode utilizar classes Java e as diversas APIs que existem normalmente. Groovy (padronizado pela JSR-241) é uma linguagem dinâmica e ágil para a plataforma Java, que possui muitas características de linguagens de script como Ruby, Python e Smalltalk, e ainda pode utilizar classes Java facilmente. Linguagens de script estão ganhando cada vez mais popularidade, devido a quantidade reduzida de código fonte necessário para implementar determinadas funcionalidades, se comparado com uma implementação em Java. Figura 6.3 - Arquitetura Grails 6.4.2 Instalando Grails .1 Instalar o Grails framework pode ser considerada uma tarefa simples. O passo a passo para a instalação do Grails framework é apresentado na Figura 6.4. 133 Uma vez instalado, é possível abrir um prompt de comando e testar sua instalação utilizando, por exemplo, o comando “grails help”. Figura 6.4 - Instalação Grails Para criar uma aplicação em grails o desenvolvedor dever executar o comando: grails create-app multitenantapp Esse comando cria um projeto de aplicação grails contendo um conjunto de arquivos e diretórios necessários para executar uma aplicação grails. Os diretórios criados e suas descrições podem ser vistos com mais detalhes na Tabela 6.1. Tabela 6.1 - Estrutura de pastas de uma aplicação Grails Pasta + grails-app + conf + controllers + domain + i18n Descrição Contém as configurações da aplicação, como a configuração de banco (DataSource.groovy) e onde podem ser feitas as configurações de inicialização(BootStrap.groovy) entre outros. Contém as classes de controller(controladores) da aplicação. Contém as classes de Domínio, ou modelos. Contém arquivos inerentes a internacionalização. 134 + services + taglib + views + layouts + grails-tests + lib + src + groovy + java + web-app + css + images + js + WEB-INF + index.jsp Nessa pasta ficam as classes utilizadas na camada de serviços do Grails, caso sejam criadas. Contém as TagLibs criadas pelo usuário Contém os arquivos “.gsp”(Groovy Server Pages) utilizados para cada classe de domínio. Nessa pasta ficam os templates da aplicação. Templates em Grails são views incluídas em outras views. Parta que contém os testes unitários Contém as libs externas, como por exemplo os drivers de conexão aos bancos de dados Contém classes groovy que não se encaixam nem em Domain, Controller ou Service. Contém classes java que poderão ser usadas na aplicação Contém os arquivos .css Imagens JavaScript Arquivos relacionados ao deploy O Index da app Para executar a aplicação criada deverão ser executados os seguintes comandos: >> cd multitenantapp >> grails run-app O comando “cd multitenantapp” irá abrir o diretório da aplicação e o comando “grails run-app” executará a aplicação. Para adicionar um cadastro de uma entidade (CRUD) à aplicação, deve ser criado uma classe de domínio e em seguida uma classe de controller que irá gerenciar as requisições para o cadastro desta entidade. >> grails create-domain-class Product >> grails create-controller multitenantapp Product O primeiro comando cria uma classe de domínio no seguinte endereço >> grails create-domain-class Product “..\multitenantapp\grails-app\domain\multitenantapp\Product.groovy”. Esse arquivo >> grails create-controller multitenantapp Product deverá ser alterado para o código da Figura 6.5 a seguir: 135 Figura 6.5 - código Product.groovy O segundo comando cria a classe de controller, que deverá ser alterada para o código a da seguir: Figura 6.6 - Código da Classe Controller Uma vez criadas as classes de domínio e controller,o comando “grails run-app” deverá ser executado novamente. Após isso será possível acessar o link “http://localhost:8080/multitenantapp” para visualizar a primeira tela da aplicação, mostrado na Figura 6.7, com o CRUD da entidade criada (Product). Figura 6.7 - Tela Inicial de uma aplicação Grails Assim, observamos que com poucos comandos temos uma pequena aplicação grails funcionando. 6.4.3. Componentes de uma aplicação multi-tenancy .1 Para implementar uma aplicação multi-tenancy é necessário implementar autenticação, controle de configurabilidade e controle de acesso a dados que atendam às necessidades de uma aplicação multi-tenancy que foram descritos nos tópicos anteriores. A fim de diminuir a complexidade da aplicação de exemplo não será implementado o controle de configurabilidade em nosso aplicativo de demonstração. 136 Grails disponibiliza uma arquitetura de plugins que promove o reuso e o aumento de produtividade. Dentre os plug-ins disponíveis existem plugins que facilitam muito a implementação de aplicações multi-tenancy em Grails. São eles Multi-tenant Plugin (Core) e Multi-tenant Spring Security Integration. Esses 2 plugins extendem as funcionalidades de controle de acesso a dados e autenticação de forma a atender os requisitos de aplicações multi-tenant. Para dar continuidade ao nosso exemplo prático será instalado agora um plugin que adiciona a funcionalidade de autenticação em nossa aplicação. Para isso é necessário a execução dos comandos a seguir. >> grails install-plugin spring-security-core 1.1.2 >> s2-quickstart login SecUser Role Requestmap Para que esse plugin funcione é necessário que o código no arquivo BootStrap.groovy existente na pasta “conf” do seu projeto esteja semelhante ao código apresentado na Figura 6.8. Em Seguida já é possível re-executar a aplicação com o comando “run-app” e verificar que para utilizar a aplicação agora é necessário utilizar um login e senha. As configurações adicionadas no arquivo BootStrap.groovy criaram no banco de dados 2 usuários(user1 e user2), ambos com senha “123”. Figura 6.8 - BootStrap.groovy (Single Tenant) O próximo passo é instalar os plug-ins necessários para tornar nosso exemplo uma aplicação multi-tenant. Para isso é necessário a execução do comando a seguir em nosso projeto. >> grails install-plugin multi-tenant-spring-security Após a instalação do plugin são necessárias algumas configurações adicionais: 137 1. Adicionar o campo “Integer userTenantId” na classe SecUser.groovy. Isso fará com que cada usuário esteja associado a um tenant específico. Ao cadastrar um usuário, ele deverá possuir um valor para o campo userTenantId, isso irá indicar quais registros do banco de dados ele terá acesso. 2. Anotar a classe Product.groovy com “@MultiTenant”. Em uma aplicação multitenancy poderemos ter entidades que terão ou não características multi-tenancy. Ou seja, mesmo em uma aplicação multi-tenancy poderemos ter entidades que terão seus registros compartilhados entre todos os tenants. Para indicar quais entidades terão seus registros filtrados por tenant utilizamos a anotação @MultiTenant. 3. Adicionar no bloco de constraints da classe Product.groovy a seguinte linha de código “tenantId(display:false)”. Isso fará com que o campo tenantId, que é criado dinamicamente não seja alterado pelos usuários. Dado que o gerenciamento de seu valor é feito pelo plugin. 4. Altera a classe BootStrap.groovy para que o valor do atributo userTenantId seja informado na criação dos 2 usuários padrões do sistema. O código da classe BootStrap.groovy deverá ficar semelhante ao código apresentado na Figura 6.9. Essa alteração fará com que cada usuário padrão do sistema esteja em um tenant diferente. 5. Adicionar o código da Figura 6.10 ao final do arquivo Config.groovy, localizado na pasta “conf” do projeto. Figura 6.9 - BootStrap.groovy (Multi-tenant) Figura 6.10 - Alteração Config.groovy 138 Seguido estes passos, as funcionalidades básicas de uma aplicação multi-tenancy estão presentes no nosso exemplo. Para validar o funcionamento é necessário efetuar o login com o usuário user1(senha=123) e cadastrar alguns produtos. Em seguida efetuar login com o usuário user2 e, também, efetuar alguns cadastros no sistema. Será possível ver que os dados cadastrados efetuados pelo usuário user1 não serão visualizados pelo usuário user2, e vice versa. Isto ocorre porque quando esses usuários foram criados colocados em tenants diferentes. 6.5. Conclusão .1 Tomando como base a fundamentação teórica e o exemplo prático apresentado, concluímos que é possível implementar uma aplicação multi-tenancy de forma produtiva e com alto grau de reuso de código. Apesar do exemplo prático ser implementado em Groovy e Grails, essa arquitetura pode ser replicada em várias outras linguagens existentes no mercado. Embora os esforços de pesquisa na área de SaaS e multi-tenancy estejam crescendo bastante ainda existem vários pontos a serem explorados e evoluídos, como avaliação de desempenho, monitoramento de tenants, escalabilidade, migração de aplicações legadas para a arquitetura multi-tenancy, entre outros. 6.6. Referências .1 ABDUL-JAWAD,. Groovy and Grails Recipes. Berkely: Apress, 2009. BEZEMER, C.-P.; ZAIDMAN, A. Multi-tenant SaaS applications: maintenance dream or nightmare? ERCIM Workshop on Software Evolution (EVOL) and International Workshop on Principles of Software Evolution (IWPSE), New York, 2010. CHONG , ; CARRARO,. Architecture Strategies for Catching the Long Tail, 2006. Disponivel em: <http://msdn.microsoft.com/en-us/library/aa479069.aspx>. Acesso em: 2011 out. 01. HU WANG, Z. et al. A Study and Performance Evaluation of the Multi-Tenant Data Tier Design Patterns for Service Oriented Computing. ICEBE '08 Proceedings of the 2008 IEEE International Conference on e-Business Engineering, Washington, DC, 2008. 94-101. JANSEN, S.; HOUBEN, G.-J.; BRINKKEMPER, S. Customization realization in multi-tenant web applications: case studies from the library sector. Proceeding ICWE'10 Proceedings of the 10th international conference on Web engineering, Berlin, Heidelberg, 2010. 445-459. JIE GUO, et al. A Framework for Native Multi-Tenancy Application Development and Management. 9th IEEE International Conference on E-Commerce Technology and the 4th IEEE International Conference on Enterprise Computing, E-Commerce, and E-Services, Tokyo, 2007. 551 - 558. KLEIN, D. Grails: A Quick-Start Guide. [S.l.]: Pragmatic Bookshelf, 2009. KWOK, T.; NGUYEN, T.; LAM, L. A Software as a Service with Multi-tenancy Support for an Electronic Contract Management Application. International Conference on Services Computing. Washington: IEEE Computer Society. 2008. p. 179-186. 139 LIN, H. et al. Feedback-Control-Based Performance Regulation for Multi-Tenant Applications. 15th International Conference on Parallel and Distributed Systems (ICPADS). Shenzhen: IEEE. 2009. p. 134 - 141. MELL , P.; GRANCE,. Draft nist working definition of cloud computing - v15. National Insitute of Standards and Technology, 2009. Disponivel em: <http://www.nist.gov/itl/cloud/upload/cloud-def-v15.pdf>. Acesso em: 1 out. 2011. NITU. Configurability in SaaS (software as a service) applications. Proceedings of the 2nd annual India Software Engineering Conference (ISEC), Pune, India, 2009. 1926. ROCHER, G. K. The Definitive Guide to Grails. Berkely: Apress, 2006. SALESFORCE, 2000. Disponivel em: <http://www.salesforce.com>. Acesso em: 2011 out. 1. SMITH, G.; LEDBROOK, P. Grails in Action. Greenwich: Manning Publications, 2009. TAURION, C. Cloud Computing: Computação em Nuvem: Transformando o mundo da tecnologia da informação. Rio de Janeiro: Brasport, 2009. WARFIELD, B. Multitenancy Can Have a 16:1 Cost Advantage Over Single-Tenant. SmoothSpan Blog, 2007. Disponivel em: <http://smoothspan.wordpress.com/2007/10/28/multitenancy-can-have-a-161-costadvantage-over-single-tenant/>. Acesso em: 17 out. 2011. 140 Capítulo 7 Introducão a Agentes Autônomos e Sistemas Multiagentes 1 Samy Sá1, João Alcântara1 Departamento de Computação – Universidade Federal do Ceará (UFC) Caixa Postal 12.166 – 60.455-970 – Fortaleza – CE – Brazil {samy,jnando}@lia.ufc.br Abstract. The goal of this chapter is to introduce the main concepts in the area of autonomous agents and multiagent systems. Starting with the question "What is an agent?", we discuss the key aspects of building agents and agent societies. For each such concept, we provide some intuition about it, its formalization, illustrations and examples. In the beginning, we consider the properties of a single agent and its relation towards the environment in which it exists. Next, we step up to observe agents societies and the peculiarities that emerge from the plurality of autonomous self-interested entities. In such situations, it is possible that agents get involved in situations of conflict or cooperation, or that some coordination of their actions is required to assure their goals can be achieved. Resumo. O objetivo deste capítulo é introduzir os principais conceitos da área de agentes autônomos e sistemas multiagentes. Começando pela pergunta "O que é um agente?", abordamos os aspectos-chave para a contrução de agentes autônomos e sociedades de agentes. Para cada conceito abordado, fornecemos uma intuição sobre o mesmo, sua formalização, ilustrações e exemplos. Inicialmente, consideramos as propriedades de um único agente e sua relação com o ambiente em que existe. Em seguida, passamos a observar sociedades de agentes e as peculiaridades emergentes da pluralidade de entidades autônomas dotadas de interesses próprios. Em tais situações, é possível que os agentes se envolvam em situações de disputa ou cooperação, ou que precisem coordenar suas ações para garantir que seus objetivos possam ser cumpridos. 7.1. O que são agentes? Nesta seção, vamos definir o que são agentes e abordar alguns aspectos sobre como construí-los. No entanto, devemos destacar que a definição de agente é controversa, pois depende muito do domínio do problema a ser explorado. Apesar disso, todos nós iremos concordar que uma característica central que todo agente deve ter é o da autonomia. Mas o que vem a ser essa autonomia? Responder essa pergunta não é fácil e irá depender mais uma vez do tipo de problema que estamos lidando. Para alguns problemas, autonomia pode ser entendida como a capacidade de aprender a partir da própria experiência; já em outras aplicações, aprendizagem é um aspecto a ser evitado. Começaremos, então, com uma definição de agentes adaptada de [Wooldridge and Jennings 1995]: Um agente é um sistema computacional que está situado em algum ambiente e é capaz de ações autonômas nesse ambiente com o propósito de alcançar os objetivos que lhe foram delegados. O que precisamos comprender é que agentes devem decidir por conta própria o que precisa ser feito para alcançar os objetivos propostos em vez de receber comandos para tal fim. Em suma, um agente deve ser 141 não só capaz de tomar decisões, mas também de escolher que ações tomar e quando executá-las. Figura 7.1. Um agente em seu ambiente Como ilustrado na Figura 7.1, um agente percebe o ambiente no qual ele está inserido através de sensores. Como resultado dessa percepção, o agente produz como saída uma ação que irá, por sua vez, alterar o ambiente. Esse processo de percepção/ação continua ciclicamente e é a base do funcionamento de um agente. Não obstante, na maioria dos problemas de interesse prático, um agente irá possuir um controle parcial sobre o ambiente. Isso quer dizer que a mesma ação executada duas vezes em circunstâncias aparentemente idênticas podem ocasionar resultados completamente diferentes, podendo, em particular, falhar na obtenção dos resultados esperados. Podemos, assim, dizer que os ambientes em geral são nãodeterminísticos. No presente contexto, devemos compreender autonomia como a habilidade de decidir como agir para alcançar os objetivos delegados. Mas até que ponto um agente é autônomo? Um agente, por exemplo, não deve ter autonomia para escolher o objetivo a ser alcançado pelo sistema; sua autonomia limita-se, portanto, a buscar os meios para alcançar esse objetivo. Além disso, nem todo agente possui o mesmo nível de autonomia. Por exemplo, um agente participante de uma operação médica não deve ter a mesma autonomia de um agente responsável por desligar os quartos de um hotel quando não houver ninguém presente. Mais recentemente, tem-se recorrido à ideia de agentes com a autonomia ajustável de acordo com as circunstâncias. O princípio geral é que um agente deve entregar o poder de tomar decisão para uma autoridade maior (um ser humano) quando Ele acredita que isso trará grandes benefícios; Ambiente com alto grau de incerteza; A decisão pode causar danos; Incapacidade do agente de tomar decisão. 7.1.1. Agentes Inteligentes Com base no que foi exposto, podemos perceber que a noção de agente é bastante abrangente e vai de um simples interruptor de luz ou um termostato ao complexo sistema de controle de uma nave espacial não-tripulada. No entanto, nem todos esses agentes (como é o caso do termostato) são inteligentes. Mas o que seria um agente inteligente? De acordo com [Wooldridge and Jennings 1995], um agente é inteligente se ele possui • Reatividade 142 • Proatividade • Habilidade Social 7.1.1.1. Reatividade Agentes inteligentes devem perceber seu ambiente e responder atempadamente às mudanças ocorridas nele para satisfazer os seus objetivos. Agora se as únicas mudanças que ocorrem no ambiente são resultantes das ações do agente, ele não precisará se preocupar com essas mudanças, pois as terá sobre controle. Não obstante, o mundo não é bem assim: os ambientes são normalmente dinâmicos e as mudanças ocorrem independentes do agente, dificultando sobremaneira o desenvolvimento de sistemas computacionais eficientes. Podemos definir um sistema reativo como aquele que mantém uma interação contínua com o seu ambiente e responde em tempo hábil às mudanças que ocorrem nele. 7.1.1.2. Proatividade Desenvolver agentes reativos é considerado relativamente simples na medida que para cada estímulo percebido do ambiente, haja uma regra que permita ao agente dar a resposta mais adequada. Em geral, no entanto, nós queremos que os agentes executem tarefas para nós. Com isso, agentes inteligentes devem também ser proativos, ou seja, devem exibir um comportamento orientado a objetivos, tomar a iniciativa para alcançá-los além de reconhecer novas oportunidades. 7.1.1.3. Habilidade Social O mundo real é um mundo com multiagentes; em pouquíssimas tarefas, podemos alcançar algum êxito sem a ajuda de outros. O problema é que nem todo mundo compartilha os nossos mesmos objetivos, sendo muita vezes necessário negociar e cooperar com outros. Algo similar ocorre num ambiente computacional. Nesse sentido, a habilidade social de um agente deve ser compreendida como a sua capacidade de interagir com outros agentes (e possivelmente humanos) através de alguma linguagem de comunicação de agentes. Através dessa linguagem os agentes podem Cooperar: neste caso, os agentes trabalham como se estivessem num time com o objetivo de alcançar um objetivo em comum. Os agentes cooperam quando um agente sozinho não é capaz de alcançar o objetivo ou quando o trabalho conjunto irá produz um resultado melhor . Coordenar: nem sempre dois ou mais agentes podem usar o mesmo recurso ao mesmo tempo. Assim é preciso que eles coordenem suas atividades de modo que os agentes envolvidos possam fazer uso do recurso. Negociar: é a habilidade de fazer acordos sobre assuntos de interesse em comum. Se você quer assistir TV e seu irmão quer estudar, um possível acordo é você ligue a TV, mas com volume baixo. Tipicamente, negociação envolve proposta e contraproposta com compromissos feitos pelos participantes. Essas habilidades sociais serão esmiuçadas mais adiante. 143 7.1.1.4. Algumas Propriedades que Agentes Podem Ter Além das propriedades citadas um agente (inteligente) pode ter • Mobilidade: habilidade do agente se mover. Para um agente software, esse movimento é por volta de uma rede eletrônica; • Veracidade: se um agente irá propositalmente comunicar informação falsa para alcançar seu objetivo; • Benevolência: se agentes possuem objetivos conflitantes e se nesse caso, eles vão se ajudar entre si; • Racionalidade: se um agente irá agir para alcançar seus objetivos e não irá deliberadamente agir para evitar esse alcance; • Aprendizagem/adaptação: se os agentes melhoram o seu desempenho com o decorrer do tempo. 7.1.2. Arquitetura Abstrata para Agentes Vamos agora formalizar a noção de agente da qual temos discutido. Primeiramente, iremos caracterizar o ambiente simplesmente como um conjunto finito, discreto E de estados instantâneos: E = {e, e′ , . . .} . Os agentes possuem um repertório de ações disponíveis, que podem ser usadas para transformar o estado do ambiente. Seja Ac = {a, a′ , . . .} o conjunto (finito) dessas ações. A ideia geral é que o ambiente começa em algum estado e0 ∈ E. Então o agente inicia escolhendo uma ação α0 ∈ Ac. Como resultado dessa ação, o ambiente poderá escolher (inclusive não-deterministicamente) dentre um certo número de estados possíveis aquele que servirá de resposta para o agente (chamemo-lo de e1 ). Com base nesse estado e1 , o agente terá que escolher uma ação para executar, que irá, por sua vez, provocar uma resposta do ambiente e assim por diante. Uma execução r de um agente em um ambiente é, portanto, uma sequência alternada de estados do ambiente e ações: α0 α1 α2 α3 αu−1 r ∶ e0 → e1 → e2 → e3 → ⋯ → eu . Sejam • R o conjunto de todas as sequências finitas possíveis (sobre E e Ac); • RAc o subconjunto dessas sequências que terminam em ação; • RE o subconjunto dessas sequências que terminam em um estado. Nós usaremos r, r′ , . . . para nos referirmos aos membros de R. Para representar o efeito que as ações do agente acarretam no ambiente, nós introduzimos a função de transformação de estados: τ ∶ RAc → 2E . 144 Observe que a função de transformação de estados recebe uma execução terminando em uma ação α do agente e retorna um conjunto de possíveis estados que poderiam servir de respostas a α. Essa definição deixa claro que o ambiente é dependente do seu histórico, ou seja, o próximo estado do ambiente não é somente determinado pela última ação do agente e pelo estado corrente, mas por tudo que ocorreu antes. O segundo aspecto a destacar é que essa definição permite a definição de ambientes não-determinísticos. Quando τ (r) = ∅, temos que a execução r terminou. Vamos assumir em nosso sistema que todas as execuções chegam a um fim em algum momento. Formalmente, um ambiente é uma tripla Env = ⟨E, e0 , T ⟩, em que E é um conjunto de estados, e0 ∈ E é um estado inicial e τ é uma função transformadora de estados. Nós podemos agora formalizar a noção de agente como sendo a função Ag ∶ RE → Ac Um agente, portanto, toma decisoes sobre que ação executar com base no histórico do sistema até aquele momento. Chamemos de AG o conjunto de todos os agentes. Nós definimos um sistema como um par contendo um agente e um ambiente; todo sistema será associado com um conjunto de execuções possíveis. Nós denotamos o conjunto de todas as execuções de Ag em um ambiente Env por R(Ag, Env ). Por simplicidade, R(Ag, Env ) conterá apenas execuções r que terminam (τ (r) = ∅). Formalmente, uma sequência (e0 , α0 , e1 , α1 , e2 , . . .) representa uma execução de um agente Ag num ambiente Env = ⟨E, e0 , τ ⟩ se 1. e0 é o estado inicial de Env 2. α0 = Ag(e0 ) e 3. para u > 0 eu ∈ τ ((e0 , α0 , . . . , αu−1 )) e αu = Ag((e0 , α0 , . . . , eu )). Dois agentes Ag 1 e Ag 2 possuem comportamentos equivalentes com relação a um ambiente Env se R(Ag 1 , Env ) = R(Ag 2 , Env ). Alguns agentes são puramente reativos, isto é, decidem com base exclusivamente no presente e não levam conta o seu histórico: Ag ∶ Env ← Ac. O termostato é um exemplo de agente reativo, podendo ser definido como Ag(e) = { desligado se e = temperatura ok ligado caso contrário Nós precisamos agora refinar nosso modelo de agente e separar a decisão de um agente em dois subsistemas: percepção e ação. Ademais, os agentes têm alguma estrutura de dados interna que permita registrar a informação sobre os estados e o histórico (veja Figura 7.2). 145 Figura 7.2. Um agente com estado Nessa arquitetura, a função veja tem como objetivo representar a habilidade do agente de capturar o seu ambiente. No mundo real, essa função é implementada em hardware como um sensor ou uma câmera de vídeo por exemplo. Matematicamente, podemos definí-la como veja ∶ E → Per , em que Per é um conjunto de percepções. Já a função ação representa o processo de tomada de decisão do agente. Essa função vai receber como entrada o estado interno em que o agente se encontra e retornar a ação a ser tomada: ação ∶ I → Ac, em que I é o conjunto de todos os estados internos do agente. A atualização do estado interno do agente é feita pela função próximo a partir do estado atual e das novas percepções obtidas pelo agente: próximo ∶ I × Per → I. O comportamento de um agente baseado em estados pode ser sumarizado como segue: 1. Agente Ag começa em algum estado interno i0 . 2. Ag observa o estado do seu ambiente e e gera uma percepção veja(e). 3. O estado interno de Ag é atualizado via função próximo e passa a ser próximo(i0 , veja(e)). 4. A ação selecionada por Ag é ação(próximo(i0 , veja(e))). Essa ação é então executada. 5. Vá para (2). 7.1.3. Abordagens para a Construção de Agentes Na sequência, iremos mostrar as principais abordagens desenvolvidas para a construção de agentes: agentes como provadores de teorema, programação orientada a agentes, agentes reativos e agentes híbridos. 146 7.1.3.1. Um Agente como um Provador de Teoremas Na abordagem tradicional de Inteligência Artificial, IA simbólica, o comportamento inteligente é gerado em um sistema através de uma representação simbólica do seu ambiente e do comportamento desejado. Partindo desse pressuposto e recorrendo às fórmulas lógicas para fazer essa representação, em [Genesereth and Nilsson 1987] os autores desenvolveram um modelo simples de agente baseado em lógica. Em tais agentes, o estado interno é assumido ser um banco de dados de fórmulas da Lógica Clássica de Primeira Ordem. A ideia básica é usar lógica para codificar uma teoria retratando a melhor ação a ser executada numa dada situação. Assim sendo, sejam L um conjunto de sentenças da Lógica Clássica de Primeira Ordem, D = 2L , o conjunto dos conjuntos de fórmulas de L. O estado interno de um agente é, então, um elemento de D. Nós escrevemos ∆, ∆1 , . . . para membros de D. O processo de tomada de decisão de um agente é modelado através de um conjunto de regras de dedução ρ. Essas são regras simples de inferência para a lógica. Nós escrevemos ∆ ⊢ρ φ se a fórmula φ pode ser provada do banco de dados ∆ usando somente regras de dedução ρ. A função de percepção do agente veja permanece a mesma: veja ∶ S → Per Similarmente, a função próximo tem a forma próximo ∶ D × Per → D Essa função próximo mapeia, portanto, um banco de dados e uma percepção em um novo banco de dados. No entanto, a função de seleção de novas ações ação ∶ D → Ac é definida em termo de suas regras de dedução. O pseudo-código dessa função é dado pelo algoritmo abaixo: Algorithm 1 Ação(∆ ∶ D) retorna uma ação Ac 1: begin 2: for cada α ∈ Ac do 3: if ∆ ⊢ρ Faça(α) then 4: return α 5: end if 6: end for 7: for cada α ∈ Ac do 8: if ∆ ⊢ / ρ ¬Faça(α) then 9: return α 10: end if 11: end for 12: return null 13: end 147 A ideia é que o programador do agente irá codificar as regras de dedução ρ e um banco de dados ∆ de um modo tal que se uma fórmula Faça(α) possa ser derivada, em que α é um termo que denota uma ação, então α é a melhor ação a ser executada. Nesta abordagem, o processo de tomada de decisão é visto como uma dedução, sendo esse processo codificado como uma teoria lógica e a parte de selecionar uma ação reduzse a um problema de prova. Essa abordagem é elegante e possui uma semântica bem definida. Não obstante, problemas relacionados a sua alta complexidade computacional tornam questionáveis se agentes como provadores de teorema podem ser empregados em ambientes que requerem decisões rápidas. Além disso, eles partem do pressuposto que o mundo não irá mudar de modo significativo enquanto o agente está decidindo o que fazer. Quando isso não se aplica, essa abordagem poderá acarretar em resultados indesejáveis. 7.1.3.2. Programação Orientada a Agentes Em 1990, Yoav Shoham introduziu um novo paradigma de programação baseado numa visão societária da computação. Chamado de "programação orientada a agentes", sua principal ideia é permitir programar agentes diretamente em termos noções como crença, compromisso e intenção. A primeira implementação desse paradigma foi a linguagem de programação Agent0. Nessa linguagem, um agente é especificado como um conjunto de capacidades (coisas que ele pode fazer), um conjunto de crenças iniciais, um conjunto de compromissos iniciais e um conjunto de regras de compromisso. O componente-chave que determina como o agente agente vai agir é o conjunto de regras de compromissos. Cada regra de compromisso contém uma condição de mensagem, uma condição mental e uma ação. Para determinar se uma regra pode ser aplicada, a condição de mensagem é confrontada com as mensagens que o agente recebeu; a condição mental é confrontada com as crenças do agente. Se essas condições forem satisfeitas, o agente torna-se comprometido àquela ação. Ações em Agent0 podem ser privadas, correspondendo a uma sub-routina executada internamente ou comunicativa (envio de mensagens). Por sua vez, mensagens são restritas a três tipos: ‘requests’ or ‘unrequests’ para respectivamente se comprometer a ou declinar a execução de ações e mensagens do tipo ‘inform’. É de se destacar que mensagens request e unrequest resultam tipicamente na modificação dos compromissos do agente, enquanto que mensagens inform alteram as crenças do agente. A seguir, mostramos um exemplo de regra de compromisso em Agent0: COMMIT( ( agent, REQUEST, DO(time, action) ), ;;; msg condition ( B, [now, Friend agent] AND CAN(self, action) AND NOT [time, CMT(self, anyaction)] ), ;;; mental condition self, DO(time, action) ) 148 Essa regra pode ser parafraseada da seguinte maneira: se eu recebo uma mensagem de agent que me solicita executar action em time e eu acredito que • agent é um amigo no momento atual; • eu posso fazer essa ação; • em time, eu não estou comprometido em fazer qualquer outra ação, então eu me comprometo a fazer action em time. 7.1.3.3. Agentes Reativos Dois problemas principais são verificados com as abordagens baseadas em representação lógica/simbólica dos agentes: Problema da Transdução: como traduzir o mundo real numa representação simbólica? Problema da Representação/Raciocínio: como representar simbolicamente informação sobre complexas entidades do mundo real e raciocinar com elas eficientemente? As dificuldades de lidar com esses problemas nas abordagens simbólicas levaram muitos pesquisadores a investigar alternativas. Uma delas é a abordagem reativa, que tem esse nome porque tais sistemas são frequentemente percebidos como simplesmente reagindo a um ambiente sem raciocinar sobre ele. Um dos pesquisadores que estudou essa abordagem, Rodney Brooks, argumentou o seguinte: 1. Comportamento inteligente pode ser gerado sem representação explícita do tipo que a IA simbólica propõe. 2. Comportamento inteligente pode ser gerado sem raciocínio abstrato explícito do tipo que a IA simbólica propõe. 3. Inteligência é uma propriedade emergente de certos sistemas complexos. Ele identifca duas ideias centrais em sistemas reativos: 1. Situado e encorpado: inteligência real é situada no mundo; não são sistemas desencorpados tais como provadores de teorema ou sistemas especialistas. 2. Inteligência e emergência: comportamento inteligente surge como um resultado da interação de um agente com o seu ambiente. Ademais, inteligência está no olho do observador; não é uma propriedade inata e isolada. Para ilustrar suas ideias, Brooks construiu alguns agentes baseados em sua arquitetura da subsunção. Nesse trabalho, podemos identificar que no processo de tomada de decisão do agente é feito através de um conjunto de comportamentos de realização de tarefas. Cada comportamento pode ser visto como uma simples regra situação → ação, que mapeia uma entrada de dados (perceptual) diretamente em ações. Além do mais, cada comportamento ‘compete’ com outros pelo controle sobre o agente. Uma outra característica dessa abordagem é que ela está dividida em camadas que formam uma hierarquia, sendo que as camadas inferiores representam tipos mais primitivos de comportamentos (tais como evitar obstáculos) e têm precedência sobre camadas superiores dessa hierarquia (Figura 7.3). 149 Figura 7.3. Exemplo de máquina baseada em arquitetura da subsunção É válido mencionar que uma das vantagens desses sistemas baseados em arquitetura da subsunção é a sua simplicidade aliada à sua eficiência computacional, sendo empregado em situações que exigem um rápido tempo de resposta. Em [Steels 1990], por exemplo, o autor propõe um agente baseado em arquitetura da subsunção concebido para explorar planetas distantes. 7.1.3.4. Agentes Híbridos Muitos pesquisadores têm argumentado que tanto uma abordagem que seja completamente deliberativa quanto um que seja completamente reativa não são apropriadas para a construção de agentes. Em função disso, eles sugeriram um sistema híbrido que buscasse tirar proveito das boas qualidades de ambas as abordagens. Um caminho direto para tal sistema seria o de construir um agente a partir de dois (ou mais) subsistemas: • Subsistema Deliberativo: contém um modelo simbólico do mundo que desenvolve planos e toma decisões à maneira proposta pela IA Simbólica; • Subsistema Reativo: é capaz de reagir a eventos sem ter que fazer raciocínios complexos. De um modo geral, o componente reativo tem precedência sobre a parte deliberativa. Pode-se ainda argumentar que essa forma de estruturação leva naturalmente à ideia de arquitetura em camadas, das quis TouringMachines [Ferguson 1992] and InteRRaP [Müller and Pischel 1993] são dois bem conhecidos exemplos. Em tais arquiteturas, os subsistemas de controle de um agente são dispostos numa hierarquia com as camadas mais altas lidando com informação cada vez mais abstrata. Um problema central nessas arquiteturas é que tipo de estrutura de controle embutir nos subsistemas do agente para gerir as interações entre as várias camadas. Em todo caso, podemos identificar dois tipos de fluxo de controle nessas arquiteturas por camadas: • Camada horizontal: cada camada está diretamente conectada às entradas sensoriais e as ações retornadas como saídas (Figura 7.4(a)). Note que é como se cada camada agisse como um agente, produzindo sugestões sobre que ação executar. • Camada vertical: nesse tipo de arquitetura entradas sensoriais e as ações como saída são lidadas cada uma por no máximo uma camada (Figuras 7.4(b) e (c)). 150 Figura 7.4. Agentes Híbridos 7.2. Sistemas Multiagentes Até o momento temo-nos concentrado na construção de um agente. No entanto, à exceção de sistemas extremamente triviais, não iremos encontrar um sistema com um único agente. A razão é que um sistema contém um número de subsistemas que devem interagir uns com os outros com o objetivo de executar suas tarefas com êxito. O ponto-chave agora não é como construir um agente, mas como construir uma sociedade de agentes. A Figura 7.5 ilustra a estrutura típica de um Sistema Multiagente. Figura 7.5. Estrutura típica de um Sistema Multiagente Um aspecto fundamental aqui é a interação entre agentes. Num sistema multiagentes, os agentes são capazes de agir num ambiente; diferentes agentes têm diferentes esferas de influência, ou seja, as partes do ambiente sobre a qual os agentes vão ter alguma forma de controle. Esferas de influência com áreas em comum dão margem para 151 uma relação de dependência entre os agentes. Por exemplo, dois agentes robôs podem ser capazes de passar por uma porta, mas ambos não poderão fazer isso simultanemente. Num sistema multiagente, iremos encontrar duas maneiras distintas (muitas vezes conflitantes) de se analisar um agente: • Na perspectiva de quem construiu um agente individual, espera-se que esse agente faça o melhor para ele. • Na perspectiva de quem projetou o sistema multiagente, o sistema deve otimizar o desempenho do sistema como um todo. Um bom sistema multiagente é aquele que consegue satisfazer essas duas perspectivas e evitar conflitos. Ademais, no que tange aos tipos de interações que podem ocorrer entre os agentes, podemos destacar: Coordenação : objetivo é que um agente pelo menos não interfira no trabalho do outro. Eles podem, inclusive, coordenarem entre si para que um agente possa ajudar um outro. Comunicação : coordenação normalmente requer comunicação entre os agentes. Cooperação : Dependendo do objetivo de cada agente, quando um agente coordena ele pode também querer cooperar ou ainda competir. Negociação : se um agente age por interesse próprio, então cooperação é descartada, mas agentes podem recorrer a uma negociação para alcançar cooperação. Em suma, um sistema multiagente consiste de um número de agentes que interagem entre si. No caso mais geral, eles vão agir para atender os propósitos para os quais foram projetados, podendo ter diferentes objetivos e motivações. Não obstante, para que essa interação seja exitosa, eles terão que cooperar, coordenar e negociar uns com os outros [Wooldridge 2009]. 7.3. Interações entre Agentes Como sugerido anteriormente na Figura 7.5, as esferas de influência dos agentes em um sistema podem coincidir. Quando isso acontece, abre-se espaço para conflitos, mas também para ganhos mútuos. De certa maneira, o que faz um sistema multiagentes são o conjunto de entidades autônomas (às quais chamamos de agentes) e as interações entre as mesmas. Portanto, compreender que tipo de interações se dão entre os agentes e em que situações elas ocorrem é de extrema importância ao nos depararmos com algo que pareça um domínio multiagentes. 7.3.1. Utilidades e Preferências Para simplificar o entendimento dos conceitos a seguir, vamos nos restringir a sistemas com apenas dois agentes, os quais chamaremos de i e j, respectivamente. Assumimos que cada um desses agentes tem interesses próprios (é self-interested), ou seja, cada agente tem suas próprias preferências e desejos sobre como o mundo deveria ser. No momento, também não vamos nos preocupar com a origem desses desejos e preferências. Considere Ω = {ω1 , ω2 , . . . , }, o conjunto de todos os possíveis estados ou resultados (outcomes) sobre os quais os agentes têm preferências. As preferências dos agentes são capturadas por funções de utilidade, uma para cada agente, que designam um número real para cada resultado em Ω. Esse número real 152 indica o quão favorável o agente acredita que esse resultado lhe é, de forma que, quanto maior o número, mais o agente tem interesse naquele estado. Dessa forma, a função de utilidade do agente i será ui ∶ Ω → R e as preferências do agente j são capturadas pela função de utilidade uj ∶ Ω → R. Note que, para cada agente, a relação ρ = {(ω, ω ′ ) ∈ Ω × Ω ∣ ui (ω) ≥ ui (ω ′ )} é uma ordem parcial, isto é, uma relação reflexiva, transitiva e anti-simétrica. 7.3.2. Encontros entre Agentes Vamos agora considerar um modelo para o ambiente no qual os agentes agem. Consideraremos que os agentes escolhem simultaneamente que ação irão executar no ambiente e que um dos estados de Ω será o resultado dessas escolhas. O resultado dependerá de que ação cada agente executa, portanto ambos os agentes podem influenciar no resultado. Assumimos também que os agentes precisam executar uma ação, ou seja, não podem influenciar o resultado ao permanecer inativos, mas precisam escolher entre uma das ações. Além disso, assumiremos que um agente não pode influenciar e nem sabe qual será a ação executada pelo outro agente. Para simplificar ainda mais, assumiremos que cada agente só tem duas opções de ação para executar. Chamaremos às opções de ‘C’ (cooperar) e ‘A’ (abster-se), terminologia que será utilizada em um exemplo à frente. Seja Ac = {C, A}, o ambiente é modificado de acordo com a função τ ∶ Ac × Ac → Ω, onde a primeira ocorrência de Ac é a ação do agente i e a segunda ocorrência é a ação de j. Essa função é, essencialmente, uma função de transformação de estados, como discutido na Seção 1.2. Eis um exemplo de função de ambiente: τ (A, A) = ω1 , τ (A, C) = ω2 , τ (C, A) = ω3 , τ (C, C) = ω4 (3.1) Nessa função, cada combinação de ações dos agentes leva a um resultado diferente. Isso significa que o ambiente é sensível às escolhas dos agentes. Em um outro extremo, os agente poderiam ser completamente incapazes de influenciar no seu ambiente. Nesse caso, a função de ambiente mapearia todas as combinações de ações para o mesmo resultado. τ (A, A) = ω1 , τ (A, C) = ω1 , τ (C, A) = ω1 , τ (C, C) = ω1 (3.2) No exemplo acima, não importa o que os agentes façam. Uma outra possibilidade é que apenas um dos agentes possa influenciar no ambiente. No exemplo abaixo, o ambiente varia somente de acordo com as escolhas do Agent j. τ (A, A) = ω1 , τ (A, C) = ω2 , τ (C, A) = ω1 , τ (C, C) = ω2 (3.3) 153 Como podemos observar, as escolhas de i não interferem no ambiente. Consideremos novamente o cenário (3.1), em que os dois agentes têm influência no ambiente, mas agora vamos levar em conta as suas preferências. Suponhamos que as preferências dos agentes são dadas pelas seguintes funções de utilidade: ui (ω1 ) = 1 ui (ω2 ) = 1 ui (ω3 ) = 4 ui (ω4 ) = 4 uj (ω1 ) = 1 uj (ω2 ) = 4 uj (ω3 ) = 1 uj (ω4 ) = 4 Nesse caso, podemos relacionar as escolhas dos agentes às utilidades atribuídas a cada resultado da seguinte maneira: ui (τ (A, A)) = 1 ui (τ (A, C)) = 1 ui (τ (C, A)) = 4 ui (τ (C, C)) = 4 uj (τ (A, A)) = 1 uj (τ (A, C)) = 4 uj (τ (C, A)) = 1 uj (τ (C, C)) = 4 Neste caso, a preferência do agente i é dada pela ordem parcial ui (τ (C, C)) ≥ ui (τ (C, A)) > ui (τ (A, C)) ≥ ui (τ (A, A)), sugerindo que o agent i deve cooperar para obter um resultado mais satisfatório. Dizemos que essa é a opção racional, pois não há nenhum cenário em que ele tenha vantagem se agir de maneira diferente. As preferências dos agentes podem ser apresentadas de maneira concisa em uma matriz de payoffs: Figura 7.6. A matrix de payoffs para os agentes i e j. Em uma matriz de payoffs, o agente i joga de acordo com as colunas e recebe a recompensa de cima em cada célula. Similarmente, as jogadas do agente j estão representadas nas linhas e a utilidade alcançada em cada caso é o número de baixo em cada célula. 7.3.3. Extratégias Dominantes Dado um encontro envolvendo dois agentes i e j, cada um deve decidir sobre que ação tomar. Um agente decide por sua ações de maneira racional se pesa, em cada escolha, os possíveis cenários que podem resultar de suas ações e as utilidades destes. Sejam Ω1 , Ω2 ⊆ Ω, dizemos que Ω1 domina Ω2 se cada resultado em Ω1 é preferível sobre cada resultado em Ω2 . 154 Por exemplo, considere Ω = {ω1 , ω2 , ω3 , ω4 }, com ui (ω1 ) > ui (ω2 ) > ui (ω3 ) > ui (ω4 ), Ω1 = {ω1 , ω2 } e Ω2 = {ω3 , ω4 }. Nesse caso, dizemos que Ω1 domina fortemente Ω2 , pois ui (ω1 ) > ui (ω3 ), ui (ω1 ) > ui (ω4 ), ui (ω2 ) > ui (ω3 ) e ui (ω2 ) > ui (ω4 ). Uma vez que os conceitos discutidos nessa seção são provenientes de teoria dos jogos, vamos começar a falar de ações possíveis como estratégias. Dessa forma, os agentes devem escolher que estratégias vão adotar em cada situação. Denotemos por s∗ , o conjunto de resultados que podem ser alcançados quando o agente i escolhe e joga de acordo com a estratégia s. Nos exemplos da Seção 3.2, teríamos, portanto, C∗ = {ω3 , ω4 }, enquanto que A∗ = {ω1 , ω2 } para o agente i. Dizemos que uma estratégia s1 domina fortemente outra estratégia s2 se s∗1 domina fortemente s∗2 . Se uma estratégia si domina fortemente a todas as outras estratégias acessíveis a um agente, dizemos que si é uma estratégia dominante. A intuição por trás de uma estratégia dominante é que esta garante ao agente alcançar os melhores resultados possíveis, ou seja, maximizar a sua utilidade. Quando existe uma estratégia dominante, o melhor que o agente tem a fazer é jogar por ela, pois os desvios podem lhe levar a recompensas menores ou cenários envolvendo perdas. Quando temos dois ou mais agentes em uma mesma situação, podemos perceber que, por vezes, as estratégias dominantes de cada agente o são por influência mútua. Nesse caso, dizemos que as estratégias se encontram em equilíbrio, pois nenhum agente tem incentivo para desviar-se dessa escolha de estratégias. Esse conceito é o de Equilíbrio de Nash e é um dos mais importantes em Teoria dos Jogos e na análise de interações entre agentes. Um cenário de interação entre agentes pode ter zero, um ou vários equilíbrios de Nash. 7.3.4. O Dilema dos Prisioneiros Considere o seguinte cenário: Dois homens são presos e acusados de terem cometido um crime juntos. Os prisioneiros são mantidos em celas separadas e não têm como se comunicar. A cada um dos homens é dito o seguinte: 1. Se um dos dois confessar o crime, mas o outro não o fizer, o que tiver confessado será absolvido e o outro cumprirá pena de 3 anos; 2. Se os dois confessarem o crime, cada um receberá dois anos. Os dois prisioneiros sabem que, se nenhum dos dois confessar, cada um pegará 1 ano de cadeia. A partir de agora, nos referiremos a confessar como cooperar (com a polícia) e não confessar como abster-se (da oferta). Suponha que você é um dos agentes e responda: se você fosse um dos prisioneiros, o que faria? Esse cenário é conhecido como o dilema do prisioneiro. Existem quatro resultados possíveis para esse cenário, dependendo de que estratégias cada agente escolhe. Tais resultados são resumidos nas funções ui (τ (A, A)) = 3 ui (τ (A, C)) = 0 ui (τ (C, A)) = 5 ui (τ (C, C)) = 2 uj (τ (A, A)) = 3 uj (τ (A, C)) = 5 uj (τ (C, A)) = 0 uj (τ (C, C)) = 2. As funções de utilidade dos agentes, portanto, são: 155 ui (τ (C, A)) > ui (τ (A, A)) > ui (τ (C, C)) > ui (τ (A, C)), uj (τ (A, C)) > uj (τ (A, A)) > uj (τ (C, C)) > uj (τ (C, A)). Ilustramos as funções de utilidade dos agentes na seguinte matriz de payoffs: Figura 7.7. Matrix de payoffs para o dilema dos prisioneiros Observe que os números na matrix não dizem respeito ao número de anos que cada agente poderia pegar de cadeia, mas à utilidade vista em cada resultado por cada agente. Nesse sentido, menos anos de cadeia sugerem uma utilidade maior. Façamos uma análise para definir o que o agente i deve fazer, colocando-nos em seu lugar: • Suponha que eu (agente i) me abstenha da oferta. Nesse caso, se o j também se abstiver, nós dois teremos um payoff 3 (pena de 1 ano). Se, por outro lado, o j cooperar, então eu terei um payoff 0 (pena de 5 anos). Portanto, o melhor payoff que eu posso garantir se eu me abstiver, é 0. • Suponha, então, que eu coopere com a polícia. Nesse caso, se j se abstiver, eu fico com um payoff 5, mas se ele cooperar também, eu fico com payoff 2. Assim sendo, o melhor payoff que eu garanto se cooperar, é 2. • Se eu me abstiver da oferta, garanto um payoff mínimo de 0, mas se eu cooperar com a polícia, garanto o payoff mínimo de 2. Consequentemnte, se desejo maximizar a minha utilidade, devo garantir o payoff mínimo de 2. Então eu devo cooperar com a polícia! O dilema dos prisioneiros é um exemplo clássico em teoria dos jogos e multiagentes, pois seu resultado é inesperado. Como o cenário de j é idêntico ao de i, seu raciocínio j deve ser similar. Consequentemente, se i e j agirem de forma racional, os dois cooperarão com a polícia e o resultado será de utilidade 2 para ambos. Observe que o melhor cenário possível para os agentes é aquele em que os dois decidem por se abster, o que lhes daria um payoff de 3, mas ambos evitam esse cenário porque há um risco de que o resultado lhes dê uma utilidade menor. 7.4. Comunicação Além da interação, a comunicação entre agentes é outro ponto essencial a ser estudado em sistemas multiagentes. Nesse sentido, devemos destacar que a maioria dos trabalhos sobre comunicação de agentes está alicerçado na noção de atos de fala, cuja origem remonta-nos ao livro How to Do Things with Words de Johm Austin [Austin 1962]. Para ele, enunciar algo pode ser entendido como uma ação física. Nessa linha de estudos, em [Searle 1969] John Searle identificou vários tipos diferentes de atos de fala: 156 Representativas : tem caráter informativo: "está chovendo". Diretivas : tenta fazer com que o ouvinte faça algo: "por favor, pegue o chá". Compromissivas : compromete o falante a fazer algo: "eu prometo que...". Expressivas : meio pelo qual o falante expressa um estado mental: "muito obrigado". Declarações : tais como declarar uma guerra ou nomear alguém. De um modo geral, os atos de fala possuem dois componentes: um verbo performativo(pedir, informar,...) e um conteúdo proposicional ("a porta está fechada"). Observe que o verbo performativo está relacionado à intenção do ato de fala. Assim, vários atos de fala diferentes podem ter o mesmo conteúdo proposicional. Veja os seguintes exemplos com o mesmo conteúdo proposicional ("a porta está fechada") gerando três tipos diferentes de atos de fala à medida que mudamos o verbo performativo: • performativo = pedir; ato de fala: por favor, feche a porta. • performativo = informar; ato de fala: a porta está fechada. • performativo = perguntar; ato de fala: a porta está fechada? A Foundation for Intelligent Physical Agents (FIPA) começou a trabalhar sobre a definição de padrões de programas de agentes. Na linguagem desenvolvida por eles, os atos de fala são caracterizados em termos dos performativos (a FIPA propõe 20 tipos diferentes), personagens envolvidos (remetente, destinatário, etc) e conteúdo proposicional da mensagem. Na Figura 7.8, temos um exemplo de ato de fala representado em FIPA no qual o agent1 informa o agent5 que o preço oferecido a good200 é 150. Figura 7.8. Exemplo de Ato de Fala em FIPA Em FIPA, "Inform" e "Request" são dois performativos básicos, em termos dos quais todos os demais são definidos. O significado de "Inform" e "Request" é definido em duas partes: pré-condição (o que deve ser verdadeiro para o ato de fala ser bem sucedido) e efeito racional (o que o remetente da mensagem espera ocorrer). No caso do performativo "Inform", o conteúdo é uma declaração e a sua pré-condição determina que o remetente acredita que o conteúdo é verdadeiro, pretende que o destinatário acredite na veracidade do conteúdo e não acredita que o destinatário saiba se o conteúdo seja verdadeiro ou não. 7.5. Alocação de Recursos Em sociedades de agentes com interesses próprios (self-interested), recursos escassos serão disputados e estarão, com frequência, indisponíveis. Exemplos de recursos escassos incluem o direito ao uso de um objeto ou recursos computacionais. Tais recursos podem ser necessários à realização de uma atividade por um agente e, portanto, essencial para que este alcance um objetivo. Nesse sentido, a alocação de recursos é um problema central em Sistemas Multiagentes e as disputas geradas por escassez devem ser tratadas de maneira eficiente. 157 Formalmente, dados um conjunto com k recursos R = {o1 , . . . , ok } e um conjunto com n agentes Agentes = {Ag1 , . . . , Agn }, um problema de alocação de recursos envolve encontrar uma partição de R com tamanho n, ou seja, conjuntos R1 , . . . , Rn tais que R1 ∪ ⋅ ⋅ ⋅ ∪ Rn = R, mas Ri ∩ Rj = ∅ para quaisquer Ri , Rj ∈ {R1 , . . . , Rn }. Nesse caso, cada Ri é o conjunto de recursos alocados para o agente Agi , 1 ≤ i ≤ n. Os mecanismos utilizados para alocação de recursos dependem essencialmente do número de agentes e recursos envolvidos no problema. No caso em que há uma pluralidade de agentes, uma forma bastante eficiente de lidar com a distribuição dos recursos é pela utilização de leilões. 7.5.1. Leilões Os leilões eram, até pouco tempo, não muito utilizados. A razão era o alto custo para viabilizar um leilão, dada a necessidade de encontrar um local para o evento, transportar as mercadorias, contratar um leiloeiro, e mais. A internet mudou isso, reduzindo drasticamente os custos e permitindo realizar e concorrer em leilões a partir de qualquer lugar do mundo. Essencialmente, um leilão ocorre entre um agente (o leiloeiro) que controla um recurso (uma mercadoria) e outros agentes (os licitantes) interessados neste. O objetivo de um leilão é decidir qual dos licitantes fica com a mercadoria. Para tal, os licitantes farão ofertas com outros recursos (a exemplo de dinheiro) com o intuito de minimizar o seu custo, enquanto que o leiloeiro deseja maximar seu ganho. Nesse sentido, se uma troca é uma atribuição de valores a pagar por uma mercadoria, devemos considerar que: • Cada agente envolvido atribui um preço limite à mercadoria. • Um comprador que troca mais do que o seu valor limite pela mercadoria, tem uma perda. • Um vendedor que troca sua mercadoria por menos do que o seu valor limite, tem uma perda. Uma instituição de mercado define como trocas devem ser feitas. Dessa maneira, uma instituição define que tipo de mensagens podem ser trocadas e, com base nas mensagens trocadas, quem é o vencedor. O mais comum é que as mensagens sejam restritas a lances ou pedidos e que a regra que determina o vencedor dificulte a elaboração de estratégias para vencer o leilão. Um leilão é uma instituição de mercado em que mensagens entre interessados em trocas (comerciantes) envolvem alguma informação de valor - uma oferta para comprar a mercadoria (um lance) ou para vendê-la (um pedido) por um certo valor - e que prioriza lances de valor maior ou pedidos de valor menor. 7.5.1.1. Leilão Inglês O leilão inglês é o tipo mais conhecido, consistindo em uma sucessão de propostas públicas, com valor crescente e cujo vencedor é aquele que profere a última (maior) proposta: • O leiloeiro propõe um preço de reserva (que pode ser 0). Se nenhum agente der um lance maior, a mercadoria fica com o leiloeiro por esse valor. 158 • Agentes são convidados a dar lances cada vez mais altos, à vontade. Quando nenhum agente está disposto a aumentar o lance, o leilão é encerrado e o vencedor paga o valor que propôs. A estratégia dominante em leilões desse tipo envolve o agente elevar o valor das propostas aos poucos até atingir o seu valor de reserva e, então, desistir. 7.5.1.2. Leilão Holandês Este modelo de leilão consiste de propostas públicas, mas decrescentes. Dessa maneira, é ligeiramente diferente do leilão inglês, mas tal diferença é o bastante para que, em geral, não haja estratégia dominante. • O leiloeiro propõe um valor inicial artificialmente alto e segue com ofertas pouco menores a cada momento. • Quando um agente aceita a última oferta feita, paga este valor e fica com a mercadoria. Em caso de empate, o leilão é reiniciado com um valor um pouco maior do que o atual. 7.5.2. Negociação Em alguns casos, o mecanismo de leilões é insuficiente e outras técnicas mais elaboradas precisam ser utilizadas. Um dos motivos possíveis para tal é que os leilões dependem da existência de algum tipo de moeda de troca que permita comparações de valor. Em um contexto mais geral, tal moeda pode não existir, mas os agentes poderiam que trocar recursos diversos ou até favores. Nos referimos a essas técnicas, em geral, pelo nome de negociação. Em uma negociação, os agentes buscam um acordo em situações que sejam de interesse mútuo. Em um contexto de negociação teremos ao menos quatro elementos: • Um conjunto de negociação, com todas as propostas que os agentes podem fazer. • Um protocolo, o qual define quais propostas são legais em cada momento da negociação, com base em seu histórico. • Um coleção de estratégias de negociação, uma para cada agente, que determina quais propostas um agente pretende fazer. Essa estratégia costuma ser privada para cada agente, embora as propostas, quando feitas, costumem ser públicas. • Uma regra que determina o fim da negociação e qual seria o acordo alcançado. Vários fatores podem fazer com que o processo seja bastante complicado, a exemplo do número de itens negociados e do número de envolvidos na negociação. Devido às dificuldades introduzidas por esses fatores, a maior parte dos trabalhos nesse tema envolve negociações dos tipos mais simples, envolvendo apenas dois agentes, um único item negociável e em que há simetria, no sentido de que um resultado mais favorável a um dos agentes é menos favorável ao outro, em igual proporção, e vice-versa. Normalmente, assume-se que os agentes buscam maximizar sua utilidade durante a negociação e que o pior resultado possível é a ausência de um acordo. Uma negociação costuma ser realizada em uma sequência de rodadas que envolvem, cada, uma proposta. A seguir, considere o seguinte modelo para negociação um-a-um com ofertas alternadas: 159 • Os agentes Ag1 e Ag2 negociam em uma sequência de rodadas 0, 1, 2, . . . • Nas rodadas pares (r = 0, 2, 4, . . . ): Ag1 faz uma proposta pr ; Ag2 aceita ou rejeita pr ; Se aceita, a negociação é finalizada. Caso contrário, a negociação avança para a rodada r + 1. • Nas rodadas ímpares (r = 1, 3, 5, . . . ): Ag2 faz uma proposta pr ; Ag1 aceita ou rejeita pr ; Se aceita, a negociação é finalizada. Caso contrário, a negociação avança para a rodada r + 1. A negociação também pode ser encerrada caso nenhum dos agentes possa fazer nova proposta após a rejeição da última. Nesse caso, não há acordo. Tão logo a negociação seja finalizada, o acordo a que os agentes chegaram (se chegaram) é implementado. A Figura 7.9 ilustra uma negociação desse tipo: Figura 7.9. O ciclo de negociação entre dois agentes. 7.6. Cooperação Considerando que agentes são autônomos, eles têm que tomar decisões em tempo de execução e devem ser capazes de se coordenarem dinamicamente. No entanto, se os agentes foram projetados por indivíduos diferentes, eles podem não compartilhar as mesmas metas. Assim por que e como os agentes irão trabalhar juntos? Para responder essa pergunta precisamos distinguir dois tipos de agentes: agentes benevolentes e os com interesses próprios. Se o sistema multiagente foi projetado por uma única pessoa ou organização, é possível conceber os agentes de um modo tal que eles possam ajudar uns aos outros toda vez que for preciso. Nesse caso, nós assumimos que os agentes são benevolentes: nosso melhor interesse é o melhor interesse deles. Nesse caso, a solução de problemas e cooperativa e distribuída, sendo que a presença de agentes benevolentes simplifica enormemente a projeção do sistema. 160 Por outro lado, se os agentes representam os interesses de indivíduos ou organizações distintas, então nós não poderemos assumir que os agentes serão benevolentes. Nesse cenário, os gentes serão assumidos como defendendo seus próprios interesses, possivelmente às custas de outros. Não é difícil constatar que a possibilidade de conflitos é grande, tornando a projeção de tarefas bem mais complicadas. Além disso, pode ser necessário elaborar estratégias de atuação para que os agentes possam atingir os seus objetivos, como ocorre, por exemplo, na Teoria de Jogos. 7.6.1. Resolução de Problemas em Conjunto Ao se tentar resolver um problema cooperativamente em um sistema multiagentes, uma pergunta fundamental é "como um grupo de agentes trabalha junto para resolver problemas?". Esse trabalho cooperativo pode ser dividido em três estágios: decomposição do problema, solução do subproblema e síntese da resposta. A seguinte figura, adaptada de [Wooldridge 2009], ilustra as fases para a solução de problemas de forma cooperativa e distribuída: Figura 7.10. Os três estágios para a solução de problemas em cooperação. Na decomposição do problema, o problema principal é subdividido em subproblemas. Esse processo é normalmente hierárquico/recursivo. Os subproblemas são, por sua vez, divididos em subproblemas ainda menores, sendo que esse processo continua até se chegar a um nível atômico razoável. Claramente há como se processar essa divisão. A questão a ser decidida durante a projeção do sistema é como isso será feito? Outra questão importante é quem irá fazer a divisão. Será centralizado? Que agentes terão conhecimento da estrutura das tarefas? Que agentes irão resolver cada subproblema? Como se pode perceber, essa fase de decomposição do problema passa pela resposta de muitas perguntas. No próximo estágio, os agentes procurarão solucionar cada um dos subproblemas gerados. Para tal, eles vão geralmente compartilhar informação durante esse processo. Além disso, eles terão que sincronizar suas ações quando precisarem dos mesmos recursos ao mesmo tempo. Por fim, no estágio de síntese da solução, as soluções dos subproblemas são integradas. Mais uma vez esse processo pode ser hierárquico, permitindo a obtenção de 161 soluções diferentes em diferentes níveis de abstração. Uma questão recorrente nesta fase é que agente irá fazer essa síntese. Neste modelo de solução de problemas cooperativamente, muito provavelmente, nós encontraremos as seguintes atividades: • Compartilhamento de tarefas: os componentes de uma tarefa são distribuídos entre os agentes; uma questão a responder é como nós decidimos como alocar tarefas a agentes? • Compartilhamento de resultados: informações relevantes à solução de subproblemas (a exemplo de resultados parciais) são compartilhadas entre os agentes do time por demanda ou pró-ativamente. A Figura 7.11, adaptada de [Wooldridge 2009], ilustra os conceitos discutidos acima: Figura 7.11. (a) Compartilhamento de tarefas e (b) compartilhamento de resultados. 7.6.2. Distribuição de Tarefas com Contract Net Distribuir tarefas de maneira eficiente entre os agentes de um time pode ser difícil, uma vez que cada membro do time pode ter capacidades muito diferentes. Nos casos em que os agentes são realmente autônomos, é concebível que estes possam rejeitar tarefas que lhes sejam atribuídas e precisem buscar acordos sobre a alocação destas tarefas. Um protocolo bem conhecido de compartilhamento de tarefas que leva isso em consideração é o Contract Net. Ele inclui cinco estágios e nos lembra um processo de licitação: 1. 2. 3. 4. 5. Reconhecimento do Problema Anúncio da Tarefa Ofertas Seleção do contrato Expedição Esse protocolo prima por ser autoexplicatório e é ilustrado na Figura 7.12. Apesar de sua simplicidade, o Contract Net se tornou o framework mais implementado e melhor estudado para a solução de problemas com distribuição de tarefas. 7.7. Coordenação Talvez o problema principal no que tange o trabalho cooperativo em grupos de agentes é o da coordenação entre os envolvidos. Este problema concentra-se no gerenciamento de 162 Figura 7.12. Protocolo Contract Net dependências entre as atividades dos agentes para que estas possam interagir de maneira apropriada. As dependências entre atividades vão desde os requisitos para seu início ao cumprimeiro de regras que garantem a consistência do sistema. Considere o seguinte exemplo, corriqueiro na vida real: Duas pessoas estão em uma sala e, ao mesmo tempo, resolvem sair. Há uma única porta na sala e sua largura só permite que uma pessoa passe por vez. Nesse caso, há um recurso público (a porta) que ambos desejam usar, mas do qual apenas um pode usufruir por vez. Como consequência, as atividades das pessoas precisam ser coordenadas para que não haja uma colisão e todos possam cumprir os seus objetivos. Os agentes de uma sociedade também podem usar de coordenação para evitar a repetição de esforços, nesse caso, agindo de forma coordenada e cooperativa. No exemplo acima, se a porta deve permanecer fechada quando ninguém estiver utilizando a passagem, as ações necessárias a cada agente envolveriam abrir a porta, sair e fechar a porta. Se o primeiro que passar pela porta perceber que há outra e, invés de executar todos os passos, deixar a porta aberta, alguns esforços são evitados. Existem diversas abordagens para coordenar dinamicamente as atividades de agentes em um sistema. Entre estas abordagens, pode-se modelar os agentes desde o começo para que ajam coordenadamente, desenhar regras que induzam à coordenação de atividades ou promover a comunicação das intenções dos agentes à sociedade. Neste capítulo, vamos nos concentrar em apenas uma dessas abordagens, a dizer, baseada em normas e leis sociais. 163 7.7.1. Normas e Leis Sociais Sociedades são frequentemente reguladas por regras (muitas vezes não-escritas) de comportamento. Por exemplo, um grupo de pessoas está esperando numa parada de ônibus. O ônibus chega; quem entra no ônibus primeiro? Num sistema multiagentes, nós teremos que projetar as normas que o programa dos agentes deve seguir ou formalizar aspectos do ambiente e dos agentes de maneira que essas normas possam emergir. Vamos agora dar mais detalhes sobre esses dois tipos de normais sociais: Antes de descrever como projetar as normas que o programa dos agentes deve seguir, é válido destacar que nós descrevemos agentes como Ag ∶ RE → Ac, ou seja, uma função que dada uma execução terminando num estado, retorna uma ação. Uma restrição é um par ⟨E ′ , α⟩ , em que E ′ ⊆ E é um conjunto de estados e α ∈ Ac é uma ação. Essa restrição afirma que α não pode ser realizada em qualquer estado em E ′ . Uma lei social é definida, então, como um conjunto dessas restrições. Nós ainda podemos refinar nossa visão de um ambiente. Para tal, vamos chamar de estados focais, F ⊆ E, aqueles que nós queremos nosso agente seja capaz de estar. De qualquer estado focal e ∈ F , deve ser possível chegar a qualquer outro estado focal (embora não necessariamente de maneira direta). Uma lei social útil é uma que não previne agentes de chegar de um estado focal a outro. No que tange às normas sociais, nós também podemos projetar sistemas em que elas podem emergir. Um conhecido exemplo é o jogo da camiseta [Shoham and Tennenholtz. 1997]: "Agentes têm ambos uma camiseta vermelha e uma azul e vestem uma delas. O objetivo é que cada cada agente termine com a mesma cor. Em cada rodada, cada agente encontra-se com um outro agente e decide se muda de camiseta ou não. Durante a rodada, eles veem somente a camiseta de que seu par estiver vestindo (eles não obtêm nenhuma outra informação)". Que função de atualização de estratégia eles deveriam usar? Na sequência, estão algumas funções de atualização que poderiam ser aplicadas nesse caso: • Maioria simples: agentes pegam a camiseta que eles viram com mais frequência. • Maioria simples com tipos: agentes estão divididos em dois tipos; quando eles encontram um agente do mesmo tipo, eles compartilham suas memórias. Caso contrário, eles agem como no caso de maioria simples. • Recompensa cumulativa mais alta: para essa atualização funcionar, agentes devem ser capazes de perceber que adotando certa estratégia irá produzir um resultado particular. A regra de atualização baseada na recompensa cumulativa mais alta estabelece que um agente use a estratégia que produza o resultado cumulativo mais alto até o momento. 164 Referências Austin, J. L. (1962). How To Do Things With Words. Oxford University Press. Ferguson, I. A. (1992). TouringMachines: An Architecture for Dynamic, Rational, Mobile Agents. PhD thesis, Computer Laboratory, University of Cambridge, Cambridge, UK. Genesereth, M. R. and Nilsson, N. (1987). Logical Foundations of Artificial Intelligence. Morgan Kaufmann. Müller, J. P. and Pischel, M. (1993). The agent architecture inteRRaP : concept and application. Technical report, Saarländische Universitäts- und Landesbibliothek; Sonstige Einrichtungen. DFKI Deutsches Forschungszentrum für Künstliche Intelligenz. Searle, J. R. (1969). Speech Acts: an Essay in the Philosophy of Language. Cambridge University Press. Shoham, Y. and Tennenholtz., M. (1997). On the emergence of social conventions: modeling, analysis and simulations. Artificial Intelligence, 94(1):139–166. Steels, L. (1990). Cooperation between distributed agents through self organization. In Demazeau, Y. and Müller, J.-P., editors, Decentralized AI - Proceedings of the 1st European Workshop on Modelling Autonomous Agents in a Multi-Agent World(MAAMAW89), pages 175–196. Elsevier. Wooldridge, M. (2009). An Introduction to MultiAgent Systems. Wiley Publishing, 2nd edition. Wooldridge, M. and Jennings, N. R. (1995). Intelligent agents: theory and practice. The Knowledge Engineering Review, 10(2):115–152. 165 Capítulo 8 Desenvolvimento para dispositivos móveis que utilizam plataforma iOS Roniel Soares de Sousa, José Almí Soares Filho, Ricardo Teles Freitas, Kelson Aires, André Soares, Vinicius Machado, Laurindo Britto Neto Laboratório de Inteligência Computacional (LabInC) da Universidade Federal do Piauí (UFPI) Abstract This chapter describes the main principles for the applications development for iOS devices – iPhone, iPad and iPod Touch. The main tool required for such activity, the Xcode, is presented. Moreover, this work will approach the Objective-C programming language, Views’ manipulation, the use of the iOS Simulator – tool used for testing the applications – and even the connectivity technologies’ functionality (Bluetooth and WiFi). Resumo Este capítulo descreve princípios fundamentais para o desenvolvimento de aplicativos para dispositivos que utilizam a plataforma iOS – iPhone, iPad e iPod Touch. Apresentar-se-á a principal ferramenta necessária nesta atividade, o Xcode. Além disso, este trabalho abordará a linguagem de programação Objective-C, manipulação de “Views”, o uso do iOS Simulator – ferramenta utilizada para testar as aplicações – e até mesmo o funcionamento de tecnologias de conectividade (Bluetooth e Wi-Fi). 8.1. Introdução Esta seção apresenta o iOS SDK, kit de desenvolvimento de sistemas para iOS. É coberto desde a sua instalação até a definição dos principais componentes. 8.1.1. Obtendo o iOS SDK (Software Development Kit) O iOS SDK é um conjunto de aplicações necessárias para desenvolver aplicativos para dispositivos móveis (conhecidos popurlamente como apps), que rodam nativamente no 166 iOS. Ele fornece uma grande variedade de programas que dão suporte ao desenvolvedor nas etapas de produção do app. Para obter acesso ao iOS SDK, é necessário inicialmente possuir uma conta na App Store (loja virtual de aplicativos para iOS). Abaixo está o link para download: - http://itunes.apple.com/us/app/xcode/id448457090?mt=12 Além disso, você precisa de um Mac baseado em Intel com o Mac OS Snow Leopard ou superior instalado. O iOS SDK é dividido em diferentes componentes que auxiliam no desenvolvimento das aplicações. Tais componentes são: Xcode, DashCode, iOS Simulator, e Instruments. Dentre eles, os mais utilizados aqui serão o Xcode e o iOS Simulator. NOTA: Neste minicurso será considerada a versão 4 do SDK, versão mais atual no momento da confecção deste minicurso. 8.1.2. Componentes do SDK O SDK do iOS inclui as seguintes ferramentas: • XCode - Ambiente de desenvolvimento integrado através do qual o programador gerencia, edita e depura os projetos. • Dashcode - Ambiente de desenvolvimento integrado voltado para produzir aplicações web para iOS, assim como Dashboard Widgets. • iOS Simulator - Simulador do iOS. • Interface Builder - Editor gráfico para projetar interfaces, que está integrado ao XCode desde a versão 4. Nas versões anteriores o Interface Builder não compunha o XCode mas vinha separado no SDK. • Instruments - Programa que visa analisar o desempenho dos apps. O SDK do iOS também inclui outras aplicações. Entretanto, este minicurso está focado apenas no XCode, Interface Builder, e no iOS Simulator. Nas próximas seções tais componentes serão detalhados. 8.1.2.1. XCode O XCode é o principal ambiente, onde se desenvolvem as aplicações, tanto na sua parte gráfica, com auxílio de um construtor de interfaces gráficas que auxilia de maneira bastante intuitiva na criação da interface, quanto na sua parte funcional, feita através do código escrito em Objective-C. Este ambiente de desenvolvimento integrado (IDE) permite que o programador comece sua aplicação a partir de modelos pré definidos (templates): • • • Navigation-based application: Tema que viabiliza um ponto de partida para uma aplicação com controle de navegação. OpenGL ES Application: Permite utilizar uma visualização na qual é possível renderizar uma cena OpenGL ES. Split View-based Application: Começa uma aplicação com uma visualização do tipo dividida e duas visualizações comuns. 167 • • • • Tab Bar Application: Começa uma aplicação com um controlador de barra e um controlador de visualização do primeiro item da barra. Utility Application: Inicia uma aplicação com uma visualização principal e uma outra do tipo “flipside”. View-based Application: Provem uma visualização com um controlador e um arquivo .nib. Window-based Application: Template inicial para qualquer tipo de aplicação. Alguns desses templates são exclusivos para determinados dispositivos, mesmo rodando sobre a mesma plataforma. Por exemplo o template Split-View Based serve apenas para iPad. 8.1.2.2. iOS Simulator Outra ferramenta importante que o SDK fornece é o simulador que imita o funcionamento de um iPhone, iPad ou iPhone Retina utilizando o iOS. É importante notar que esta ferramenta é um simulador, ou seja ela tenta imitar o comportamento do iOS nos dispositivos baseando-se no MAC OS para compilar o código da aplicação. Isso significa que ela irá usar as bibliotecas presentes no MAC para fazer a aplicação se comportar como se estivesse num verdadeiro iPhone, embora em um dispositivo real a aplicação precise ser compilada para o byte-code usado nele. O iPhone utiliza um código baseado na arquitetura ARM para isso. O simulador apresenta algumas características importantes do dispositivo real. Entre elas estão os movimentos de toque: toque leve, duplo toque e pinça, rotações de tela e simulações de avisos de pouca memória. Porém outras características importantes como fazer ligações, utilizar a câmera ou o acelerômetro não estão disponíveis até a versão 4.3 do simulador. 8.1.2.3. Interface Builder (Integrado ao Xcode) O Interface Builder favorece a construção de interfaces gráficas para o usuário, fornecendo elementos gráficos baseados no drag-and-drop (arrastar e soltar) e formas intuitivas de conectar tais elementos com o código. Os conceitos de Outlets e Actions são essenciais para se compreender como são ligados os elementos gráficos do Interface Builder com as funcionalidades do código. 8.2. Objective-C A Objective-C é uma linguagem de programação orientada a objeto, desenvolvida pela Apple, a fim de ser usada tanto no desenvolvimento para MAC OS X quanto para o iOS. É uma extensão da linguagem C padrão, portanto quem já possui certa experiência com a programação em C não terá problemas na adaptação ao Objective-C. Para entender o código de uma aplicação em tal linguagem é preciso saber como são divididos seus arquivos: • .h – Arquivos de cabeçalho, onde se localizam as declarações de objetos e métodos. • .m – Arquivos de implementação, como sugere o próprio nome, contém a implementação do que foi declarado nos arquivos de cabeçalho. 168 A seguir veremos algumas características da Linguagem Objective-C. 8.2.1. Diretivas Para incluir bibliotecas na implementação de código usa-se o pré-processador de diretivas, no caso do Objective-C, o #import, que atua de forma igual ao #include no C ou C++. 8.2.2. Classes Como Objective-C é uma linguagem orientada a objetos, existe a necessidade de um certo conhecimento sobre classes. Para declarar uma classe usa-se @inteface, como no exemplo a seguir: @interface NomeDaClasse : NSObject { } @end NSObject refere-se a classe da qual a classe declarada herda. Já na implementação de uma classe usa-se @implementation além da declaração da mesma. Veja o exemplo: #import “NomeDaClasse” @implementation NomeDaClasse //implementação @end Entretanto, podem ocorrer casos de importação cíclica, onde uma determinada classe, que será chamada de “ClasseUm”, tem como atributo uma instância de uma certa “ClasseDois” e vice-versa, tendo assim a necessidade de cada uma ser importada na outra. Para evitar isso, faz-se necessário o uso de @class como mostrado abaixo: #import<Foundation/Foundation.h> @class ClasseDois; @interface ClasseUm : NSObject{ ClasseDois *classeDois; } @end #import <Foundation/Foundation.h> @class ClasseUm; @interface ClasseDois : NSObject{ ClasseUm *classeUm; 169 } @end 8.2.3. Instanciando classes Para criar uma instância de determinada classe em Objective-C, devemos usar a palavra “alloc”, que permite reservar um espaço na memória para o novo objeto. Além disso, usa-se o caractere ‘*’ (asterisco) precedendo o nome da variável que irá referenciar o objeto criado, exceto nos casos em que a variável é de tipo primitivo. O trecho abaixo exemplifica a criação de um objeto: ClasseQualquer *classeQualquer = [ClasseQualquer alloc]; 8.2.4. Métodos Como qualquer linguagem orientada a objetos, em Objective-C existem métodos. Métodos são funções definidas dentro de uma classe e podem ser de dois tipos: • Métodos de instância: são usados apenas através da instância de uma classe e em Objective-C são precedidos pelo caractere ‘-‘. Exemplo: - (void) facaAlgo { //Implementação } Caso existam parâmetros: - (void) facaAlgo:(NSString *) outroParametro: (NSInteger *){ //Implementação } Para fazer a chamada de métodos de instância: [objeto metodo]; Caso existam parâmetros: [objeto metodo: parametro1 outroParametro: parametro2]; • Métodos de classe: São usados diretamente com o nome da classe, não se fazendo necessária a existência de uma instância. Em Objective-C são precedidos pelo caractere ‘+’. Exemplo: + (void) facaAlgo { //Implementação } Caso existam parâmetros: + (void) facaAlgo:(NSString *) outroParametro: (NSInteger *){ 170 //Implementação } Para fazer a chamada de métodos de classe: [classe metodo]; Caso existam parâmetros: [classe metodo: parametro1 outroParametro: parametro2]; 8.2.5. Privilégios de acesso Os campos de uma classe não devem ser acessados de qualquer forma, pois a alteração de um desses campos indevidamente pode causar erros. Para evitar que isso aconteça existem os privilégios de acesso. Por padrão os campos em Objective-C são do tipo @protected, mas existem ainda 2 outros tipos, veja a lista: • @protected – Campo visível tanto na classe em que foi declarada quanto nas que herdam da mesma. • @public – Campo visível em todas as classes. • @private – Campo visível apenas na classe em que foi declarado. Exemplo de como mudar o privilégio de acesso de um campo: #import<Foundation/Foundation.h> @class OutraClasse; @interface ClasseQualquer : NSObject{ OutraClasse *outraClasse; @public float rate; @public NSString *name; } @end 8.2.6. Propriedades As propriedades permitem um maior controle sobre como os campos da classe são definidos ou retornados, sendo mais útil que os privilégios de acesso vistos anteriormente. Elas criam automaticamente os chamados “assessores” (getters e setters). Exemplo de como definir um campo como propriedade: #import <Foundation/Foundation.h> @class OutraClasse; @interface ClasseQualquer : NSObject{ OutraClasse *outraClasse; float rate; NSString *name; 171 } @property float rate; @property (retain, nonatomic) NSString *name; -(void)doSomething; -(void) doSomething:(NSString*)str; -(void)doSomething:(NSString*) strwithAnotherPara:(float)value; +(void)alsoDoSomething; @end Já na implementação usa-se o operador @synthesize como a seguir: #import“ClasseQualquer.h” @implementation ClasseQualquer @synthesize rate, name; Veja que após o operador @property do campo “name” foram colocadas as palavras “retain” e “nonatomic”, elas são as responsáveis pelas modificações na definição e no modo como o campo é retornado. 8.2.7. Inicializadores Quando uma instância de uma classe é criada existe a necessidade de inicializá-la. Em Objective-C é usada a palavra-chave “init” para a inicialização dessa instância. Entretanto, também há a possibilidade de criar inicializadores adicionais, definindo-se métodos iniciados com “init”. Exemplo de inicialização: [[ClasseQualquer alloc] init]; Exemplo de inicializadores adicionais: #import<Foundation/Foundation.h> @class OutraClasse; @interface ClasseQualquer:NSObject{ OutraClasse *OutraClasse; float rate; NSString *name; } @property float rate; @property (retain,nonatomic) NSString *name; -(void)doSomething; -(void)doSomething:(NSString*) str; 172 -(void)doSomething:(NSString*) strwithAnotherPara: (float)value; +(void)alsoDoSomething; - (id)initWithName:(NSString *) n; - (id)initWithName:(NSString *) n andRate:(float) r; @end E a implementação é feita da seguinte forma: #import“SomeClass.h” @implementationSomeClass @synthesizerate,name; - (id)initWithName:(NSString *) n { return [self initWithName:n andRate:0.0f]; } - (id)initWithName:(NSString *) n andRate:(float) r { if (self = [super init]) { self.name = n; self.rate = r; } return self; } 8.2.8 Protocolos Protocolos funcionam de forma bem parecida às interfaces de programação, porém com a diferença que o programador pode escolher o que a classe vai implementar ou não. Assim existe a possibilidade de se adicionar um protocolo e usar apenas um método declarado por ele. Para melhor entendimento veja o trecho abaixo: UIAlertView*alert=[[UIAlertViewalloc] initWithTitle:@”Hello” message:@”This is an alert view” delegate:self cancelButtonTitle:@”OK” otherButtonTitles:@”Option 1”, @”Option 2”, nil]; [alertshow]; O código anterior ilustra um alerta na tela, isso será explicado mais detalhadamente na seção 9.4. O trecho em negrito é usado quando se quer adicionar botões ao alerta, e para saber qual botão foi clicado é necessário manipular métodos declarados no protocolo UIAlertViewDelegate. Os protocolos devem ser adicionados no arquivo .h da classe em que se quer implementar, como no explicitado no exemplo a seguir: 173 @interface ObjCTestViewController:UIViewController <UIAlertViewDelegate> { //Declaração de atributos } @end Agora basta fazer a implementação do método desejado no arquivo .m. 8.3. Anatomia de um aplicativo para iOS e “Hello World” Para exemplificar a criação de um projeto, vamos desenvolver um aplicativo “Hello World”. Para isto, siga as seguintes etapas: 1. Abra o Xcode e você verá a tela de boas vindas como mostra a Figura 8.1. Acesse File New New Project para criar um novo projeto. 2. Você poderá observar na nova janela os vários tipos de projetos que podem ser criados, conforme explicado anteriormente. Escolha “View-based Application” e clique em Next. 3. Na próxima janela, nomeie o projeto para “HelloWorld” e em Device Family escolha iPhone. Clique em Next e escolha o local onde deseja salvar o projeto. No lado esquerdo da nova janela você poderá observar os vários arquivos gerados com o projeto. Assim como em qualquer aplicativo em C, em Objective-C o programa é inicializado por uma função main, que no nosso projeto está localizada na pasta Supporting Files, dentro do arquivo main.m. Porém, é necessário editá-lo. Foram criadas duas classes com o nosso projeto. A primeira, “HelloWorldAppDelegate”, receberá e manipulará importantes mensagens durante o tempo de execução do aplicativo. Já a “HelloWorldViewController” é a primeira visualização, que foi criada automaticamente porque escolhemos “View-Based Application” na hora da criação do projeto. Observe o método (BOOL)application: (UIApplication*) applicationdidFinishLaunchingWithOptions: (NSDictionary*) launchOptions presente no arquivo HelloWorldAppDelegate.m. Ele será invocado assim que o aplicativo for inicializado completamente. Observe que ele é o responsável pela exibição da primeira visualização, que é uma instância da classe HelloWorldViewController. 4. No lado esquerdo, realize um clique simples “HelloWorldViewController.xib” para exibir o Interface Builder. 174 no arquivo 5. No canto superior direito, existem botões para exibir novas áreas na janela. Clique no botão de utilitários, como destacado na Figura 8.1, para exibir a área de Utilitários. Figure 8.1. Interface Builder. 6. No canto inferior direito, observa-se a biblioteca de objetos. Clique e arraste um Label para o Interface Builder. Aumente o tamanho do label. Depois, com o label selecionado, clique no ícone “Attributes Inspector” para editar o mesmo. Na tag name, digite Hello World. Sua janela deverá ficar como na Figura 8.2. Figura 8.2. Colocando um label na aplicação. 175 7. Por fim, clique no botão Run, localizado no canto superior esquerdo. O iOS Simulator deverá abrir automaticamente e exibir o aplicativo com o Hello World, conforme ilustra a Figura 8.3. Figura 8.3. iOS Simulator com Hello World. 8.4. Actions e Outlets Outlets e Actions são dois dos principais conceitos que você tem que entender para programar para iOS. Quando o objetivo é utilizar atributos de uma determinada classe como referência para algum objeto criado pelo Interface Builder, é necessário declará-los como “Outlet”. Para isto, basta declarar o atributo no arquivo .h como segue abaixo: IBOutlet <Class> *<var>; em que <Class> representa o nome da classe a que pertence o atributo, e <var> é o nome da variável. Ex: IBOutlet NSString *nome; Caso o atributo não fosse declarado com a tag IBOutlet, ele não poderia ser visualizado pelo Interface Builder. Para ligar o Outlet a algum objeto do Interface Builder, siga os passos abaixo: 1. Com o projeto anterior aberto, vá no arquivo HelloWorldViewController.h e adicione o código destacado abaixo: … @interface HelloWorldViewController : UIViewController { 176 IBOutlet UIButton *button; } … 2. Clique no arquivo HelloWorldViewController.xib, insira um Round Rect Button logo abaixo do Label “Hello World” e renomeio para “Enviar”, da mesma maneira que foi inserido o label. 3. Clique com o botão direito no ícone File Owner, arraste para o botão criado e solte. Será exibida uma janela com os “Outlets” disponíveis para seleção, escolha “button”. Figura 8.4. Outlets. Pronto, agora você já pode manipular o botão criado no Interface Builder através do atributo “button” criado no .h. Vamos exemplificar isto alterando o nome do botão. Para isto, implementaremos o método viewDidLoad no arquivo HelloWorldViewController.m. Você pode observar que este método já existe, porém está em forma de comentário. Descomente-o e adicione o código destacado abaixo: -(void)viewDidLoad { [button setTitle:@”Novo” forState:UIControlStateNormal]; [super viewDidLoad]; } Pressione Run para testar, se tudo tiver ocorrido como o esperado, o iOS Simulator deverá exibir o botão com o nome “Novo”. 177 Já quando deseja-se que algum método seja invocado por algum objeto do Interface Builder, é necessário declará-lo no arquivo .h como segue abaixo: - (IBAction) <metodo> …; em que <metodo> receberá o nome do método. A utilização de parâmetros segue normalmente. Ex: -(IBAction) imprimeNome:(NSString*) nome; Para exemplificar a utilização de Actions, siga o exemplo abaixo: 1. Com o projeto anterior aberto, vá no arquivo HelloWorldViewController.h e adicione o código destacado abaixo: … @interface HelloWorldViewController : UIViewController { IBOutlet UIButton *button; } -(IBAction) buttonPressed; … 2. Vamos agora implementar o método criado. Para isto , abra o arquivo HelloWorldViewController.m e adicione o código destacado abaixo. … @implementation HelloWorldViewController -(void) buttonPressed { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Funcionou" message:@"Voce pressionou o botão.” delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; [alert release]; } … O que fizemos foi criar um alerta e exibí-lo toda vez que o método for chamado. 3. Abra o arquivo HelloWorldViewController.xib. Clique com o botão direito no botão “Enviar” e arraste para File Owner (sentido contrário ao feito com o Outlet). Será exibida uma janela com os eventos disponíveis, escolha “buttonPressed”. Com isso, toda vez que o botão for pressionado, ele chamará o método “buttonPressed”. 4. Salve o projeto, clique em “Run” e pressione o botão “Novo”. Se tudo tiver ocorrido como o esperado, será exibido um alerta como mostra a Figura 8.5. Clique em Ok para fechar o alerta. 178 Figura 8.5. IBAction, Alerta. 8.5. Adicionando views 8.5.1. Window-Based Application Até então o único template do XCode explorado foi o “View-based application” que já traz um controlador de visualizações e uma visualização ligada à janela principal da aplicação. Para o projeto alcançar essa etapa, primeiramente, ele foi desenvolvido a partir de um template básico chamado “Window-based application”. Este template oferece somente o alicerce para qualquer aplicação que rode em iPhone, e todo o restante deve ser criado pelo programador. A vantagem de se usar um template assim é que qualquer tipo de aplicação com qualquer tema inicial pode surgir de uma aplicação “Window-based”, em contrapartida, por ser um template muito básico, o desenvolvedor precisa construir toda a aplicação “quase do zero”. O programador deve escolher, de acordo com suas necessidades, o template adequado para sua aplicação. Se nenhum template for “correto” para o tipo de aplicação que o desenvolvedor almeja, então ele pode começar a aplicação a partir do template “Window-based application” e fazer todo o app da maneira que desejar. Agora iremos mostrar como o template “View-based application” é criado a partir do “Window-based application”. Para isso será criada uma visualização e um controlador de visualizações ao projeto. 1. Crie um projeto no XCode para uma aplicação iOS com o template “Windowbased application”, coloque o nome “Window” e selecione a opção destinado a iPhone. 2. Clique no arquivo “WindowAppDelegate.xib”, isso irá mostrar a janela principal do app. Note que, ao rodar o aplicativo neste ponto, será possível ver apenas uma tela 179 branca sem nenhuma visualização dentro, diferente de um template “View-based” que traz uma tela cinza. Isto ocorre porque não há um controlador de visualizações nem mesmo uma visualização vinculada ao aplicativo. 3. Agora vá no menu View/Utilities/ e clique em “Show Object Library”, uma coluna no lado direito da tela surgirá com os objetos na parte de baixo. 4. Arraste o objeto “View Controller” para qualquer local do editor fora da janela principal do app, isto irá exibir outra janela ao lado da principal. Neste passo você está inserindo um controlador de visualizações como objeto na instância que representa a janela principal do projeto. 5. Clique com o botão direito do mouse na pasta que tem o nome do projeto no navegador de projeto, coluna mais à esquerda do XCode. Na janela selecione a opção “New File” e então escolha o template do tipo iOS Cocoa Touch chamado “UIVIewController subclass”. Marque a opção “With XIB for user interface”, clique em “Next”, nomeie a classe para “primeiraView” e selecione o local para salvá-la. Agora você tem um programa mais modularizado com uma visualização separada para usar no projeto. Essa visualização ainda não está associada à janela principal do app, no entanto você já pode trabalhar nela. 6. Selecione o arquivo “primeiraView.xib” que foi gerado do passo anterior e insira um objeto UIButton chamado “Round Rect Button” que pode ser encontrado na biblioteca de objetos (coluna da direita no canto inferior). Realize um duplo clique nele e nomei-o para “Visualização conectada”. Ao rodar o aplicativo neste ponto você perceberá que, mesmo tendo alterado a visualização do arquivo que acabou de inserir, ainda não será possível acessá-la no app, pois ela ainda não está vinculada à janela principal. 7. Selecione o outro arquivo, “MainWindow.xib”, e clique no objeto “View Controller”. Acesse o menu View/Utilities/ e clique em “Show Identity Inspector”, ele irá aparecer no canto direito do construtor gráfico. Em “Custom Class” selecione a classe que você criou no passo 5. Agora vá no inspetor de atributos, clicando em “Show Attributes Inspector” em View/Utilities/, no campo “NIB Name” escreva o nome da classe que você criou no passo 5. Aperte Command + S. 8. Clique no arquivo WindowAppDelegate.h e adicione o seguinte trecho de código que está em negrito: #import <UIKit/UIKit.h> @class primeiraView; @interface mesaAppDelegate : NSObject <UIApplicationDelegate> @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet primeiraView *teste; @end Ao fazer isso você está declarando um atributo para o WindowAppDelegate da aplicação e ele será visível no construtor de interface. 180 objeto 9. Agora clique no arquivo WindowAppDelegate.m e adicione o trecho de código que está em negrito: #import "mesaAppDelegate.h" #import "primeiraView.h" @implementation mesaAppDelegate @synthesize window = _window,teste; (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { // Override point for customization after application launch. [_window addSubview:teste.view]; [self.window makeKeyAndVisible]; } return YES; - (void)dealloc// Últimas linhas { [teste release]; [_window release]; } [super dealloc]; 10.Vá ao MainWindow.xib, clique com o botão direito do mouse no ícone do objeto Window App Delegate e arraste para o ícone primeiraView. Irá aparecer uma pequena janela com o nome do atributo do tipo primeiraView criado na interface WindowAppDelegate.h, clique nele e salve. Esse movimento representa a conexão entre o atributo criado para o objeto WindowAppDelegate e a classe que você inseriu. 11.Pressione Command + R para rodar a aplicação. Ela deverá exibir a tela da classe inserida no passo 5. Este projeto que começou com o template Window-based Application é agora uma aplicação idêntica a uma que começa como o template View-based application. 8.5.2. Adicionando Views dinamicamente Outra maneira de se adicionar uma visualização é através do código. Dessa maneira pode-se colocar uma visualização com o aplicativo em execução, característica que deverá ser bastante explorada pelo desenvolvedor para aliviar a quantidade de elementos da primeira visualização, deixando o código mais modularizado e legível. O objeto UIView que será adicionado dinamicamente funciona como um container para outras visualizações como botões, textos, campos de texto e até mesmo para um outro container. A primeira visualização do projeto servirá de base para suportar o objeto que será inserido dinamicamente, portanto, este objeto dependerá da base para existir. 181 Para adicionar uma visualização dinamicamente, inicie um projeto do tipo “View-based application” destinado a iPhone com o nome viewBased. 1. Clique no arquivo viewBasedViewController.xib, insira um objeto “Round Rect Button” e nomeie-o para “Mudar Visualização”, aperte Command + S. Esse botão vai servir de gatilho para mudar da primeira para a segunda visualização. 2. Adicione um novo arquivo do tipo “UIViewController” com o nome “novaVisualizacao”. Clique no arquivo .xib gerado por ele, adicione um “Label” e coloque o nome “Nova Visualização” nele. Essa nova classe vai gerar o objeto que será usado na segunda visualização. 3. Clique no arquivo viewBasedViewController.h, e acrescente o seguinte trecho de código em negrito e aperte Command + S: #import <UIKit/UIKit.h> @class novaVisualizacao; @interface docsViewController : UIViewController @property(nonatomic, retain) novaVisualizacao *nova; -(IBAction)mudarVisualizacao; @end A nova visualização será declarada como um atributo da primeira com o nome “nova”. 4. Clique agora no arquivo da implementação viewBasedViewController.m, acrescente o trecho de código em negrito e aperte Command + S: #import "docsViewController.h" #import "novaVisualizacao.h" @implementation docsViewController @synthesize nova; … -(IBAction)mudarVisualizacao{ nova = [[novaVisualizacao alloc] init]; [self.view addSubview:nova.view]; } … -(void)dealloc{ [nova release]; [super dealloc]; } O método mudarVisualizacao, que será acionado pelo botão “Mudar Visualização”, irá criar um objeto da classe que você inseriu no passo 2 e associá-lo ao atributo “nova”. O 182 método “addSubview” chamado pelo campo “view” irá colocar a segunda visualização em cima da primeira e a partir de agora o usuário irá interagir com ela. 5. Agora vá ao viewBasedViewController.xib, clique com o botão direito do mouse no elemento UIButton que você inseriu no primeiro passo, arraste para o “File’s Owner” e selecione o método que aparece na tela. Agora está feita a vinculação que fará o botão da interface chamar o método. 6. Clique em Command + R para executar o projeto. 8.5.3. Animando a transição de visualizações A implementação anterior mostra uma transição brusca de uma visualização para outra, sem nenhum efeito visual para deixar a aplicação mais agradável. Os apps, no entanto, devem apresentar recursos estéticos para tornar a aplicação mais aprazível e consequentemente atrair mais usuários. Um desses efeitos disponíveis pelo framework UIKit é o de transição de visualizações. Para mostrar como é implementado o efeito você pode aproveitar o projeto anterior. 1. Clique em <NomeDoProjeto>ViewController.m e dentro do corpo do método mudarVisualizacao insira o seguinte trecho em negrito: (IBAction)mudarVisualizacao { self.nova = [[novaVisualizacao alloc] init]; [UIView beginAnimations:@"Curl" context:nil]; [UIView setAnimationCurve:UIViewAnimationCurveEaseIn]; [UIView setAnimationDuration:.4]; [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES]; [self.view addSubview:nova.view]; [UIView commitAnimations]; } Essa série de métodos da classe UIView serve para customizar a animação de transição e esta transição pode ser aplicada a qualquer objeto da classe UIView. 2. Aperte Command + R para rodar o projeto. 8.6. Suporte a multi-plataformas para iPhone e iPad Por ter o mesmo sistema operacional que o iPhone e o iPod Touch, o iPad pode rodar aplicativos desenvolvidos para ambos dispositivos. Porém, a visualização do aplicativo 183 na tela do iPad terá a mesma dimensão usada pelo iPhone. Isso significa um grande desperdício, visto que a tela do iPad possui uma dimensão maior que a tela do iPhone. Para resolver esse problema, a Apple recomenda que se crie aplicações universais alterando apenas a interface de usuário. Veja a seguir como isso é feito (Figura 8.6): 1. Usando o Xcode, crie uma “View-based application” (para iPhone) e nomeie-o como “Universal”. 2. Abra o UniversalViewController.xib adicione um “Label” e mude seu texto para “iPhone application”. 3. Na tela inicial do projeto, em targets, selecione o aplicativo e em devices, selecione Universal. Então clique em YES. Figura 8.6. Tornando o aplicativo multiplataforma. 4. Veja que foi criada uma pasta para recursos do iPad. Crie uma nova classe do tipo UIViewController destinada a iPad e nomeie como iPadUniversal. 5. Edite o novo .xib criado. 6. Selecione MainWindow-iPad.xib para que seja editado no Interface Builder. Agora selecione o item Universal View Controller e no inspetor de identidade coloque Class como “iPadUniversal”. 7. Agora em inspetor de atributos defina NIB Name como “iPadUniversal”. Dessa forma o aplicativos terá duas interfaces de usuário, sendo cada uma iniciada no dispositivo para o qual foi desenvolvido. 184 8.7. Rotação de tela Os dispositivos que utilizam iOS possuem a capacidade de detectar a orientação da interface. Assim existe a possibilidade de adequar a interface do usuário à nova orientação, nesta seção descreve como é feita a rotação. Por padrão o projeto no Xcode é criado com suporte a única orientação, para habilitar mas múltiplas orientações é preciso sobrescrever o método “shouldAutorotateToInterfaceOrientation”. Esse método é chamado sempre que a orientação muda e retorna ‘YES’ caso esteja em orientação “Portrait” (retrato). Então basta mudá-lo para que sempre retorne ‘YES’ de modo que o método fique como a seguir: - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation { return YES; } Agora, para colocar cada objeto em seu lugar na tela siga o passo-a-passo: 1. Crie um projeto “View-Based Application” no Xcode, nomeie-o ScreenRotations e edite-o no Interface Builder adicionando um botão. 2. No arquivo ScreenRotationsViewController.h adicione o seguinte código: #import <UIKit/UIKit.h> @interface ScreenRotationsViewController : UIViewController { IBOutlet UIButton *btn; } @property (nonatomic, retain) UIButton *btn; @end 1. No Interface Builder, conecte o Outlet que você criou clicando com botão direito do mouse e arrastando até o Rounded Rect Button. Selecione btn. 2. No arquivo ScreenRotationsViewController.m adicione o seguinte código: #import “ScreenRotationsViewController.h” @implementation ScreenRotationsViewController @synthesize btn; -(void) positionViews { UIInterfaceOrientation destOrientation = self.interfaceOrientation; if (destOrientation == UIInterfaceOrientationPortrait || destOrientation == UIInterfaceOrientationPortraitUpsideDown) { //---if rotating to portrait mode--btn.frame = CGRectMake(20, 20, 233, 37); } else { 185 btn.frame = CGRectMake(227, 243, 233, 37); } } - (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation duration:(NSTimeInterval) duration { [self positionViews]; } - (void)viewDidLoad { [self positionViews]; [super viewDidLoad]; } - (void)dealloc { [btn release]; [super dealloc]; } 8.8. Conectividade Serviços de conexão no iOS podem ser feitos de várias maneiras. Podem ser usadas redes wi-fi ou bluetooth para conectar dispositivos. No entanto, o iOS Simulator apenas utiliza rede wi-fi para fazer as simulações. Ao simular uma conexão Bluetooth o simulador vai, efetivamente, utilizar a rede wi-fi. Nesta sessão mostraremos como fazer uma conexão entre dois aparelhos via bluetooth. Para isto, utilizaremos o Game Kit, que é um framework que contém APIs que permitem a comunicação através de uma rede bluetooth. Instanciaremos a classe GKSession para representar uma sessão entre dois dispositivos conectados. Também utilizaremos uma instância da classe GKPeerPickerController, que providência uma interface gráfica para procurar e conectar com outros dispositivos. Para isto, siga os seguintes passos: 1. Crie um novo projeto utilizando o template View-Based Application, nomeie-o “Bluetooth”. Adicione o GameKit.framework ao projeto. Para isto, acesse Targets Build Phases Link Binary With Libraries, clique no sinal ‘+’ e selecione o GameKit.framework. 2. Adicione 3 botões, 1 label e 1 Text Field ao BluetoothViewController.xib e renomeie-os como mostra a Figura 8.7. 186 Figura 8.7. Interface. 3. Em BluetoothViewController.h, adicione o código destacado abaixo: #import <UIKit/UIKit.h> #import <GameKit/GameKit.h> @interface BluetoothViewController : UIViewController <GKSessionDelegate, GKPeerPickerControllerDelegate> { GKSession *currentSession; GKPeerPickerController *picker; } IBOutlet IBOutlet IBOutlet IBOutlet @property @property @property @property UIButton* conectar; UIButton* disconectar; UIButton* enviar; UITextField *texto; (nonatomic, (nonatomic, (nonatomic, (nonatomic, retain) retain) retain) retain) GKSession *currentSession; UITextField *texto; UIButton *conectar; UIButton *disconectar; -(IBAction) btnSend:(id) sender; -(IBAction) btnConectar:(id) sender; -(IBAction) btnDisconectar:(id) sender; @end 4. Conecte os botões e o Text Field aos seus respectivos atributos pelo Interface Builder, como já foi feito com projetos anteriores. Faça o mesmo com os métodos. 5. Em BluetoothViewController.m, adicione o código destacado abaixo para implementar os métodos necessários: @synthesize conectar,desconectar,texto,currentSession; -(IBAction) btnConectar:(id) sender { picker = [[GKPeerPickerController alloc] init]; picker.delegate = self; 187 } picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby; [conectar setHidden:YES]; [desconectar setHidden:NO]; [picker show]; -(IBAction) btnDesconectar:(id) sender { [self.currentSession disconnectFromAllPeers]; [self.currentSession release]; currentSession = nil; [conectar setHidden:NO]; [desconectar setHidden:YES]; } -(void)peerPickerController:(GKPeerPickerController *)pk didConnectPeer:(NSString *)peerID toSession:(GKSession *)session { self.currentSession = session; session.delegate = self; [session setDataReceiveHandler:self withContext:nil]; picker.delegate = nil; [picker dismiss]; [picker autorelease]; } -(void)peerPickerControllerDidCancel:(GKPeerPickerController *)pk { picker.delegate = nil; [picker autorelease]; [conectar setHidden:NO]; [desconectar setHidden:YES]; } -(void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state { switch (state) { case GKPeerStateConnected: NSLog(@"Conectado"); break; case GKPeerStateDisconnected: NSLog(@"Desconectado"); [self.currentSession release]; currentSession = nil; [conectar setHidden:NO]; [desconectar setHidden:YES]; break; } } -(void)session:(GKSession *)session didFailWithError:(NSError *)error { NSLog(@"%@",[error description]); } - (void) mySendDataToPeers:(NSData *) data { if (currentSession) [self.currentSession sendDataToAllPeers:data withDataMode:GKSendDataReliable error:nil]; } -(IBAction) btnSend:(id) sender { NSData* data; NSString *str = [NSString stringWithString:texto.text]; data = [str dataUsingEncoding: NSASCIIStringEncoding]; [self mySendDataToPeers:data]; } 188 - (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context { NSString* str; str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Informacao recebida" message:str delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; [str release]; } - (void)dealloc { [texto release]; [currentSession release]; [conectar release]; [desconectar release]; [super dealloc]; } - (void)viewDidLoad { [conectar setHidden:NO]; [desconectar setHidden:YES]; [super viewDidLoad]; } 6. Rode o projeto e pressione o botão Conectar. O método btnConectar será chamado quando o botão “Conectar” for pressionado. Ele configura e exibe o PickerController. Figura 8.8. PeerPickerController 189 Quando algum dispositivo for encontrado, o picker exibirá uma lista com esses dispositivos, para que o usuário possa escolher com qual deseja se conectar. Após selecionar dispositivo para conexão, será exibida uma interface de espera, até que o outro dispositivo aceite a conexão. Caso a conexão seja aceita, o método session: peer: didChangeState: será chamado automaticamente. Ao enviar uma mensagem, o método btnSend transformará o texto em Data e enviará a informação através do método mySendDataToPeers. Ao receber uma mensagem, o método receiveData: fromPeer: inSession :context será invocado, decodificará a informação em String e a exibirá em um alerta. 8.9. – Considerações finais Este minicurso abordou princípios fundamentais e alguns conceitos mais técnicos relacionados a programação para a plataforma de dispositivos móveis iOS. Ele teve como objetivo servir de ponto de partida para aqueles que desejam se aprofundar mais na programação para o sistema operacional móvel da Apple. Para isto, recomenda-se a leitura dos livros mencionados na bibliografia. 8.10. - Bibliografia − ALI, Maher. Advanced iOS 4 programming: developing mobile applications for Apple iPhone, iPad and iPod Touch. Wiley. EUA: 2010. 698 p. − LEE, Wei-‐Meng. Beginning iOS 4 development application. Wiley. EUA: 2010. 631 p. − iOS Dev Center <http://developer.apple.com/devcenter/ios/index.action>. Acessado em 23/09/2011, às 14:37. 190 Capítulo 9 Desenvolvimento Rápido de Aplicações Móveis Utilizando a Linguagem Declarativa QML Ricardo Erikson V. S. Rosa, Adriano M. Gil, Paulo R. B. Mendonça, Cícero F. F. Costa Filho e Vicente F. Lucena Jr. Abstract The emerging software development technologies have their focus on reducing the timeto-market of new products. In the mobile applications context, reduce time-to-market is critical to maximize ROI. QML (Qt Meta-object Language) is part of a declarative UI framework known as Qt Quick which aims to facilite the rapid development of applications. This course aims to present the main components and features of the QML language and Qt Quick UI framework addressed to the development of mobile applications. Resumo As novas tecnologias de desenvolvimento de software buscam meios de reduzir o tempo excessivo gasto na criação de novos produtos. No contexto de aplicações móveis, reduzir o tempo que um produto leva para chegar ao mercado é crítico para maximizar o retorno sobre investimento. QML é uma linguagem declarativa que facilita o desenvolvimento rápido de interfaces do usuário (UIs) e consequentemente reduz o tempo de desenvolvimento. Este minicurso tem por objetivo apresentar os principais componentes e características da linguagem QML e do framework Qt Quick com foco em aplicações móveis. 9.1. Introdução Desenvolvedores de aplicações móveis buscam meios de reduzir o tempo excessivo gasto na criação e publicação de novos produtos. No mercado das app stores, as lojas online de aplicativos, o desenvolvedor precisa considerar três parâmetros de tempo: (1) tempo de desenvolvimento, (2) time to shelf (intervalo de tempo entre a submissão de uma aplicação e sua publicação para compra na loja) e (3) time to payment (período de tempo entre a venda da aplicação e os rendimentos chegarem até o desenvolvedor). A redução 191 do intervalo de tempo compreendido entre o início do desenvolvimento e o retorno dos rendimentos é um fator crítico para maximizar o ROI. Levando em consideração o tempo de desenvolvimento, a rapidez na codificação e na prototipagem dos aplicativos é apontada como uma das principais razões técnicas para que os desenvolvedores escolham uma plataforma de desenvolvimento [10]. Os resultados de codificação e prototipagem são muitas vezes apresentados na forma de interfaces do usuário (UIs). No entanto, a dificuldade de criação dessas UIs, onde os usuários têm que aprender novas APIs inteiramente sem levar em conta as experiências de desenvolvimento em outras plataformas, é destacada como um dos problemas comumente enfrentado pelos desenvolvedores em várias plataformas [9, 10]. QML (Qt Meta-objects Language) é uma linguagem declarativa utilizada no kit de desenvolvimento Qt Quick (Qt User Interface Creation Kit) [8] que faz parte do framework Qt1 . QML é utilizada no desenvolvimento de aplicativos cross-platform e busca facilitar o projeto e a implementação de UIs para dispositivos móveis através da rapidez na codificação e na prototipagem. O estilo de programação da linguagem QML é baseado nas linguagens CSS (Cascading Style Sheets) e JavaScript, tornando-se de aprendizado rápido e fácil para programadores C, Qt/C++, Java e principalmente desenvolvedores web. Diferentemente de outras linguagens de programação que utilizam a abordagem imperativa para construir as UIs, QML utiliza o paradigma declarativo. A utilização desse paradigma permite que os desenvolvedores descrevam UIs declarando somente os objetos que devem ser exibidos, ou seja, sem descrever o fluxo de controle ou sequência de ações que devem ser executadas para criar a interface. Enquanto a UI é criada de maneira declarativa, a lógica da aplicação pode ser escrita de maneira imperativa através das linguagens JavaScript e C++. Isso facilita a separação entre a UI e a lógica da aplicação, permitindo que projetos Qt/C++ baseados no padrão model-view-controller (MVC) sejam facilmente portados para QML [1, 2]. Uma das principais características da linguagem QML é a flexibilidade na criação das UIs. Essa flexibilidade é possível graças à possibilidade de adicionar e manipular elementos como formas, imagens e texto. Outros elementos como estados, transições, animações, transformações, efeitos gráficos e elementos de interação permitem a criação de aplicações mais sofisticadas em QML, principalmente quando utilizados em conjunto com JavaScript e C++. Este capítulo tem o objetivo de apresentar a linguagem QML e sua utilização no desenvolvimento rápido de aplicações móveis. Os principais características da linguagem são apresentados na Seção 9.2. A utilização dos elementos no tratamento de eventos é demonstrada na Seção 9.3. A Seção 9.4 mostra os principais elementos utilizados na criação de animações. Os mecanismos de criação de componentes customizados são descritos na Seção 9.5. As particularidades das principais plataformas de desenvolvimento são mostradas na Seção 9.6 e, por fim, as considerações finais são feitas na Seção 9.7. 1 Qt é um framework de desenvolvimento de aplicações cross-platform que foi desenvolvido em C++ e está disponível para Windows, Linux e Mac OS X. O framework disponibiliza um amplo conjunto de APIs que podem ser utilizadas no desenvolvimento para plataformas móveis embarcadas e desktop. 192 9.2. Linguagem Declarativa QML Muitas plataformas móveis disponibilizam APIs, em linguagens de programação comumente conhecidas (por exemplo Symbian C++, Qt/C++, Java, e Objective-C), para que os desenvolvedores criem as UIs dos aplicativos. Porém, muitas vezes o custo de aprendizado de uma API inteira, considerando também as particularidades da sintaxe de cada linguagem, é bastante alto [9]. Além disso, o paradigma imperativo, que é frequentemente empregado nas linguagens de programação modernas, faz com que o processo de criação das UIs se torne árduo e demorado [12]. Baseando-se nesses fatos, o desenvolvimento cross-platform utilizando plataformas que possibilitem baixo custo de aprendizado e rápida prototipagem parece ser uma alternativa desejável no desenvolvimento de aplicativos móveis. A linguagem declarativa QML fornece um ambiente de programação bastante simples e flexível, porém robusto. Ela dispõe de um amplo conjunto de componentes, para a criação das UIs, e de mecanismos de integração (bindings) com as linguagens JavaScript e Qt/C++. Os objetos da UI dos aplicativos QML são especificados por elementos que possuem tipo e propriedades (no estilo nome:valor, como em CSS), o que torna a linguagem intuitiva e de fácil aprendizado tanto para designers de UI quanto para programadores. Para ilustrar isso, as Listagens 9.1 e 9.2 apresentam uma comparação entre as linguagens Qt/C++ e QML para a criação de um simples elemento visual. O código Qt/C++ necessário para desenhar um retângulo é apresentado na Listagem 9.1. Esse código exibe o retângulo na UI e define propriedades como largura, altura e cor. 1 void paint(QPainter *painter, 2 const QStyleOptionGraphicsItem *option, 3 QWidget *widget){ 4 QRect rect(0, 0, 200, 100); 5 painter->setBrush(QBrush(Qt::green)); 6 painter->drawRect(rect); 7 } Listagem 9.1. Código Qt/C++ necessário para exibir um retângulo. A Listagem 9.2 exibe o código QML necessário para desenhar o mesmo retângulo. O retângulo criado consiste em um único objeto do tipo Rectangle com 3 propriedades (width, height e color). 1 import QtQuick 1.0 2 Rectangle { 3 width: 200 4 height: 100 5 color: "green" 6 } Listagem 9.2. Código QML necessário para exibir um retângulo. 193 9.2.1. Ambiente de Desenvolvimento O ambiente necessário para o desenvolvimento de aplicativos em QML é disponibilizado juntamente com o QtSDK [5], que pode ser obtido em http://qt.nokia.com/downloads. O QtSDK inclui o Qt Quick e disponibiliza a ferramenta Qt QML Viewer [6], que é muito utilizada no desenvolvimento e depuração de código QML, mas não deve ser utilizada em ambiente de produção. Outra opção disponível no SDK é a utilização da IDE Qt Creator, que fornece um ambiente completo para o desenvolvimento de aplicativos em Qt/C++ e QML. Uma aplicação QML é executada através da máquina de execução QML, também chamada de QML runtime, para ser executada. Existem duas maneiras de se iniciar essa máquina de execução: (1) a partir de uma aplicação Qt/C++ (utilizando a classe QDeclarativeView) ou (2) através da ferramenta Qt QML Viwer2 . Aplicativos QML destinados ao usuário final (para instalação e utilização no dispositivo móvel) devem utilizar a primeira opção. Já os aplicativos quando ainda em processo de desenvolvimento podem ser testados utilizando o Qt QML Viewer. Os exemplos apresentados nas próximas seções podem ser testados utilizando o Qt QML Viewer. Porém, mais adiante neste capítulo, será apresentado como invocar aplicativos QML utilizando código Qt/C++. 9.2.2. Propriedades e Tipos de Dados Os objetos QML são especificados por meio de seus elementos e cada elemento possui um conjunto de propriedades. Essas propriedades são formadas por pares nome-valor (por exemplo, color:“blue”) e assumem uma variedade de tipos de dados que podem ser referências para outros objetos, strings, números, etc. Em QML, as propriedades são fortemente tipadas, ou seja, se uma propriedade possui um tipo específico então um valor de tipo diferente não pode ser atribuído à ela. A Tabela 9.1 apresenta alguns dos tipos de dados comumente utilizados em propriedades QML. Objetos definidos em QML podem ser referenciados por outros objetos por meio da propriedade id. Essa propriedade é definida explicitamente pelo programador para identificar um objeto dentro do escopo QML. Dessa maneira, o valor de uma propriedade de um determinado objeto podem ser externamente acessado por <id>.<propriedade>, como na linha 5 da Listagem 9.3 com obj2.width. Uma propriedade pode ser expressada em função de outra propriedade do mesmo objeto (ou de um objeto diferente, por meio de seu id) desde que possuam tipos compatíveis. Dessa maneira, se o valor da propriedade referenciada for alterado durante a execução aplicação, a máquina de execução QML recalcula a expressão e atualiza o valor da propriedade que fez a referência. Por exemplo, na Listagem 9.3 a propriedade height é definida pela expressão obj2.width*2. Então, se o valor da propriedade width for alterado, o valor de height será automaticamente atualizado, alterando também a aparência visual do objeto. QML permite que novas propriedades sejam declaradas na definição de um objeto. Ainda na Listagem 9.3 (linha 7), a propriedade area, que não faz parte da definição 2 Para testar o aplicativo é necessário executar o seguinte comando: $ qmlviewer arquivo.qml 194 Tabela 9.1. Alguns tipos utilizados no sistema de tipagem da linguagem QML. Tipo color bool date time font int double real list point rect size string url Descrição Nomes de cores especificadas no padrão SVG [11] entre aspas. Os valores também podem ser especificados nos formatos “#RRGGBB” ou “#AARRGGBB”. Pode assumir os valores true ou false. Data especificada no formato “YYYY-MM-DD”. Horário especificado no formato “hh:mm:ss”. Encapsula as propriedades de uma instância do tipo QFont do Qt. Representa valores inteiros. Possui ponto decimal e seus valores são armazenados com precisão dupla. Representa um número real. Representa uma lista de objetos. Representa um ponto através das coordenadas x e y. Consiste na representação dos atributos x, y, width e height. Consiste na representação dos atributos width e height. Texto livre entre aspas. Representa a localização de algum recurso, como um arquivo, através de seu endereço. original do tipo Rectangle, é definida como sendo o produto width*height. O valor dessa propriedade é atualizado pela máquina de execução QML sempre que os valores de width e height são alterados. 1 import QtQuick 1.0 2 Rectangle { 3 id: obj1 4 width: 200 5 height: obj2.width*2 6 color: "#008000" 7 property real area: width*height 8 } Listagem 9.3. Exemplos da utilização de propriedades na linguagem QML. 9.2.3. Elementos Básicos A linguagem QML oferece um conjunto de elementos que permitem a rápida e fácil inclusão de objetos nos aplicativos. Esses elementos podem ser aninhados permitindo a criação de um relacionamento do tipo pai-filho (parent-child) entre eles. Esse tipo de relacionamento pode ser muito útil para a criação de layouts baseados em pontos de ancoragem, que será apresentado na Seção 9.2.4. Uma lista dos elementos mais básico comumente encontrados em QML está disponível na Tabela 9.2. Esses elementos, através de suas propriedades e seus sinais 3 , 3 Sinais (de sinais e slots) é uma característica muito importante no tratamento de eventos do framework 195 Tabela 9.2. Alguns elementos básicos disponíveis na linguagem QML. Elemento Item Rectangle Image Text TextInput MouseArea Descrição O elemento mais básico da linguagem QML. Embora não possua aparência visual, ele é utilizado como base de todos os elementos visuais. É um dos elementos mais básicos para a criação de aplicações em QML. É utilizado para preenchimento de áreas e é frequentemente utilizado como base para o posicionamento de outros elementos. O elemento é utilizado para exibir imagens na UI. Todos os formatos suportados pelo Qt podem ser utilizados no elemento Image, incluindo PNG, JPEG e SVG. É utilizado para adicionar texto na UI. Esse elemento possibilita a entrada de texto no aplicativo, podendo restringir o formato dos dados de entrada través da utilização de uma máscara de validação. É um elemento invisível, frequentemente utilizado em conjunto com elementos visíveis, no tratamento de eventos de entrada com o ponteiro do mouse ou toques na tela. possibilitam a criação das UIs e o tratamento de eventos em aplicativos móveis. Uma lista mais completa dos elementos QML pode ser encontrada em [4]. 9.2.4. Layouts Baseados em Pontos de Ancoragem O layout baseado em pontos de ancoragem é um poderoso recurso proporcionado pela linguagem QML e é muito útil no posicionamento dos objetos na UI dos aplicativos. Cada elemento QML possui basicamente seis pontos invisíveis de ancoramento: left, right, top, bottom, horizontalCenter e verticalCenter. Esses pontos possibilitam a criação de relacionamentos entre as linhas de ancoragem de diferentes objetos. Em outras palavras, esses pontos facilitam o posicionamento de um objeto em relação a outros objetos adjacentes. A Figura 9.1 apresenta um exemplo da utilização de pontos de ancoragem com dois elementos do tipo Rectangle. O código apresentado a esquerda (Figura 9.1(a)) realiza a ancoragem da borda esquerda do objeto obj2 à borda direita do objeto obj1. Dessa maneira, obj2 sempre fica localizado à direita de obj1. O resultado visual da utilização desses pontos de ancoragem é apresentado na Figura 9.1(b). Múltiplos pontos de ancoragem podem ser especificados por objeto. Esse tipo de abordagem permite maior flexibilidade no posicionamento dos objetos assim como melhor ajuste da UI do aplicativo. Isso pode ser muito útil na adaptação de aplicativos para execução em dispositivos com tamanhos de tela diferentes. Uma das abordagens utilizadas por designers gráficos é criar áreas de flutuação (que variam de tamanho) para facilitar o reajuste dos objetos da UI em tamanhos de tela variados. A utilização de ancoragem permite que os valores de largura e/ou altura dessas áreas sejam ajustados sem que sejam Qt. Os sinais são emitidos com a intenção de comunicar eventos que ocorrem na aplicação. Os slots, por sua vez, são funções que são chamadas respondendo aos sinais, ou seja, aos eventos. 196 1 2 3 4 5 6 7 8 9 Rectangle { id:obj1; ... Text {text: "obj1"; ...} } Rectangle { id:obj2; ... Text {text: "obj2"; ... } anchors.left: obj1.right } (a) Código (b) Resultado Figura 9.1. Ancoragem da borda direita do objeto obj1 à borda esquerda do objeto obj2. definidos explicitamente. A Figura 9.2(a) apresenta o código para a criação de uma área que é redimensionada dependendo do posicionamento dos outros objetos. Na definição do objeto obj2, o ponto top foi ancorado ao bottom do objeto obj1, e o ponto bottom foi ancorado ao top do objeto obj3. Como resultado, a propriedade height do objeto obj2 é dependente do posicionamento dos objetos obj1 e obj3, não necessitando defini-la explicitamente. O resultado visual dessa ancoragem é apresentado na Figura 9.2(b). 1 2 3 4 5 6 7 8 9 10 11 12 13 Rectangle { id: obj1; ... x: 0; y: 0; width: 120 } Rectangle { id: obj2; ... anchors.top: obj1.bottom anchors.bottom: obj3.top } Rectangle { id: obj3; ... x: 0; y: 200; width: 120 } (a) Código (b) Resultado Figura 9.2. Exemplo da utilização de ancoragem para a criação um objeto que varia a propriedade height automaticamente. Uma propriedade de ancoragem muito útil e frequentemente utilizada para o preenchimento de objetos é o anchors.fill. Essa propriedade convenientemente fixa as âncoras left, right, top e bottom de um objeto às âncoras left, right, top e bottom de um objeto alvo. Essa propriedade é muitas vezes utilizada pelo tipo MouseArea para criar uma área clicável com as mesmas dimensões de um outro objeto. Outra propriedade de conveniência bastante utilizada é o anchors.centerIn. Essa propriedade é utilizada para automaticamente centralizar um objeto em relação à outro objeto. A propriedade anchors.centerIn fixa as âncoras verticalCenter e horizontal- 197 Center de um objeto às âncoras verticalCenter e horizontalCenter de outro objeto. 9.2.5. Criando a Primeira Aplicação QML Como explicado anteriormente na Seção 9.2.1, para que seja instalada e executada no dispositivo móvel, uma aplicação QML precisa ser executada a partir de uma aplicação Qt/C++ utilizando a classe QDeclarativeView. A IDE Qt Creator possui templates que facilitam a criação de projetos para executar aplicações QML. No lado esquerdo da tela de criação de novos projetos do Qt Creator, deve ser escolhido um projeto do tipo Qt Quick Project, e no lado direito, um template do tipo Qt Quick Application. Após a escolha do nome do projeto e sua localização, é necessário escolher o target. Dentre as opções de target apresentadas, normalmente são escolhidas duas opções para auxiliar no desenvolvimento de aplicações móveis: Desktop e Remote Compiler. A opção Desktop é utilizada para compilação e execução da aplicação no próprio desktop durante o desenvolvimento. Já a opção Remote Compiler é utilizada para a compilação dos aplicativos para plataformas móveis específicas. No projeto criado, o arquivo principal da aplicação (main.cpp) possui o código que executa o arquivo QML principal, conforme apresentado na Listagem 9.4. Nesse exemplo, o objeto viewer do tipo QmlApplicationViewer é configurado e define o arquivo QML principal como sendo “qml/ERCEMAPI/main.qml” (linha 10). Em seguida, o método showExpanded() do objeto viewer apresenta a o arquivo QML na tela do aplicativo, conforme apresentado na linha 11. O tipo QmlApplicationViewer é um tipo definido no template de criação do projeto e é do tipo QDeclarativeView. O QmlApplicationViewer é uma classe de conveniência criada com configurações específicas de código para cada plataforma móvel, como orientação de tela e debug. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 // Arquivo: main.cpp #include <QtGui/QApplication> #include "qmlapplicationviewer.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QmlApplicationViewer viewer; viewer.setOrientation( QmlApplicationViewer::ScreenOrientationAuto); viewer.setMainQmlFile(QLatin1String("qml/ERCEMAPI/main.qml")); viewer.showExpanded(); return app.exec(); } Listagem 9.4. Arquivo Qt/C++ principal de uma aplicação QML executável no dispositivo móvel. Na Listagem 9.5 encontra-se o arquivo “main.qml”, que possui código QML principal da aplicação. Esse código possui uma aplicação simples que exibe uma mensagem “Olá Mundo QML” e exibe uma imagem com o logo do Framework Qt ao se clicar em 198 botão. Logo na primeira linha do arquivo, é declarada a instrução import QtQuick 1.0, utilizada para importar os principais elementos da linguagem QML. A seguir é declarado um elemento do tipo Rectangle como a base do aplicativo. É interessante observar que o elemento Item também pode ser utilizado para criar essa base para a construção do aplicativo. Nas linhas seguintes, o restante da aplicação é construída declarando-se objetos dos tipos Image, Rectangle, Text e MouseArea. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 // Arquivo: main.qml import QtQuick 1.0 Rectangle { width: 640; height: 360 color: "white" Image { id: img y: 250 source: "qt.png" visible: false anchors.horizontalCenter: parent.horizontalCenter } Rectangle { color: "limegreen"; width: 150; height: 50; radius: 5 anchors.horizontalCenter: parent.horizontalCenter; y: 50 Text { anchors.centerIn: parent; font { family: "Helvetica"; pointSize: 20 } text: "Clique aqui" } MouseArea { anchors.fill: parent onClicked: { msg.text = "Olá Mundo QML" img.visible = true } } } Text { id: msg; font { family: "Helvetica"; pointSize: 40 } anchors.centerIn: parent; text: "" } } Listagem 9.5. Arquivo QML principal com o código da aplicação. 9.3. Tratamento de Eventos Um dos pontos mais importantes no desenvolvimento de software para dispositivos móveis é o tratamento de eventos, pois é através desses eventos que ocorre a interação entre o usuário e a aplicação. Nesta seção serão abordadas as principais maneiras de interação com o usuário através do mecanismo de tratamento de eventos utilizado na linguagem QML e no framework Qt Quick. 199 9.3.1. Sistema de Eventos Baseado em Sinais O tratamento de eventos da linguagem QML é similar ao mecanismo de sinais e slots disponível no framework Qt. Em QML os sinais são emitidos para notificar a ocorrência de eventos e estão conectados a slots específicos (também conhecidos como manipuladores de sinais ou signal handlers). Esses slots, por sua vez, permitem que código JavaScript seja executado em resposta à um evento. Dessa maneira, sempre que um determinado sinal associado a um evento for emitido, o slot associado a esse sinal é chamado e o código JavaScript contido nele é executado. Cada elemento QML possui seus próprios slots, cada um associado a um sinal ou mesmo à uma propriedade. No caso dos sinais, os slots seguem uma sintaxe padrão do tipo: on<Sinal>. Por exemplo, o tipo MouseArea possui o sinal clicked e um slot associado chamado onClicked. De maneira semelhante, cada propriedade possui um slot associado às mudanças de valor através da seguinte sintaxe: on<Propriedade>Changed. Por exemplo, o slot onWidthChanged refere-se ao slot da propriedade width. Assim, sempre que ocorrerem alterações no valor dessa propriedade, seu slot correspondente será chamado. O código exibido na Listagem 9.6 apresenta um exemplo de tratamento de evento de clique em um elemento do tipo MouseArea. Esse código fecha a aplicação executando o comando JavaScript quit() disponível na variável global Qt. 1 MouseArea { 2 anchors.fill: parent 3 onClicked: { 4 Qt.quit(); 5 } 6 } Listagem 9.6. Tratamento de evento de clique no elemento MouseArea. Ao declarar novos sinais ou propriedades, o framework Qt Quick automaticamente cria slots seguindo as convenções de nomenclatura apresentadas anteriormente. Logo, é possível construir um sistema próprio de eventos, estendendo os sinais e slots básicos providos pelo framework Qt Quick. Por exemplo, ao implementar um teclado virtual, seria interessante que a assinatura do sinal tivesse um parâmetro para enviar o valor da tecla pressionada ao slot quando o sinal for emitido. O código exibido na Listagem 9.7 mostra como um teclado virtual poderia ser implementado em QML. O elemento Rectangle não possui um sinal que é emitido na ocorrência de eventos de clique, e tão pouco um slot para tratar esses eventos. Dessa maneira, um sinal clicked(string value) é declarado recebendo como argumento um valor do tipo string (linha 5). Ao declarar esse sinal, o slot onClicked é criado automaticamente no objeto button do tipo Rectangle. No slot onClicked do elemento MouseArea, o sinal button.clicked(string value) é emitido e passa como argumento o valor buttonText.text (linha 13). No slot onClicked do objeto button, essa propriedade pode ser acessada através do parâmetro value passado pelo sinal clicked(string value), conforme apresentado na linha 15. 200 1 import QtQuick 1.0 2 3 Rectangle { 4 id: button; ... 5 signal clicked(string value) 6 Text { 7 id: buttonText 8 anchors.centerIn: parent 9 text: "A" 10 } 11 MouseArea { 12 anchors.fill: parent 13 onClicked: button.clicked(buttonText.text) 14 } 15 onClicked: console.log(value) 16 } Listagem 9.7. Trecho de código da implementação de um teclado virtual com sinais e slots customizados. 9.3.2. Elementos de Interação O mais primário tipo de interação que um usuário espera ter com um software moderno se dá através de interações de mouse ou outro mecanismo apontador. Esse tipo de interação não limita-se somente aos cliques. Na verdade, existem vários outros eventos como botão pressionado, botão solto, movimento, etc. Com a crescente popularização das telas de toque em dispositivos móveis, como tablets e smartphones, outros tipos de interações (como gestos de flick, swipe, pinch e drag) vêm sendo absorvidas pelos usuários. Na linguagem QML alguns componentes já implementam algum tipo de interação, como é o caso dos elementos a seguir: ListView, GridView e PathView, que permitem fazer o scroll por seus elementos; TextInput e TextEdit, que possibilitam a inserção e edição de texto; Flickable e PinchArea, que lidam com a interação através de gestos de flick e pinch; Flipable e MouseArea, que tratam dos eventos de mouse; e finalmente Keys e FocusScope, referentes aos eventos de teclado. Nas seções a seguir, serão apresentados os tipos de interação mais básicos que podem ser implementados com a linguagem QML: eventos de clique (MouseArea) e entrada de texto (TextInput). 9.3.2.1. MouseArea O MouseArea trata-se de um elemento para o tratamento de eventos de clique. Ele possui todos os sinais e slots necessários para a construção de qualquer funcionalidade baseada em eventos de mouse. O MouseArea é um elemento invisível e sua utilização é, tipicamente, associada a um elemento visível. O elemento MouseArea permite capturar uma variedade de eventos através de seus slots. Os slots apresentados na Tabela 9.3 são os mais comuns em dispositivos móveis e podem ser amplamente utilizados no tratamento de eventos em aparelhos que possuem tela de toque. O exemplo exibido na Listagem 9.8 apresenta o código para o tratamento de eventos de clique (onClicked) e pressionamento e liberação (onPressAndHold) em 201 aplicativos que possuam tela de toque. O slot chamado para cada sinal escreve o tipo de evento no console do aplicativo. Os outros eventos apresentados na Tabela 9.3 são tratados de maneira semelhante. Slot onCanceled onClicked onDoubleClicked onPressAndHold onPressed onReleased Descrição Chamado quando um evento de mouse é cancelado, seja porque o evento não foi aceito ou porque foi interceptado por outro elemento. Chamado quando ocorre um clique, ou seja, um pressionamento seguido de liberação. Chamado quando ocorre um duplo clique, ou seja, um pressionamento seguido de uma liberação seguida de outro pressionamento. Esse slot é chamado quando existe um longo pressionamento (durante 800ms). O slot é chamado quando ocorre um pressionamento. Chamado quando ocorre uma liberação do ponteiro. Tabela 9.3. Principais slots associados aos eventos do elemento MouseArea. 1 import QtQuick 1.0 2 3 Item { 4 width: 360 5 height: 640 6 7 MouseArea { 8 anchors.fill: parent 9 onClicked: console.log("Evento: click") 10 onPressAndHold: { 11 console.log("Evento: Press and hold") 12 } 13 } 14 } Listagem 9.8. Tratamento de eventos de mouse com os slots onClicked e onPressAndHold. Outra funcionalidade muito interessante proporcionada pelo MouseArea é o drag. Através do drag, o usuário pode arrastar elementos na tela. Esse evento é muito utilizado em dock widgets4 e jogos. O exemplo exibido na Listagem 9.9 ilustra como o evento de drag pode ser alcançado facilmente usando o elemento MouseArea. É importante observar que o layout baseado em pontos de ancoragem pode comprometer o tratamento de eventos de drag. Dependendo do tipo de ancoragem utilizado, o objeto pode ficar totalmente ancorado, impedindo sua movimentação na tela do aplicativo. 4 Dock widgets é um painel de ícones que inicialmente aparece escondida nas bordas da tela de um aplicativo. Ao clicar e arrastar a dock widgets, o painel com os ícones é exibido, permitindo assim que esses ícones sejam clicados. 202 1 import QtQuick 1.0 2 3 Rectangle { 4 id: background 5 color: "lightblue" 6 width: 360 7 height: 640 8 ... 9 Rectangle { 10 id: dragable 11 color: "white" 12 width: 50 13 height: 50 14 ... 15 MouseArea { 16 anchors.fill: parent 17 drag.target: dragable 18 } 19 } 20 } Listagem 9.9. Exemplo de implementação de um evento de drag. 9.3.2.2. Entrada de Dados Na linguagem QML, os elementos disponíveis para realizar entrada de dados são TextInput e TextEdit. Ambos são utilizados para entrada de texto, porém o TextInput mostra uma única linha de texto editável não formatado, enquanto o TextEdit exibe várias linhas de texto editável e formatado. O texto digitado no elemento TextInput pode ser restringido através da utilização da propriedade validator ou inputMask. Utilizando as restrições de entrada, o elemento TextInput somente aceitará o texto que esteja em um estado aceitável ou, no mínimo, intermediário. Alguns validadores suportados são IntValidator, DoubleValidator e RegExpValidator. A Listagem 9.10 apresenta um trecho de código demonstrando a utilização da propriedade validator para a validação de texto. Nesse exemplo, a propriedade utiliza o tipo DoubleValidator com as seguintes propriedades: bottom (menor valor assumido pelo campo), top (maior valor assumido pelo campo) e decimals (número máximo de dígitos após o ponto decimal). O slot onTextChanged verifica o valor da propriedade acceptableInput e modifica a cor de fundo do campo de entrada para verde claro quando o texto for válido ou para vermelho claro, caso contrário. Outra possibilidade interessante do elemento TextInput é a utilização da propriedade echoMode, que especifica como o texto deve ser apresentado. Essa propriedade pode assumir as seguintes opções: TextInput.Normal (exibição normal), TextInput.Password (exibe asteriscos e é normalmente utilizado para exibição de senhas), TextInput.NoEcho (não exibe o texto) e TextInput.PasswordEchoOnEdit (com exceção do caractere atual, todos os outros são exibidos como asterisco). 203 1 import QtQuick 1.0 2 3 Rectangle { 4 id: background 5 width: 200 6 height: 70 7 TextInput { 8 focus: true; horizontalAlignment: TextInput.AlignHCenter 9 font {family: "Helvetica"; pointSize: 20} 10 anchors { fill: parent; topMargin: 20 } 11 validator: DoubleValidator { 12 bottom: 0.0; top: 99.99; decimals: 2 13 } 14 onTextChanged: { 15 if (acceptableInput) { 16 background.color = "lightgreen" 17 } else { 18 background.color = "indianred" 19 } 20 } 21 } 22 } Listagem 9.10. Exemplo do uso da propriedade validator no elemento TextInput. 9.4. Criando Animações com Estados e Transições As propriedades de objetos QML podem ser manipuladas através de código JavaScript, permitindo assim que animações sejam criadas de maneira imperativa. A criação de animações através de código JavaScript é muitas vezes uma tarefa complexa para os programadores. QML disponibiliza um conjunto de elementos baseados em estados e transições, que quando associados a outros elementos, facilitam a criação de animações nas aplicações. As seções a seguir apresentam os conceitos e elementos relacionados a estados, transições e animações em QML. 9.4.1. Estados em QML Em QML, estados são coleções de configurações definidas através do elemento State. Um objeto alcança um estado quando as mudanças realizadas nas suas propriedades representam um dos estados presentes em sua coleção de estados. A propriedade states armazena essa coleção (representado através de uma lista de objetos do tipo State) e representa todos os estados possíveis de um objeto. A simples declaração de um elemento automaticamente cria um estado padrão, chamado de estado base, definido como “” (string vazia). Esse é o estado em que um objeto se encontra quando não representa nenhum dos estados definidos em sua propriedade states. Outros estados podem ser declarados e a transição entre esses estados pode ser realizada facilmente modificando o valor da propriedade state (no singular), que armazena o estado atual de um objeto. O valor dessa propriedade pode ser modificado diretamente em um slot ou função escrita em JavaScript, como apresentado na Listagem 9.11. O elemento State possui as seguintes propriedades: changes (lista de mudanças 204 1 onClicked: { 2 if(obj.state == "clicado") { 3 obj.state = ""; 4 } 5 else { 6 obj.state = "clicado"; 7 } 8 } Listagem 9.11. Exemplo da alteração do estado de um objeto utilizando código JavaScript diretamente. a serem realizadas neste estado), extend (nome do estado pai), name (nome do estado) e when (condição para ativação do estado). A Listagem 9.12 apresenta um exemplo da utilização da propriedade when para a ativação do estado “clicado” do objeto rect. Nesse caso, a propriedade diz que esse estado só será ativado quando o objeto representado pelo MouseArea for clicado, ou seja, quando o sinal clicked do objeto mouse for emitido. Na linha 12 da Listagem 9.12 também é possível observar a utilização do elemento PropertyChanges. Esse elemento é utilizado para modificar as propriedades de um objeto específico identificado em target durante o tempo em que um estado estiver ativado. Então, nesse exemplo, a propriedade color do objeto rect (target) é alterada para “red” enquanto esse objeto estiver no estado “clicado”. 1 import QtQuick 1.0 2 3 Rectangle { 4 id: rect; color: "green"; width: 120; height: 120 5 MouseArea { id: mouse; anchors.fill: parent } 6 states: [ 7 State { 8 name: "pressed" 9 when: mouse.pressed 10 PropertyChanges { target: rect; color: "red" } 11 } 12 ] 13 } Listagem 9.12. Utilização da propriedade when do elemento State para modificação de estado de um objeto. 9.4.2. Animação com com elemento Behavior Em QML, a transição de um estado para outro ocorre de maneira brusca e como consequência os valores das propriedades também são alterados bruscamente. Porém, muitas vezes, o esperado é que a transição ocorra de maneira suave, ou seja, através de animações, que são criadas através da interpolação entre os valores inicial e final de uma propriedade. O elemento Behavior é utilizado com o propósito de se criar animações em QML. Esse elemento define uma animação padrão que deve ser utilizada sempre que uma de- 205 terminada propriedade de um objeto sofrer alterações. Uma boa prática de programação para esse elemento é utilizar estados bem definidos para realizar as transições, ou seja, evitar a utilização do estado base (“”). Essa prática é recomendada porque se uma animação for interrompida, o estado base assume os valores intermediários das propriedades e o comportamento final pode não ser o esperado. A Listagem 9.13 apresenta um exemplo da utilização do elemento Behavior. Nesse exemplo, foram declarados dois estados no objeto rect, denominados “EstadoIncial” e “EstadoFinal”. A propriedade x possui o valor 0 no primeiro estado e o valor 400 no segundo estado. A declaração Behavior on x é responsável por realizar a interpolação dos valores dessa propriedade entre os dois estados que foram declarados. A interpolação é do tipo NumberAnimation e deve ter uma duração de 200ms. Além do tipo NumberAnimation existem outras animações baseadas nos tipos de dados. Algumas das animações mais utilizadas são: ColorAnimation, para animar mudanças nos valores de cor; RotationAnimation, para animar rotações; e PropertyAnimation, para animar mudanças nos valores de propriedades. 1 import QtQuick 1.0 2 3 Rectangle { 4 width: 500; height: 100 5 MouseArea { id: mouse; anchors.fill: parent } 6 Rectangle { 7 id: rect; width: 100; height: 100; color: "blue" 8 Behavior on x { NumberAnimation{duration: 200} } 9 states: [ 10 State { 11 name: "EstadoInicial" 12 when: !mouse.pressed 13 PropertyChanges { target: rect; x: 0 } 14 }, 15 State { 16 name: "EstadoFinal" 17 when: mouse.pressed 18 PropertyChanges { target: rect; x: 400 } 19 } 20 ] 21 } 22 } Listagem 9.13. Exemplo de animação utilizando o elemento Behavior. Uma propriedade pode ter somente um elemento Behavior associado à ela. Para realizar múltiplas transições com diferentes propriedades é necessário utilizar os elementos ParallelAnimation ou SequentialAnimation, para animações paralelas ou sequenciais, respectivamente. 9.4.3. Transições entre estados Outra forma de se fazer animações em transições entre estados é utilizando o elemento Transition. Esse elemento define as animações que devem acontecer quando ocorrem 206 trocas de estados. As transições são atribuídas a um objeto através da propriedade transitions, que pode receber um único objeto ou uma lista de objetos do tipo Transition. O elemento Transition possui 4 propriedades: animation, que é um lista de animações que devem ser executadas durante a transição; from e to, indicam que a transição é aplicável somente quando ocorrer a mudança de estados especificada nessas propriedades; e reversible, que indica se a animação da transição deve ser automaticamente revertida quando as condições de ativação forem invertidas. Essa última propriedade evita a criação de uma nova transição para realizar a animação reversa quando as condições de ativação estiverem invertidas. As transições podem conter elementos de animação para interpolar as mudanças causadas por mudanças de estados nos valores das propriedades. Os elementos utilizados podem ser os mesmos utilizados com o elemento Behavior apresentado na Seção 9.4.2. Porém, se uma mudança de estado altera uma propriedade que foi declarada tanto em um Transition quanto em um Behavior, o comportamento definido no elemento Transition sobrepõe o elemento Behavior. A Listagem 9.14 apresenta um exemplo da utilização do elemento Transition para realizar animações. Nesse exemplo, o elemento define animações para as propriedades color e x do objeto obj utilizando os elementos de animação ColorAnimation e NumberAnimation, respectivamente. 1 import QtQuick 1.0 2 3 Rectangle { 4 width: 200; height: 100 5 Rectangle { id: obj; width: 100; height: 100; color: "red" } 6 MouseArea { id: mouse; anchors.fill: parent } 7 states: State { 8 name: "EstadoFinal"; 9 when: mouse.pressed 10 PropertyChanges { target: obj; x: 100; color: "green" } 11 } 12 transitions: Transition { 13 NumberAnimation { properties: "x"; duration: 200 } 14 ColorAnimation { duration: 200 } 15 } 16 } Listagem 9.14. Exemplo da utilização do elemento Transition para realizar animações. 9.5. Componentes QML Customizados A linguagem QML é fortemente baseada nos conceitos de componentização. Dessa maneira, qualquer trecho de código pode ser tornar um componente e a própria declaração de elementos pode ser vista como o uso de componentes pré-definidos. 9.5.1. Definição de Componentes Um componente QML é como uma caixa preta que interage com o mundo externo através de suas propriedades, sinais e funções, e geralmente é definido dentro de um arquivo 207 QML. Um ponto importante a ser considerado é que arquivos QML que definem componentes deve ter seus nomes iniciados com letra maiúscula. A Listagem 9.15 apresenta um exemplo de um componente Button, definido em um arquivo Button.qml, que emite sinais à medida que recebe cliques do usuário. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // Arquivo: Button.qml import QtQuick 1.0 Rectangle { id: button anchors.centerIn: parent width: 100; height: 100 radius: 50; color: ’red’ signal clicked MouseArea { anchors.fill: button onClicked: { button.clicked() } } } Listagem 9.15. Componente customizado criado com o nome de arquivo Button.qml. Componentes em QML também podem ser criados de uma maneira mais explícita usando o elemento Component, que permite uma definição inline, ou seja, dentro de um documento QML, ao invés de um arquivo separado. A vantagem do uso desse elemento é permitir reusar pequenas definições de componente dentro de um arquivo QML, ou para definir componentes que pertencem logicamente a um mesmo arquivo QML. Assim, o elemento Component gera componentes abstratos que servem como um modelo para reproduzir e renderizar cópias do componente definido. É importante esclarecer que o elemento Component apenas atua na definição de componentes e essa definição pode ser utilizada por outros elementos e métodos, tais como: • Loader. É um elemento usado para carregar componentes QML visuais dinamicamente. Através dele é possível especificar como fonte de referência tanto um arquivo QML, usando a propriedade source, quanto um objeto Component, este último utilizando a propriedade sourceComponent; • createObject. É um método pertencente aos objetos do tipo Component, que permite criar dinamicamente objetos a partir de suas definições de componente. Este método recebe como parâmetro a referência (id) do objeto que será o pai do novo objeto criado. A Listagem 9.16 apresenta um exemplo da definição de um componente como um círculo de cor verde com id igual a circleComponent. Nesse exemplo, o elemento Loader é declarado, especificando o objeto circleComponent como sendo o componente de referência, através da propriedade sourceComponent, e então um círculo verde é renderizado na tela do dispositivo na posição padrão (x = 0 e y = 0). 208 O mesmo exemplo mostra a declaração de um elemento MouseArea, onde é declarado um slot onMousePositionChanged que é chamado no momento em que ocorre uma mudança no ponto de clique na tela, ou seja, no momento em que a área de toque é arrastada. Nesse slot é listada uma sequência de comandos visando a criação de objetos dinamicamente segundo a definição feita no componente circleComponent. A cada vez que o slot onMousePositionChanged é chamado, o método createObject() de circleComponent cria um novo objeto do mesmo tipo dentro do objeto mainView. Quando ocorre a criação do círculo dentro do slot, algumas das suas propriedades são alteradas diretamente no objeto armazenado na variável JavaScript circle. Assim, o novo círculo passa a ter seu centro no posição atual do mouse, e sua cor passa a ser azul. 1 import QtQuick 1.0 2 3 Item { 4 id: mainView; width: 360; height: 640 5 Component { 6 id: circleComponent 7 Rectangle { 8 id: circle 9 width: 40; height: 40; radius: 40; color: "green" 10 } 11 } 12 Loader { id: pointer; sourceComponent: circleComponent} 13 MouseArea { 14 anchors.fill: mainView 15 onMousePositionChanged: { 16 var circle = circleComponent.createObject(mainView) 17 circle.x = mouseX - circle.width/2 18 circle.y = mouseY - circle.height/2 19 circle.color = ’blue’ 20 } 21 } 22 } Listagem 9.16. Definição e carregamento de um componente para desenhar um círculo. 9.5.2. Criação Dinâmica de Componentes com JavaScript A utilização de código JavaScript é outra maneira de especificar os componentes. Essa abordagem bastante usada no desenvolvidos de jogos em QML, onde é preciso criar um número grande de objetos referentes à cena de jogo e fazer o gerenciamento destes à medida em que ocorrem alterações no estado do jogo. Para definir componentes dentro de um trecho de código JavaScript é utilizada a função createComponent() pertencente a variável global Qt. Esta função requisita como parâmetro a url do arquivo QML onde o componente foi declarado e retorna um objeto do tipo Component. A criação de objetos a partir de componentes que foram definidos é possível por meio do método createObject(). Esta função pertence a objetos do tipo Component, e recebe como parâmetros a referência do item que será o pai do novo objeto e um valor do tipo dicionário, uma série de pares chave-valor com as configurações das propriedades. O 209 retorno desta função é um objeto definido da forma como é declarado no Component e com suas propriedades alteradas segundos os valores passados como argumento. Assim é possível criar diferentes objetos de uma definição de componente e customizar livremente seus valores de propriedades. O código apresentado na Listagem 9.17 demonstra a criação de um componente do tipo Button, mostrando que o objeto criado segue a mesma interface definida na declaração inicial do componente. Um objeto do tipo Button é criado de maneira dinâmica através do método createComponent() e é armazenado na variável buttonComponent. A variável buttonObj, por sua vez, recebe um objeto Button após a chamada do método createObject() do objeto buttonComponent, conforme apresentado na linha 14. 1 import QtQuick 1.0 2 3 Rectangle { 4 id: mainView 5 width: 360 6 height: 640 7 8 function randomColor() { 9 return Qt.rgba(Math.random(), 10 Math.random(), 11 Math.random(), 12 0.5 + (Math.random())/2) 13 } 14 15 Component.onCompleted: { 16 var buttonComponent = Qt.createComponent("Button.qml"); 17 var buttonObj = buttonComponent.createObject( 18 mainView, 19 {"color" : randomColor()}); 20 buttonObj.clicked.connect(function() { 21 color = randomColor() 22 }); 23 } 24 } Listagem 9.17. Criação de componentes utilizando código JavaScript. Tanto as propriedades quando os sinais fazem parte da interface de um componente QML, assim todos os objetos criados a partir de um componente possuem as mesmas definições de propriedades e sinais. Logo, ainda na Listagem 9.17, é possível conectar um slot ao sinal clicked do objeto buttonObj do tipo Button (linha 16), onde tal sinal foi definido. O papel do slot conectado ao sinal clicked é atribuir um valor de cor aleatório a propriedade color à tela de fundo do aplicativo. É interessante perceber que apesar de estar dentro de um slot do objeto do tipo Button, o escopo de propriedade pertence a mainView, dado que o Component.onCompleted é declarado em mainView. Decorre então que a propriedade color refere-se ao objeto mainView e não ao próprio Button. Como resultado, tem-se um botão centralizado no objeto pai, mainView, e que ao ser pressionado altera a cor de fundo da tela. 210 9.5.2.1. Criação de Objetos a partir de uma String Uma maneira interessante de criação de objetos QML em tempo de execução é a partir de uma string utilizando código JavaScript. Esses objetos são criados através do método createQmlObject() disponível na variável global Qt, conforme apresentado na Listagem 9.18. 1 var newObj = Qt.createQmlObject( 2 ’import QtQuick 1.0;’ + 3 ’Rectangle { color: "red"; width: 20; height: 20}’, 4 parentItem, 5 "dynamicSnippet1"); Listagem 9.18. Novo objeto criado a partir de uma string utilizando código JavaScript O primeiro argumento do método define o componente, utilizando o código com os mesmos elementos de declarações em arquivos QML. Se o código definido na string possuir algum caminho de arquivo, este deve ser relativo ao item pai do objeto criado. O segundo argumento é o item pai do objeto a ser criado. Por fim, o terceiro argumento é apenas um nome de diretório associado ao novo objeto, caso seja necessário reportar erros. 9.5.2.2. Deletando Objetos Dinamicamente De maneira geral, uma grande quantidade de itens criados dinamicamente poder trazer impactos negativos ao desempenho de um aplicativo. Assim, é altamente recomendável que se tenha um bom gerenciamento dos objetos criados. Deletar os objetos criados dinamicamente à medida que se tornarem desnecessários pode beneficiar o desempenho como um todo. É importante pontuar a diferença entre itens criados dinamicamente por funções JavaScript e aqueles criados por elementos QML, tais como o Loader. Estes últimos possuem em sua implementação o pressuposto de que seus itens gerados não serão deletados durante o tempo de vida do componente que os engloba. Assim deve ser evitado deletar qualquer item que não tenha sido diretamente criado por funções JavaScript. Todo item possui um método de destruição chamado destroy(). Este método possui um argumento opcional onde pode ser passado o tempo em milissegundos antes que o objeto seja destruído. A Listagem 9.19 apresenta um exemplo de criação dinâmica através de uma string e destruição do objeto criado. O trecho de código definido no slot onCompleted é executado no momento em que o item pai mainView é carregado e renderizado na tela. Então, um novo objeto é criado (um retângulo vermelho de bordas arredondadas) cujo pai é definido como sendo o mainView e seu caminho para relatórios de erros como “snippetCode1”. A variável newObject passa a armazenar o objeto retângulo, enquanto na linha seguinte uma variável newMouseArea recebe uma instância do tipo MouseArea. Ao final do exemplo, um slot é conectado ao sinal clicked do MouseArea, onde o primeiro objeto tem sua opacidade modificada para zero e então seu método de destruição é chamado. 211 1 import QtQuick 1.0 2 3 Rectangle { 4 id: mainView; width: 640; height: 360 5 Component.onCompleted: { 6 var newObject = Qt.createQmlObject( 7 " import QtQuick 1.0; " + 8 " Rectangle { anchors.centerIn: parent; " + 9 " color: ’red’; width: 200; height: 200; " + 10 " radius: 20}", 11 mainView, "snippetCode1"); 12 var newMouseArea = Qt.createQmlObject( 13 " import QtQuick 1.0; " + 14 " MouseArea { anchors.fill: parent;}", 15 newObject, "mouseArea1"); 16 newMouseArea.clicked.connect(function() { 17 newObject.opacity = 0; 18 newObject.destroy(1000) 19 }); 20 } 21 } Listagem 9.19. Exemplo da utilização do método destroy() para a destruição de objetos criados dinamicamente. 9.5.3. Definição de Componentes utilizando Qt/C++ A grande diversidade de elementos QML permitem criar variados tipos de interface de maneira declarativa. Entretanto para aplicações que exijam renderizações de forma geométricas complexas ou possuam comportamento funcional acima do disponibilizado pelos elementos QML padrões, existe a possibilidade de estender novas funcionalidade através de classes C++. Dessa maneira, elementos QML com alto grau de customização podem ser definidos em uma classe C++ que herde o QDeclarativeItem, a classe pai de todos os itens declarativos. Feita a sobrecarga do método de paint do QDeclarativeItem, é possível criar um componente QML utilizando todas as potencialidades oferecidas pelo framework Qt. Na Listagem 9.20, um triângulo é desenhado através de um caminho de pontos pré-definidos, um QPaintPath. O painter é configurado para realizar a renderização do caminho de pontos definido, através do método drawPath do QPainter. Os métodos setPen e setBrush apenas selecionam a cor preta para os traços do desenho e uma cor pré-definida m_color para o preenchimento da figura. 9.5.3.1. Criação de Novos Tipos Uma vez que um novo elemento QML é criado em C++, é necessário registrá-lo para uso dentro de arquivos QML, ou seja, é preciso certificar que a máquina de execução QML consiga compreender o significado dos novos símbolos definidos. Especificada no cabeçalho qdeclarativeengine.h, a função qmlRegisterType re- 212 1 class MyItem : public QDeclarativeItem 2 { 3 ... 4 void paint(QPainter *painter, 5 const QStyleOptionGraphicsItem *, 6 QWidget *) { 7 QPainterPath path; 8 path.moveTo(boundingRect().width()/2,0); 9 path.lineTo(boundingRect().width(),boundingRect().height()); 10 path.lineTo(0,boundingRect().height()); 11 path.lineTo(boundingRect().width()/2,0); 12 13 painter->setPen(QColor("black")); 14 painter->setBrush(QBrush(m_color)); 15 painter->drawPath(path); 16 } 17 }; Listagem 9.20. Definição de um novo componente utilizando código Qt/C++. gistra os tipos C++ no sistema QML, através da especificação de um URI (Uniform Resource Identifier) da biblioteca, um número de versão e o nome do componente. Abaixo segue um exemplo de seu uso dentro do código C++, onde o elemento MyItem é declarado através do registro da classe MyItem dentro da biblioteca MyComponents sob a versão 1.0. qmlRegisterType<MyItem>("MyComponents", 1, 0, "MyItem"); O item customizado pode ser facilmente importado para o código QML, sendo que para isso é necessário conhecer o nome e versão da biblioteca onde está agrupado, assim como seu nome de componente. Uma vez importado, o novo elemento segue exatamente as mesmas regras de qualquer outro elemento do tipo Item, possuindo assim também suas propriedades, tais como x, y, width, height e outras. A Listagem 9.21 apresenta um exemplo da utilização do elemento do novo tipo (MyItem) criado a partir de código C++. 1 import MyComponents 1.0 2 3 Rectangle { 4 MyItem { 5 id: myItem 6 } 7 } Listagem 9.21. Declaração do novo elemento criado a partir de código C++. 9.6. Plataformas de Desenvolvimento No contexto de dispositivos móveis, Symbian e Maemo/MeeGo são algumas plataformas que oferecem suporte ao desenvolvimento de aplicações em Qt. No desenvolvimento em Qt, geralmente não são necessárias muitas modificações no código para que um projeto 213 existente funcione em uma nova plataforma. Dessa maneira, na maioria dos casos o processo de compilar o código para essa nova plataforma e executar a aplicação funciona sem maiores problemas. Porém, algumas vezes é necessário realizar configurações de projeto no arquivo .pro para que o aplicativo tenha acesso aos recursos do dispositivo. A Listagem 9.22 apresenta algumas configurações específicas da plataforma Symbian utilizadas no desenvolvimento de aplicações. As duas instruções apresentadas nessa listagem são: TARGET.CAPABILITIES, que define privilégios extras para que a aplicação tenha acesso aos recursos do dispositivo, como é o caso dos serviços de rede com NetworkServices; e TARGET.EPOCHEAPSIZE, que define a quantidade mínima e máxima de memória disponível para a aplicação. 1 symbian: { 2 ... 3 TARGET.CAPABILITY += NetworkServices 4 TARGET.EPOCHEAPSIZE = 0x020000 0x4000000 5 ... 6 } Listagem 9.22. Configurações específicas da plataforma Symbian para desenvolvimento Qt. Na plataforma Maemo/MeeGo, a IDE Qt Creator cria os projetos com todos os recursos necessários para o desenvolvimento. No caso da plataforma Android, ainda não existe um port oficial, mas já existem algumas iniciativas para utilizar desenvolver com o Framework Qt nessa plataforma. O Necessitas [3] é um projeto que tem o objetivo de prover o port do Qt para a plataforma Android e integração com o Qt Creator. O site do projeto fornece as informações necessárias para começar a utilizar o Necessitas SDK. Quanto ao processo de compilação das aplicações para instalação no dispositivo móvel, o QtSDK fornece o módulo Remote Compiler. Esse módulo foi criado para contornar a ausência de soluções que permitissem a compilação dos binários Symbian nas plataformas Linux e Apple Mac. Esse recurso permite a compilação para as plataformas Symbian S60, Symbianˆ1, Symbianˆ3, Maemo 5 e MeeGo 1.2 Harmattan, oferecendo suporte para a versão 4.6 do Qt, incluindo o Qt Quick a partir da versão 4.7 do Qt. 9.7. Considerações Finais Os elementos e características da linguagem QML apresentados neste capítulo possibilitam o desenvolvimento cross-platform de UIs de alto nível de maneira rápida e intuitiva. Para reforçar a importância desse kit de desenvolvimento, o framework Qt vem sendo utilizado por grandes empresas em projetos como Google Earth, Skype, KDE e Opera [7]. O Ambiente declarativo Qt Quick, através da linguagem QML, surge como uma ferramenta promissora para a criação de UIs onde a rapidez de desenvolvimento e a experiência do usuário são cruciais para as aplicações. Além disso, novos dispositivos móveis recentemente lançados com o sistema operacional MeeGo possuem o Qt como kit de desenvolvimento padrão. Em resumo, neste capítulo foi apresentado como utilizar a linguagem QML no ambiente declarativo Qt Quick para a criação de interfaces para dispositivos móveis. Através de elementos básicos como Rectangle, Item, MouseArea, Text e Image foi apresen- 214 tado como especificar UIs pelo seu conteúdo, e não por meio de comandos imperativos em Qt/C++ na especificação dos métodos. Além disso, outras características importantes da linguagem QML (como a criação de animações e o tratamento de eventos) foram apresentadas, possibilitando a criação de UIs modernas e flexíveis. 9.8. Agradecimentos A escrita deste capítulo foi apoiada pelo CETELI (Centro de Pesquisa e Desenvolvimento em Tecnologia Eletrônica e da Informação) e pelo INdT (Instituto Nokia de Tecnologia). Referências [1] Goderis, S. (2005) “High-level declarative user interfaces”. In: Companion to the 20th annual ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA ’05). New York, NY, USA, ACM, p. 236-237. [2] QUIt Coding, Crash course to Qt Quick Game Programming, disponível em: http://quitcoding.com/download/Qt_Quick_Game_Programming_1_0.pdf, acessado em outubro de 2011. [3] Necessitas, disponível em: http://sourceforge.net/p/necessitas/home/necessitas/, acessado em outubro de 2011. [4] Nokia, QML Elements, disponível em: http://doc.qt.nokia.com/4.7snapshot/qdeclarativeelements.html, acessado em outubro de 2011. [5] Nokia, Qt - Cross-platform application and UI framework, disponível em: http://qt.nokia.com/, acessado em outubro de 2011. [6] Nokia, Qt 4.7: QML Viewer, disponível em: http://doc.qt.nokia.com/4.7snapshot/qmlviewer.html, acessado em outubro de 2011. [7] Nokia, Qt in Use, disponível em: http://qt.nokia.com/qt-in-use/, acessado em outubro de 2011. [8] Nokia, Qt Quick, http://qt.nokia.com/qtquick/, acessado em outubro de 2011. [9] Vision Mobile, Developer Economics 2011, Disponível http://www.visionmobile.com/devecon.php, acessado em outubro de 2011. em: [10] Vision Mobile, Mobile Economics Developer 2010 and Beyond, Disponível em: http://www.visionmobile.com/rsc/researchreports/Mobile%20Developer %20Economics%202010%20Report%20FINAL.pdf, acessado em outubro de 2011. [11] W3C Recommendation, Basic Data Types and Interfaces, disponível em: http://www.w3.org/TR/SVG/types.html#ColorKeywords, acessado em outubro de 2011. [12] Zucker, D. e Rischpater, R., Beginning Nokia Apps Development: Qt and HTML5 for Symbian and MeeGo, Apress Series. Apress, 2010. 215 Capítulo 10 Técnicas de Processamento de Imagens em Diagnóstico Auxiliado por Computador Rodrigo M. S. Veras, Iális C. Paula Jr. e Fátima N. S. Medeiros Abstract Computer-aided diagnosis systems (CAD) are defined to improve the accuracy of diagnosis by health experts, as well as consistency of interpretation of imaging tests by using the computer’s response as a reference. Large collections of medical imaging have brought the necessity of using computational techniques to process analyze and retrieve pictorial information related to them. Therefore, computer-aided diagnosis has been attracting great interest. The aim of this chapter is to introduce the main concepts related to CAD systems: kinds of CAD and major areas of activity, techniques of Digital Image Processing, Pattern Recognition algorithms and practical applications. Resumo Sistemas de Diagnóstico Auxiliado por Computador (CAD - Computer-aided diagnosis) são definidos para aprimorar a acurácia do diagnóstico realizado por especialistas de saúde, assim como a consistência da interpretação de exames de imagens, mediante o uso da resposta do computador como referência. Grandes coleções de imagens médicas trouxeram consigo a necessidade de utilizar técnicas computacionais para processar, analisar e recuperar a informação pictórica relacionada às mesmas. Dessa forma, o diagnóstico auxiliado por computador vem atraindo grande interesse. O objetivo deste capítulo é apresentar os principais conceitos ligados aos sistemas CAD: tipos de CAD e principais áreas de atuação, técnicas de Processamento Digital de Imagens, algoritmos de Reconhecimento de Padrões e aplicações práticas. 10.1. Introdução A demanda crescente dos hospitais por um diagnóstico de exames baseados em imagens de forma rápida e precisa, somada aos avanços computacionais e de processamento de 216 imagens, fizeram surgir novas frentes de pesquisa relacionadas ao diagnóstico médico auxiliado por computador [Marques 2001]. Diagnóstico auxiliado por computador, também conhecido pela sigla CAD (Computer-Aided Diagnosis) é definido como um diagnóstico realizado pelo especialista, utilizando o resultado de análises quantitativas automatizadas de imagens como auxílio para tomada de decisões diagnósticas. A finalidade do diagnóstico auxiliado por computador é melhorar a acuidade do diagnóstico, assim como a consistência da interpretação da imagem, mediante o uso da resposta do computador como referência. A ideia do CAD pode ser aplicada em todas as modalidades de obtenção de imagem, incluindo a radiografia convencional, tomografia computadorizada, ressonância magnética, ultra-sonografia e medicina nuclear. Podem-se desenvolver sistemas CAD para todos os tipos de exame de todas as partes do corpo, incluindo, o crânio, tórax, abdome, estrutura óssea, sistema vascular e outros. O objetivo deste capítulo é apresentar os principais conceitos ligados aos sistemas CAD: tipos de CAD e principais áreas de atuação, técnicas de Processamento Digital de Imagens, algoritmos de Reconhecimento de Padrões e aplicações práticas. 10.2. Diagnóstico Auxiliado por Computador Diagnóstico auxiliado por computador pode ser definido como um diagnóstico feito por um médico que utiliza o resultado de análises quantitativas automatizadas de imagens como uma “segunda opinião” para a tomada de decisões diagnósticas [Doi 2007]. É importante ressaltar que o computador é utilizado somente como uma ferramenta para obtenção de informação adicional, sendo o diagnóstico final sempre feito pelo especialista, o que diferencia claramente o conceito básico de CAD do conceito de “diagnóstico automatizado”, que foi um conceito proposto e estudado nas décadas de 60 e 70 [Doi 2007]. A finalidade do CAD é melhorar a acurácia do diagnóstico, assim como a consistência da interpretação da imagem, mediante o uso da resposta do computador como referência. A resposta do computador pode ser útil, uma vez que o diagnóstico do especialista é baseado em avaliação subjetiva, estando sujeito a variações intra e interpessoais, bem como perda de informação devido à baixa qualidade da imagem, sobreposição de estruturas, fadiga visual ou distração. Além disso, foi demonstrado que uma dupla leitura (por dois especialistas) pode aumentar a sensibilidade do diagnóstico[Thurfjell et al. 1994]. A proposta do CAD é funcionar como um segundo especialista. Basicamente, existem dois tipos de aplicações de sistemas CAD. Um é o auxílio à detecção de lesões, a partir da localização de padrões anormais através da varredura da imagem pelo computador (por exemplo, agrupamentos de microcalcificações em imagens mamográficas ou nódulos pulmonares em imagens de tórax). O outro é o auxílio ao diagnóstico, através da quantificação de características da imagem e sua classificação como correspondendo a padrões normais ou anormais (por exemplo, a associação da quantidade e forma das microcalcificações presentes em um agrupamento com a malignidade ou não do tumor, ou a associação da textura dos pulmões com lesões intersticiais em imagens de tórax). Em geral, os sistemas CAD utilizam-se de técnicas provenientes de duas áreas do conhecimento: visão computacional, que envolve o processamento de imagem para realce, segmentação e extração de atributos, e inteligência artificial, que inclui métodos 217 para seleção de atributos e reconhecimento de padrões. 10.2.1. Auxílio à Detecção A detecção automatizada de lesões envolve a localização pelo computador de regiões contendo padrões suspeitos, porém com a classificação da lesão sendo feita exclusivamente pelo especialista. Sistemas para auxílio à detecção têm sido desenvolvidos, principalmente, para imagens radiológicas de tórax e de mama. 10.2.2. Auxílio ao Diagnóstico Uma vez que um padrão suspeito foi detectado, cabe ao especialista decidir o encaminhamento do caso, ou seja, se é necessária a realização de algum outro exame ou de uma biópsia, por exemplo. Ou então, se um simples acompanhamento é suficiente e, neste caso, qual deve ser o intervalo até o próximo exame. Sistemas de análise computadorizada estão sendo desenvolvidos para auxiliar a distinção entre lesões e não-lesões e aumentar a sensibilidade e especificidade do diagnóstico. Estes sistemas utilizam atributos extraídos e quantificados de forma automatizada e também por especialistas. A vantagem da extração automatizada é a objetividade e reprodutibilidade da medida dos atributos escolhidos. Por outro lado, os especialistas utilizam uma grande quantidade de atributos, os quais são extraídos e interpretados simultânea e instantaneamente. No caso de exames relacionados a câncer, um dos objetivos dos sistemas de auxílio à classificação é a redução do número de casos benignos encaminhados para biópsia. Porém, como o “custo” da perda de uma lesão maligna é muito maior que o de uma classificação errônea de um caso benigno, os sistemas de auxílio ao diagnóstico devem ser desenvolvidos com o propósito de aumentar a especificidade, porém sem reduzir a sensibilidade. 10.2.3. Estratégias dos Sistemas CAD Em [Hong Shao 2004] são propostas estratégias para diagnóstico de imagens, particularmente, aplicados à análise de patologias nos tecidos do cérebro (edema, tumor, etc.), tais como: 1. Análise comparativa de imagens do mesmo paciente, obtidas pelo mesmo equipamento, com os mesmos parâmetros de escaneamento, mas em diferentes instantes de tempo 2. Análise comparativa de imagens do mesmo paciente, obtidas pelo mesmo equipamento, no mesmo instante, mas com parâmetros de escaneamento diferentes. Este tipo de análise é denominado análise multi-espectral. 3. Análise comparativa de imagens do mesmo paciente, mas obtidas através de diferentes tecnologias de escaneamento, tais como, tomografia computadorizada, ressonância magnética, PET e SPECT. 4. Análise comparativa da imagem do paciente com um padrão obtido de pessoas sau- 218 dáveis, denominado atlas anatômico. Na prática, esta técnica não mostrou muita eficiência dado sua complexidade e dificuldade de estabelecer um padrão anatômico. Ainda como técnica de análise comparativa, pode-se acrescentar a técnica de comparação de duas imagens do mesmo paciente, mas obtida em diferentes partes do corpo. Essa a metodologia desenvolvida para detecção de nódulos em mamogramas, por exemplo, a qual é baseada na simetria de arquitetura entre as mamas esquerda e direita, com as assimetrias indicando possíveis nódulos, conforme descrito em [F.F. et al. 1991]. 10.3. Conhecimentos Envolvidos Em geral, os sistemas CAD utilizam-se de técnicas provenientes de duas áreas do conhecimento, visando a extração e quantificação de atributos de uma imagem em formato digital. Neste capítulo direcionamos este estudo com base nas seguintes técnicas: 1. Processamento de Imagens: envolve o processamento de imagem para realce, segmentação e extração de atributos. 2. Reconhecimento de Padrões: inclui métodos para seleção de atributos, estatística e reconhecimento de padrões. Utilizam-se técnicas de processamento de imagens para realce e segmentação das lesões, conforme o tipo do exame. Por exemplo, utilizam-se propriedades de descontinuidade dos níveis de cinza, detecção de contorno, bordas ou segmentação para separação de regiões que apresentem determinada característica. Após o realce e segmentação, segue o processamento envolvendo a quantificação de atributos da imagem, como, tamanho, contraste e forma dos seus objetos constituintes. A descrição dos atributos da imagem relaciona as características reconhecidas subjetivamente pelos especialistas. De um modo geral, os atributos são quantificados a partir de propriedades métricas, topológicas e de textura dos objetos. Após a extração e quantificação dos atributos, utiliza-se o reconhecimento de padrões para distinção entre padrões normais e anormais. Esta área do conhecimento abrange técnicas de distribuições de probabilidade de classe, técnicas de análise de discriminante, métodos estatísticos e redes neurais. 10.3.1. Processamento Digital de Imagens O processamento de imagens abrange uma ampla escala de software, hardware e fundamentos teóricos [Gonzalez e Woods 2007]. Entre estes fundamentos, discutiremos nesta seção técnicas importantes na caracterização e representação das informações relevantes de uma imagem ao usuário de sistema CAD. Esta subseção descreve algumas funções de processamento digital de imagens abordadas em sistema CAD. Inicialmente discute-se sobre o processo de aquisição de imagens. A subseção 10.3.1.2 descreve estratégias para adequar a imagem ao processamento em que ela será submetida. Técnicas para separar as regiões de interesse de uma 219 imagem são apresentadas na subseção 10.3.1.3. A subseção 10.3.1.4 expõe métodos para representação e descrição de formas. 10.3.1.1. Aquisição de Imagens Considera-se aquisição de uma imagem o processo de conversão de uma cena real tridimensional em uma imagem digital produzida por um ou vários sensores. A aquisição de imagens requer um sensor para imageamento e capacidade de digitalizar o sinal produzido pelo sensor [Burger e Burge 2009]. Este pode ser caracterizado por um dispositivo físico que seja sensível a uma banda do espectro eletromagnético (como raios-X, ultravioleta, visível ou banda infravermelha), gerando um sinal elétrico proporcional ao nível de energia recebida. Por fim, converte-se a saída elétrica deste sensor para a forma digital num outro dispositivo, conhecido como digitalizador [Azevedo et al. 2007]. Dependendo do tipo do sensor, o resultado pode variar entre uma imagem bidimensional, uma cena tridimensional ou ainda uma sequência de imagens. f (x, y) ≈ f (0, 0) f (1, 0) .. . f (0, 1) f (1, 1) ··· ··· f (0, M − 1) f (1, M − 1) .. . f (N − 1, 0) f (N − 1, 1) · · · f (N − 1, M − 1) (1) O termo imagem refere-se a uma função de intensidade luminosa bidimensional, denotada por f (x, y). O valor ou amplitude de f nas coordenadas espaciais (x, y) dá a intensidade (brilho) da imagem naquele ponto. Para adequar-se a um sistema computacional, a função f (x, y) precisa ser digitalizada tanto espacialmente quanto na amplitude. A digitalização de suas coordenadas espaciais é denominada amostragem da imagem e a digitalização da amplitude é chamada quantização em níveis de cinza. Supondo que uma imagem contínua f (x, y) é aproximada por amostras igualmente espaçadas, arranjadas na forma de uma matriz NxM (Equação (1)) com cada elemento apresentando uma quantidade discreta. O lado direito desta equação traz a representação de uma imagem digital [Gomes e Velho 1994]. Cada elemento desta matriz é definido como um elemento da imagem ou pixel (abreviação do termo picture element, elemento da figura) [Azevedo et al. 2007]. Os valores dos pixels podem indicar, além da intensidade da luz, uma ou várias faixas de cor (gerando imagens em tons de cinza ou coloridas), mas também podem indicar valores físicos como profundidade e absorção ou reflexão das ondas eletromagnéticas. Considerando a Equação (1) uma aproximação de uma imagem contínua, devese avaliar quantas amostras e níveis de cinza são suficientes para esta aproximação. A resolução desta imagem dependerá destes dois parâmetros. Maiores valores nestes índices indicam maior aproximação da matriz digitalizada à imagem original. Essa relação pode ser observada na Figura 10.1(b), que se apresenta diferente da Figura 10.1(a), após ser reamostrada no plano digital e com uma limitação nos níveis de cinza visíveis. Diferentes equipamentos podem ser utilizados para a aquisição de imagens, como câmeras CCD (do inglês Charge-Coupled Device, dispositivo de carga acopladade para vídeo e fotografia), sistema infravermelho, aparelhos para radiologia que utilizam sinais recebidos de raio-X, 220 tomografia computadorizada, medicina nuclear, ultra-sonografia e ressonância magnética nuclear. (a) (b) Figura 10.1. Efeito da discretização da imagem: exemplo de (a) imagem contínua e (b) resultado da amostragem e quantização da mesma. Adaptado de Gonzalez & Woods (2007). Para a realização de testes em um sistema CAD, é possível processar imagens armazenadas em repositórios disponíveis na web. Alguns destes podem ser abertos e oferecem a caracterização das imagens oferecidas. Como exemplo, num sistema CAD que atue com imagens de mamografia, há bases de dados que disponibilizam as imagens de mamografia com a indicação de quais destas correspondem a exames de pacientes doentes ou não. 10.3.1.2. Técnicas de Pré-Processamento Em muitas aplicações, os métodos de processamento de imagens requerem ajustes na imagem de entrada para extrair suas informações de maneira satisfatória. Somente após estes ajustes, a imagem é processada e se torna apta para análise na aplicação. Essa etapa de pré-processamento pode ser definida por diversas técnicas como: remapeamento (para assegurar o sistema de coordenadas), redução de ruídos (para assegurar que as informações são verdadeiras) e aumento de contraste (para assegurar que as informações relevantes serão detectadas), entre outras. Algumas distorções estão presentes em uma imagem e afetam a percepção da real informação presente na mesma. Uma distorção crítica é a presença de ruído na imagem. Esse fator implica em nova informação na imagem, diferente do seu comportamento real. Normalmente, os ruídos são definidos por uma distribuição estatística, como o modelo gaussiano, speckle e de poisson [Marques et al. 2004, Gonzalez e Woods 2007]. Para eliminação do ruído em uma imagem, faz-se necessário aplicar técnicas de realce com processamento por máscara. Isso consiste em definir uma nova imagem, de dimensão menor do que aquela a ser processada, e aplicar valores a cada um dos elementos dessa nova imagem que será chamada de máscara (ou janela). Essa janela percorre toda a imagem, e faz uma avaliação pixel a pixel de acordo com sua dimensão. A distribuição de valores nessa máscara define o tipo de filtragem a ser executada: passa-alta (preserva as informações de alta-frequência, como bordas e ruído, e elimina as demais) ou passa-baixa (preserva as informações de baixa-frequência, como regiões homogêneas 221 Figura 10.2. Exemplos de filtragem. Ao lado esquerdo estão dispostas imagens com resultados de tentativa de remoção de ruído com filtro passa-baixa. No lado direito, um exemplo de detecção de borda com um filtro passa-alta. em suas intensidades, e elimina o restante). Na Figura 10.2 estão dispostos exemplos de filtros passa-baixa e passa-alta com suas respectivas máscaras. Cada máscara apresenta um efeito diferenciado no resultado da filtragem [Paula 2009, Velho et al. 2009]. (a) Imagem original. (b) Fatiamento da imagem em oito pseudo-cores. Figura 10.3. Exemplo de realce por uso de fatiamento de intensidades. Adaptado de Gonzalez & Woods (2007). Existem ainda outras técnicas que não realizam um processamento especial como ocorre na filtragem, mas que permitem ajustar a imagem em seu pré-processamento. Uma dessas técnicas é o fatiamento de intensidades, que permite criar uma nova codificação de cores na imagem para melhor interpretá-la. Esse método é muito utilizado em aplicações médicas e meteorológicas, em que as intensidades dos pixels são categorizadas em intervalos específicos. Cada interstício de intensidade assume uma cor na nova representação da imagem. Um exemplo desse processo está na Figura 10.3 que apresenta uma imagem monocromática do Fantasma de tireóide de Picker [Gonzalez e Woods 2007]. Com o fatiamento de intensidades, a imagem é redefinida com oito intervalos de intensidades onde cada região assume uma pseudo-cor específica. 222 Há ainda técnicas de pré-processamento identificadas com a avaliação da estatística inerente à imagem para melhorar a sua aparência. Um exemplo muito prático consiste na equalização do histograma da imagem. O histograma de uma imagem digital corresponde a uma estimativa da probabilidade de ocorrência dos seus níveis de cinza. Este desenvolvimento produz uma imagem cujos níveis de cinza possuem uma densidade uniforme. Isso implica num aumento da escala dinâmica dos pixels, o que pode acarretar em um efeito relevante na visualização da imagem [Gonzalez e Woods 2007]. A Figura 10.4 exibe amostras em condições diferentes de contraste e suas imagens de resultado após a equalização dos seus respectivos histogramas. É possível perceber a mudança no contraste destas imagens. 10.3.1.3. Segmentação O processo de segmentação visa à separação dos pixels pertencentes a cada objeto presente na imagem. Isso implica em separação de regiões dentro da imagem analisada. 6000 5000 7000 7000 6000 6000 3500 5000 5000 3000 4000 4000 3000 3000 2000 2000 1000 1000 4000 4000 2500 3000 2000 1500 2000 1000 1000 0 0 0 50 100 150 200 250 500 0 0 50 100 150 200 250 0 0 50 100 150 200 250 0 50 100 150 200 250 0 50 100 150 200 250 7000 7000 5000 6000 6000 5000 5000 4000 4000 3000 3000 2000 2000 1000 1000 0 0 4000 3000 2000 0 50 100 150 200 250 1000 0 0 50 100 150 200 250 Figura 10.4. Realce na imagem através da equalização do histograma. Cada coluna apresenta quatro diferentes amostras de imagens: escura, clara, com baixo contraste e com alto contraste. Analisando cada amostra a partir da imagem de topo até a de base, tem-se: imagem original, histograma, efeito do realce e histograma equalizado. 223 Exemplos desse processo incluem a seleção de regiões de interesse específicos e segmentação de uma ou mais regiões que contém um objeto de interesse [Castleman 1996]. A importância do realce em uma imagem é percebida no momento em que o processo de segmentação é efetuado. O método de segmentação precisa que a imagem tratada esteja livre de distorções para atingir seu objetivo. A técnica mais simples é a limiarização (ver Figura 10.5), que consiste em definir um valor de intensidade como limiar e modificar todos os pixels da imagem com base no mesmo. Isso ocorre a partir de um comparativo, no qual o pixel com intensidade menor ou igual ao limiar recebe valor 0 (zero). Em caso contrário, a intensidade do pixel considerado receberá valor igual a 255. Portanto, duas regiões são separadas na imagem. 3500 3000 2500 2000 1500 1000 500 0 0 (a) 50 100 150 200 250 (b) (c) (d) Figura 10.5. Exemplo de segmentação por limiarização: (a) imagem original e (b) seu respectivo histograma. Aplica-se o processo a partir do valor r = 125 aplicado na (c) função de limiarização s = T (r) e obtém-se o (d) resultado da segmentação. Adaptado de Gonzalez & Woods (2007). 10.3.1.4. Representação e Descrição de Formas Com os agrupamentos obtidos a partir da segmentação da imagem, torna-se possível representar e descrever as regiões identificadas. Inicialmente, a representação de uma região pode ser caracterizada como baseada no contorno (fronteira), ou em termos de suas características internas (pixels que compõem a região) [Paula et al. 2011]. Os dados da região são enfim transformados em uma nova representação de acordo com a abordagem escolhida e depois a mesma região é descrita. Esta descrição consiste em reconhecer características da região da imagem que possam diferenciá-la de outras regiões ou ainda de outras imagens. Sendo assim, características matemáticas da imagem são extraídas em vários níveis de complexidade. Exemplos básicos incluem detecção de bordas, cantos ou pixels destacados na imagem [Costa e Cesar 2009]. Normalmente uma representação externa é escolhida quando as características da forma são importantes. Por outro lado, se a atenção da aplicação está na cor e textura, utiliza-se a representação interna. De toda maneira, as características selecionadas para o descritor da forma devem ser afetadas o mínimo possível por variações de mudança de escala, rotação e translação da imagem. A representação da forma geralmente compacta os dados em esquemas consideravelmente mais úteis no cálculo de descritores. A Figura 10.6 dispõe de duas assinaturas de formas. A primeira é uma representação funcional unidi- 224 Figura 10.6. Exemplo de realce por uso de fatiamento de intensidades. Adaptado de Gonzalez & Woods (2007). mensional de uma fronteira e pode ser gerada por maneiras distintas [Costa e Cesar 2009]. No exemplo apresentado, tem-se dois gráficos da distância da fronteira ao centróide em função do ângulo para as formas de círculo e quadrado. Obtém-se assim, uma redução na representação da forma a uma função unidimensional que se apresenta mais simples que a imagem original. Tabela 10.1. Exemplo de descrição de regiões. Na imagem à esquerda, imagem do mapa da América separada em regiões. Proporção de luzes utilizado como descritor, à direita. Adaptado de Gonzalez & Woods (2007). Para gerar uma descrição de uma região é importante avaliar quais os descritores que captam as diferenças essenciais entre os objetos ou classes de objetos. Diferentes informações da imagem podem ser tratadas como descritores. Exemplos simples são área e perímetro. A área de uma região é definida pelo número de pixels contidos dentro de sua fronteira. O perímetro é o tamanho dessa fronteira. A Tabela 10.1 traz um exemplo de descrição de uma representação de região. Utilizaram-se as informações de intensidade dos pixels para identificar quantos detalhes de luzes podem ser reconhecidos nas regiões da imagem apresentada (lado esquerdo da tabela). Um descritor foi definido a partir da contagem de pontos que representam luz em cada região e foi possível diferenciá-los entre si. 225 10.3.2. Reconhecimento de Padrões Conforme [Jain et al. 2000], o estudo de reconhecimento de padrões teve início depois da década de 60, quando foi desenvolvida a maior parte da teoria estatística moderna. Com o advento dos computadores, cresceu ainda mais a demanda por aplicações de reconhecimento de padrões. Atualmente, o reconhecimento de padrões é utilizado em aplicações envolvendo visão computacional, reconhecimento de caracteres, diagnóstico auxiliado por computador, reconhecimento da fala, reconhecimento de impressão digital, autentificação de assinaturas, etc. Existem muitos tipos de padrões, por exemplo: padrões visuais, padrões temporais, padrões lógicos. Pode-se dizer que tarefas de reconhecimentos de padrões são encontradas em toda atividade inteligente. Deste modo, não há uma só teoria de reconhecimento de padrões que seja capaz de cobrir a grande quantidade de problemas possíveis. Porém, existem algumas abordagens clássicas, incluindo: Reconhecimento Estatístico de Padrões, Reconhecimento de Padrões usando Lógica Fuzzy, Reconhecimento de Padrões usando Redes Neurais, Reconhecimento Sintático (ou Estrutural de Padrões) e Reconhecimento de Padrões Baseado em Conhecimento A abordagem mais amplamente utilizada é a estatística. Dessa forma, os problemas de reconhecimentos de padrões são tratados como problemas de classificação. O que consiste em associar um conjunto de atributos (características) de entrada a uma determinada categoria ou classe (saída). Em outras palavras, pode-se dizer que reconhecer padrões equivale a classificar determinado objeto físico ou situação como pertencente ou não a um certo número de categorias previamente estabelecidas. Há dois métodos de classificação: 1. Supervisionada: em que uma amostra de dados é previamente conhecida, representada por um conjunto de padrões e suas respectivas classes. Neste método, a regra de decisão pode ser representada por uma função densidade probabilidade, previamente conhecida (parametrizada) ou não (não parametrizada), ou por uma função de classificação. 2. Não-Supervisionada: em que não há associação dos padrões e suas respectivas classes. O desafio será encontrar agrupamentos de dados e identificar suas respectivas classes. Este método também é conhecido por clustering. 10.3.3. Atributos Uma forma de classificar um objeto ou evento é medindo algumas de suas características ou algumas de suas propriedades mais representativas (features). Por exemplo, para classificar uma letra impressa deve ser útil conhecer sua área e perímetro. A especificação das características necessárias para uma boa classificação depende das características do problema que se deseja resolver. Especificar esses atributos é uma tarefa árdua que necessita de um elevado grau de experimentação com diferentes combinações de características. Com frequência, um determinado objeto que se deseja classificar é representado por um conjunto fixo de d atributos. Neste caso, é útil pensar no conjunto de atributos 226 como um vetor de atributos x, também chamado simplesmente de padrão, tal que x é um vetor coluna de dimensão d. 10.3.4. Exemplos de Classificadores O objetivo de um classificador é dividir o espaço de atributos em regiões de decisão. Dessa forma, os vetores de atributos que estiverem contidos na mesma região de decisão compartilham a mesma classe. 10.3.4.1. Vizinho mais Próximo O algoritmo de classificação baseado no vizinho mais próximo (Nearest Neighbor - NN) é uma técnica amplamente empregada para reconhecer padrões. O centro de seu funcionamento está em descobrir o vizinho mais próximo de uma dada instância. O NN é considerado um dos mais simples algoritmos de aprendizagem de máquina. Ele funciona bem quando a distância entre as médias é grande quando comparada com a dispersão de cada classe em relação a sua média. Um resumo do funcionamento do classificador é mostrado no Algoritmo 1. Algoritmo 1 Algoritmo Vizinho Mais Próximo (Nearest Neighbor, NN). 1 Armazenar os exemplos em uma tabela. 2 Seja xnovo um vetor cuja classe é desconhecida, ou seja: Classe(xnovo ) =? 3 Procurar na tabela o vetor armazenado mais próximo de xnovo . 4 Chamar de x prox o vetor armazenado mais próximo de xnovo . 5 Atribuir a xnovo a mesma classe de x prox , ou seja: Classe(xnovo ) = Classe(x prox ) 6 Se a classificação for correta incluir xnovo na tabela. 10.3.4.2. Distância Mínima aos Centróides (DMC) O algoritmo Distância Mínima aos Centróides funciona de forma similar ao do Vizinho Mais Próximo. No entanto, cada classe passa a ter um único vetor que a representa, chamado de centróide. Dessa forma, não há necessidade de armazenar todos os exemplos de uma classe o que nos leva a uma economia de memória. O centróide de uma classe é o seu vetor médio; ou seja, a média dos exemplos daquela classe. Assim, pode-se dizer que o centróide de uma classe é um modelo que representa aquela classe. Um resumo do funcionamento do classificador é mostrado no Algoritmo 2. 227 Algoritmo 2 Algoritmo Distância Mínima aos Centróides (DMC). 1 Armazenar apenas os centróides das classes em uma tabela. 2 Seja xnovo um vetor cuja classe é desconhecida, ou seja: Classe(xnovo ) =? 3 Procurar na tabela o centróide mais próximo de xnovo . 4 Chamar de C j o centróide mais próximo de xnovo . 5 Atribuir a xnovo a mesma classe de centróide mais próximo, ou seja: Classe(xnovo ) = Classe(C j ) 6 Se a classificação for correta, usar xnovo para recalcular C j . 10.3.4.3. Rede Neural Artificial Uma rede neural artificial é composta por várias unidades de processamento, cujo funcionamento é paralelo e, por vezes, distribuído. Essas unidades, geralmente são conectadas por canais de comunicação que estão associados a determinado peso, que é denominado “peso sináptico”. As unidades fazem operações apenas sobre seus dados locais, que são entradas recebidas pelas suas conexões. O comportamento inteligente de uma rede neural artificial advém das interações entre as unidades de processamento da rede. O modelo de um neurônio artificial pode ser visto na Figura 10.3.4.3. Figura 10.7. Modelo de Neurônio Artificial baseado em Haykin (2001). O comportamento das conexões entre os neurônios é simulado por meio de seus pesos sinápticos. Os valores de tais pesos podem ser negativos ou positivos, dependendo das conexões serem inibitórias ou excitatórias. O efeito de um sinal proveniente de um outro neurônio é determinado pela multiplicação do valor (intensidade) do sinal recebido pelo peso da conexão correspondente (xi ..pi ). É efetuada a soma dos valores xi ..pi de todas as conexões, e o valor resultante é enviado para a função de ativação, que define a saída (y) do neurônio. Combinando diversos neurônios, forma-se uma rede neural artifi- 228 cial. De uma forma simplificada, uma rede neural artificial pode ser vista como um grafo onde os nós são os neurônios e as ligações fazem a função das sinapses. A rede neural artificial é um sistema de neurônios ligados por conexões sinápticas e dividido em neurônios de entrada, que recebem estímulos do meio externo, neurônios internos ou hidden (ocultos) e neurônios de saída, que se comunicam com o exterior. A forma de arranjar perceptrons em camadas é denominado Multilayer Perceptron (MLP). O MLP foi concebido para resolver problemas mais complexos, os quais não poderiam ser resolvidos pelo modelo de neurônio básico. Para isto são necessárias mais conexões, os quais só existem em uma rede de perceptrons dispostos em camadas. Na Camada escondida, camada oculta ou camada intermediária, como também é conhecida os neurônios são denominados escondidos porque eles não tem acesso direto a saída da rede MLP, onde os erros de aproximação são calculados. 10.4. Aplicações Nesta seção apresentamos duas aplicações do processamento digital de imagens no diagnóstico auxiliado por computador. A primeira é a identificação de estruturas da retina. A identificação dessas estruturas auxiliam no diagnósticos de várias doenças como a retinopatia diabética e o edema macular. A segunda envolve o reconhecimento e a análise de posturas humanas. Estas aplicações utilizam imagens obtidas em ambientes clínicos para o tratamento de desvios posturais. 10.4.1. Identificação de Estruturas da Retina A retina constitui a membrana mais interna do olho, situando-se na parede posterior. Quando o olho focaliza uma cena, a imagem correspondente é projetada sobre a retina, na qual estão distribuídos dois tipos de receptores de luz: os cones e os bastonetes. Os cones são são responsáveis pela chamada visão fotóptica ou de luz clara. Já os bastonetes são úteis para detectar detalhes. Entre os padrões presentes no fundo do olho, encontra-se a rede de veias da retina, que pode revelar uma série de doenças ligadas às variações ocorridas no globo ocular. O tratamento para essas doenças se torna mais eficaz quanto mais cedo elas são descobertas, e isso se dá através de exames oftalmológicos periódicos [Hajer et al. 2006]. Imagens digitais de retina podem prover informações sobre mudanças patológicas causadas por doenças oculares locais e sinais recentes de doenças sistemáticas como a hipertensão, a arterioesclerose e o Diabetes Mellitus (DM). A análise e interpretação desse tipo de imagem vem se tornando um auxílio importante e necessário no diagnóstico dessas doenças. 10.4.1.1. Disco Óptico Nos sitemas CAD para detecção de doenças oculares usando imagens coloridas de retina, a localização automática do disco óptico (DO) e o cálculo do seu contorno são passos fundamentais, antes de qualquer análise. A segmentação do disco óptico é um passo importante no pré-processamento de vários algoritmos desenvolvidos para a extração au- 229 tomática das estruturas anatômicas e para a detecção de lesões na retina. Além disso, esse processo também atua como um indicador de patologias oftalmológicas, como o glaucoma, que é uma das causas mais comuns de cegueira [Xu et al. 2007]. A identificação do disco óptico é também essencial na localização dos vasos, os quais, fornecem uma referência que pode facilitar o processo da detecção da posição de outras estruturas do fundo ocular e de lesões. Além disso, é essencial a remoção prévia do disco óptico nos algoritmos de detecção de exudatos1 , dada a semelhança entre ambos em termos de cor, brilho e contraste [Gagnon et al. 2001]. Basicamente, podemos classificar os algoritmos de detecção do disco óptico em duas classes: abordagem geográfica e abordagem baseada em características locais. Os algoritmos que usam a abordagem geográfica baseiam-se na informação fornecida pela estrutura dos vasos, isto é, no fato de todos os vasos da retina terem origem no disco óptico. Embora a detecção dos vasos seja uma operação complexa, a sua relação geométrica com o disco óptico pode ser utilizada para identificar a localização deste. Uma vez conhecida a localização do disco óptico, este é detectado como ponto inicial para determinar o respectivo contorno. Os algoritmos baseados nas características locais levam em consideração, principalmente, a sua forma redonda e brilho relativamente elevado, quando comparado com o resto da imagem. Em [Veras et al. 2011] os principais algoritmos de detecção do disco óptico foram avaliados e o método proposto por [Hoover e Goldbaum 2003]. A Figura 10.8 apresenta três exemplos de imagens de retina. Na Figura 10.8(a) o contorno do disco óptico é bem definido o que torna mais fácil sua detecção. Já na Figura 10.8(b), o plano de fundo é bastante heterogêneo e a região central possui intensidade de cor bem próxima a da região do disco óptico. A Figura 10.8(c) apresenta uma imagem onde não é possível a identificação do disco óptico. (a) (b) (c) Figura 10.8. Exemplos de imagens da base STARE 10.4.1.2. Mácula A detecção da mácula é uma tarefa relevante no processamento de imagens de fundo de olho (retina) e é utilizada em várias aplicações. Ela é considerada um importante 1 Produto seroso, purulento, resultante de processo inflamatório. 230 marcador fisiológico na análise de imagens da retina e é usada na localização de outras estruturas da retina como o DO, microvasos e exsudatos [Winder et al. 2009]. A mácula é um ponto ovalado de cor amarela junto ao centro da retina do olho humano e tem um diâmetro de cerca 1,5 mm. A região da mácula possui uma sub-região chamada fóvea, que localiza-se no centro da mácula e possui uma área menor que a mácula. É nesta última que encontramos uma maior quantidade de cones (células responsáveis pela percepção de cores). A mácula contém uma pigmentação mais escura e sua localização é aproximadamente o centro da retina. De acordo com [Tobin et al. 2007], o raio da mácula é igual ao dobro do raio do DO e a fóvea mede metade do raio do DO. Uma detecção precisa da mácula é um importante primeiro parâmetro para determinar patologias como a Degeneração e o Edema Macular. O diagnóstico de Edema Macular, por exemplo, é realizado levando-se em consideração a quantidade de exsudatos e suas localizações com relação à região da mácula. O algoritmo de detecção da mácula, na maioria dos trabalhos publicados, determina uma Região de Interesse (ROI - Region of Interest) baseado na localização do DO, como mostra a Figura 10.9. Após a determinação da ROI, algoritmos específicos são executados para definir o centro da mácula. Uma descrição e avaliação de vários métodos de detecção da mácula pode ser encontrada em [Silva et al. 2011]. Figura 10.9. Ilustração da ROI. 10.4.1.3. Microaneurismas e Exsudatos A Retinopatia Diabética (RD) é uma doença assintomática nas fases iniciais, o que dificulta a sua detecção. Ela ocorre como resultado das alterações vasculares na retina causando inchaços de capilares, conhecidos como microaneurismas (MA). Estes podem romper-se, e, eventualmente, se tornarem uma fonte de extravasamento de plasma causando o espessamento da retina, ou edema, se ocorrem na região macular, e pode causar perda de visão de alta qualidade [Fleming et al. 2007]. O espessamento da retina não é facilmente visível nas fotografias do fundo do olho, portanto, regiões de depósitos de gordura, conhecidas como exsudatos, são usadas como marcadores. Exsudatos geralmente formam aglomerados, e podem ser distribuídos em toda a retina ou podem aparecer em um anel em torno de um ponto central do vazamento. 231 A Organização Mundial de Saúde estima que 135 milhões de pessoas tenham diabetes no mundo e que este número de diabéticos deverá aumentar para 300 milhões até o ano de 2025 [Amos et al. 1997]. A RD afeta uma parte considerável da população, o que justifica trabalhos avançados na área de detecção de doenças por imagens. Desenvolvendo técnicas simples, e com um custo-benefício baixo, seria possível a detecção da RD em sua fase inicial. Estudos comprovam que quanto mais cedo for detectada a RD mais chances o paciente terá de não desenvolver a doença em seu grau mais elevado. Microaneurismas se apresentam como os primeiros padrões patológicos que surgem na retina humana, em portadores de Retinopatia Diabética, e constituem uma evidência direta de isquemia, uma vez que cada microaneurisma representa a oclusão de ao menos um vaso capilar. Portanto, existe uma relação entre o número de microaneurismas e o agravamento da doença em pacientes portadores do Diabetes Mellitus, ou simplesmente Diabetes, como é popularmente conhecida. Sistemas automáticos de detecção de microaneurismas vêm sendo bastante utilizados em substituição aos sistemas manuais de contagem, uma vez que estes consomem muito tempo e são mais sujeitos a erros. Alguns dos principais métodos são [Fleming et al. 2007, Walter et al. 2002, Martins et al. 2008]. Com o avanço da Retinopatia Diabética aparecem os exsudatos. Quando isso ocorre a doença já se encontra em um nivel grave, mas ainda pode ser controlada. A Figura 10.10(a) é um exemplo de retina saudável. Já na Figura 10.10(b) existe alguns exsudatos bem pequenos na região central da retina. A Figura 10.10(c) apresenta vários exsudatos e hemorragias. (a) (b) (c) Figura 10.10. Exemplos de imagens com e sem exsudatos Basicamente existem três técnicas diferentes de detecção exsudatos em imagens da retina. A primeira funciona através da definição de um limiar como foi observado em [Kavitha e Shenbaga 2005]. Essa técnica não possui um bom desempenho, pois como o disco óptico possui propriedades de cor semelhante a dos exsudatos muitas vezes ele é marcado incorretamente como exsudato. Outro problema dessa técnica ocorre quando aparecem regiões mais claras na imagem, elas também podem ser marcadas como exsudatos. A segunda técnica utiliza agrupamento de pixels, como em [Sopharak et al. 2010]. Essa é a principal técnica usada para a detecção dos exsudatos, porém isoladamente ela não produz resultados satisfatórios, pois muitas vezes é necessário o uso de classificação ou morfologia matemática para eliminar falsos candidatos [de Araújo et al. 2011]. A terceira técnica faz uso de operações de morfologia matemática, como foi ob- 232 servado em [Walter et al. 2002]. Assim como no agrupamento, essa técnica não produz bons resultados isoladamente. Geralmente ela é usada para remover falsos candidatos ou pixels isolados. 10.4.1.4. Microvasos Imagens de fundo de olho fornecem informações sobre as mudanças patológicas causadas por doenças oculares. Uma característica importante para o diagnóstico de doenças como diabetes, arteriosclerose e hipertensão é a aparência dos vasos sanguíneos na retina [Li et al. 2006]. O desenvolvimento de uma solução computacional para a segmentação dos vasos sanguíneos permite que o médico especialista diagnostique melhor os casos de vascularidade anormal. Em outros casos, o desempenho de métodos automáticos de detecção (segmentação) de vasos auxilia no diagnóstico de outras doenças como foi apresentado em [Martins et al. 2009]. Entretanto, essa segmentação é dificultada pelos seguintes fatos [Li et al. 2006]: a largura dos vasos pode variar de muito larga a muito fina e o contraste local dos vasos não é estável. Várias abordagens para segmentação de vasos são encontradas na literatura [Zana e Klein 2001, Niemeijer et al. 2004, Soares et al. 2006]. A Figura 10.4.1.4 apresenta um exemplo de imagem de retina e o resultado de dois métodos diferentes de segmentação de vasos. (a) (b) (c) Figura 10.11. Imagem de retina com duas segmentações de vasos 10.4.2. Postura Postura é geralmente definida como o arranjo relativo das partes do corpo. Dessa maneira, a postura correta traduz o estado de equilíbrio muscular e esquelético que protege as estruturas de suporte do corpo contra lesão ou deformidade progressiva. Independente da atitude assumida pelo paciente (agachada, ereta, deitada), estas estruturas estão trabalhando ou repousando. Os órgãos torácicos e abdominais mantêm-se em posições ideais e os músculos funcionam com mais eficiência sob tais condições. [Furlaneto et al. 2007] Consequentemente, com a má postura ocorre uma relação defeituosa entre várias partes do corpo. Isso produz uma maior tensão sobre estruturas de suporte e onde ocorre um equilíbrio menos eficiente do corpo [Ferreira et al. 2011]. 233 Entre diversos tratamentos fisioterapêuticos existentes, a Reeducação Postural Global (RPG) é um método que atua na dor e no desconforto provocados por alterações posturais. Diferente das outras abordagens, o tratamento por RPG leva em consideração que o corpo humano é um sistema muscular integrado. Portanto, há a necessidade da observação de toda a silhueta humana independentemente da localização do desvio de postura [Ferreira et al. 2010]. A Figura 10.12 traz a caracterização da coluna vertebral, que é dividida em quatro curvaturas. Duas delas com a concavidade virada para trás (lordoses nas regiões cervical e lombar) e duas delas com a concavidade virada para a frente (cifoses nas regiões toráxica e pélvica, ver Figura 10.12(a)). Em geral, as imagens para o diagnóstico são tomadas em vários planos de observação. Alguns desvios da postura podem ser observados em imagens do plano frontal do paciente, como a escoliose (Figura 10.12(b)). Imagens adquiridas em plano sagital provêem conhecimento de desvios posturais, tais como hipercifose dorsal, hiperlordose lombar e hiperlordose cervical. Também é possível mensurar a angulação da tíbia em relação ao pé, flexão e extensão dos joelhos e inclinação anterior e posterior da pélvis [Passarinho et al. 2006, Ferreira et al. 2011]. (a) (b) Figura 10.12. Caracterização da (a) coluna vertebral e suas regiões ou curvaturas (Fonte: Wikipédia). Como exemplo de desvio, a (b) escoliose que pode ser percebida no plano frontal (Fonte: Wikipédia). Passarinho e outros autores apresentaram em [Passarinho et al. 2006] uma metodologia utilizada para avaliação de mudanças no equilíbrio postural em pacientes submetidos à RPG. Este trabalho descreve uma medida de similaridade com o intuito de obter melhor desempenho na quantificação das alterações posturais. O procedimento se inicia com o cálculo das assinaturas das formas dos pacientes. Estas formas são obtidas de segmentações aplicadas sobre as imagens obtidas pelo fisioterapeuta. Esta assinatura traz uma nova representação da forma com uma relação entre a curvatura e a informação de ângulo, em relação ao centróide, de cada um dos pontos 234 do contorno da forma. A função de curvatura descreve a variância da direção assumida pelo contorno da forma [Cesar e Costa 1996]. Quanto mais ereta é a postura do paciente, sua função de curvatura correspondente alcançará menor amplitude. A Figura 10.13(a) mostra a comparação entre duas assinaturas polares de um mesmo paciente antes e depois do tratamento. O método descrito em [Passarinho et al. 2006] usa como medida de similaridade o cálculo da diferença de áreas sob as assinaturas e é inversamente proporcional à diferença da área sob as curvas das assinaturas. Esta medida, SM, é calculada através da relação: SM = 1 − 1 S, em que S é a diferença das áreas sob as assinaturas polares dos perfis sagitais em estudo. Este mesmo método avaliou sua medida de similaridade em comparação com a proposta em [Bernier e Landry 2003] para um conjunto de imagens adquiridas em 22 pacientes. Cada um deles possibilita a aquisição de duas imagens para que sejam obtidas suas respectivas formas. O objetivo é demonstrar que a medida SM mostra-se mais sensível às mudanças da postura do paciente após o tratamento de RPG. Os resultados podem ser vistos na Figura 10.13(b). Os valores alcançados para essa medida significam que o paciente em questão obteve resposta mais significativa ao tratamento fisioterapêutico. (a) (b) Figura 10.13. Análise de similaridade de formas: (a) há a correspondência das assinaturas polares de um paciente antes (azul) e após (vermelho) o RPG; (b) similaridade para os contornos sagitais de 22 pacientes distintos. Adaptado de [Passarinho et al. 2006]. Uma aplicação em fisioterapia utilizando o Software de Análise Postural (SAPO) como um sistema CAD foi proposto em [Ferreira et al. 2011]. Este possui características distintas quando comparado com outros softwares disponíveis, porque inclui a análise de postura global do corpo e a análise de ângulos e distâncias de maneira independente. Permite o arquivamento e comparação de fotografias para observar a evolução do paciente em determinado tratamento. O software também permite calibração e ajustes de foto para evitar erros de medição pequenos e aumentar a confiabilidade do método. Ferreira e outros autores, com o auxílio deste software citado, realizaram uma avaliação quantitativa global do alinhamento postural de adultos jovens e saudáveis em pé, com base em pontos de vista anterior, posterior e lateral. O trabalho envolveu 122 participantes que foram submetidos a uma análise de agrupamento pela similaridade das variáveis do estudo. O sistema indica a marcação de 50 pontos anatômicos distribuídos entre as visões anterior, laterais e posterior, como o lóbulo da orelha, a linha de junção 235 do joelho, calcâneo, ângulo inferior da escápula, entre outros. Destes total, 16 deles eram bilaterais [Ferreira et al. 2010]. Alguns destes pontos estão visíveis na Figura 10.14. Este estudo e o software estão disponíveis em http://sapo.incubadora.fapesp.br, e inclui tutoriais científicos, bem como vários recursos de apoio à análise de fotografias. Figura 10.14. Pontos anatômicos e ângulos avaliados nas visões laterais. Adaptado de [Ferreira et al. 2011]. Os dados foram submetidos à análise estatística descritiva. Valores quantitativos para a cabeça, membros superiores e inferiores, e alinhamento do tronco foram obtidos, juntamente com a freqüência das inclinações para a esquerda e direita. As medidas utilizadas para análise da postura incluiram distâncias (em centímetros) e ângulos (em graus), extraídos da combinação de pontos anatômicos descritos em [Ferreira et al. 2011]. As variáveis da avaliação postural foram analisadas através da média, desvio padrão e valores de máximo e mínimo. Vários valores de referência padrão de referência foram definidos neste estudo para avaliar o alinhamento postural. Membros inferiores mostraram alinhamento similar. A posição da cabeça, tronco e membros superiores e inferiores em vistas laterais são apresentados na Tabela 10.2. Medidas envolvendo o quadril, tornozelo, cifose, lordose e mostrou maior variabilidade. Existem pequenas variações no alinhamento dos segmentos corporais nas visões anterior e posterior em indivíduos saudáveis. Os resultados do estudo demonstraram uma tendência de assimetria entre os segmentos bilaterais na visão anterior, com a pélvis, ombros e tronco mostrando ligeira inclinação para a direita. Na visão posterior, uma pequena assimetria foi observada no posicionamento da pélvis e escápula. Maiores detalhes, como identificação dos outros pontos anatômicos e o resultado detalhado da análise das variáveis da postura estão descritos em [Ferreira et al. 2011]. 236 Tabela 10.2. Valores de média (desvio padrão), mínimo e máximo para as variáveis de postura observadas nas visões laterais, medidas em ângulos. Adaptado de [Ferreira et al. 2011]. Variáveis (◦ ) Média (desvio padrão) Alinhamento horizontal da cabeça 47,1 (4,8) Alinhamento horizontal da pélvis 172,6 (4,8) Alinhamento sagital do membro inferior 177,9 (4,8) Ângulo de junção do quadril 149,8 (8,0) Ângulo de junção do maléolo 86,2 (2,6) Alinhamento vertical do torso 182,4 (2,1) Alinhamento vertical do corpo 178,2 (0,9) Alinhamento do membro superior 155,8 (5,1) Alinhamento sagital do corpo 186,8 (3,6) Ângulo da cifose torácica 55,4 (7,4) Ângulo da lordose lombar 47,7 (15,4) Mínimo 31,2 158,6 166,7 129,7 79,9 177,6 175,8 145,7 176,4 39,3 23,3 Máximo 58,4 182,4 190,6 176,2 91,6 187,0 180,0 170,7 198,5 68,2 96,4 10.5. Conclusão Estre trabalho apresentou, com base em publicações científicas, que a utilização de algoritmos computacionais para auxílio à análise de imagens médicas tem-se mostrado eficiente na melhoria da detecção e classificação de lesões e/ou patologias humanas. Existem aplicações em diferentes especialidades e todas com resultados eficientes para distintas análises clínicas. É importante ressaltar que o objetivo de um sistema CAD está na realização de uma análise automatizada como forma de auxílio, e não um substituto para o especialista da área da saúde. Portanto, é exigido que toda aplicação dessa natureza apresente um desempenho próximo ao desse especialista. O acompanhamento deste último ao processo de desenvolvimento do sistema CAD é um fator que incrementa confiança e eficiência aos resultados da aplicação. Referências [Amos et al. 1997] Amos, A. F., MacCarty, D. J., e Zimmet, P. (1997). The rising global burden of diabetes and its complications: Estimates and projections to the year 2010. Diabetc Medicine, 14:S1–S85. [Azevedo et al. 2007] Azevedo, E., Conci, A., e Leta, F. R. (2007). Computação Gráfica: Processamento de Imagens Digitais, volume 2. Editora Campus. [Bernier e Landry 2003] Bernier, T. e Landry, J.-A. (2003). A new method for representing and matching shapes of natural objects. Pattern Recognition, 36(8):1711–1723. [Burger e Burge 2009] Burger, W. e Burge, M. J. (2009). Principles of Digital Image Processing: Fundamental Techniques. Springer. [Castleman 1996] Castleman, K. R. (1996). Digital Image Processing. Prentice Hall, Upper Saddle River. 237 [Cesar e Costa 1996] Cesar, R. M. J. e Costa, L. F. (1996). Towards effective planar shape representation with multiscale digital curvature analysis based on signal processing techniques. Pattern Recognition, 28(9):1559–1569. [Costa e Cesar 2009] Costa, L. F. e Cesar, R. M. J. (2009). Shape Analysis and Classification: Theory and Practice. CRC Press, 2a edição. [de Araújo et al. 2011] de Araújo, F., Veras, R., e Silva, R. (2011). Aperfeiçoamento da detecção de exsudatos com k-means fuzzy, detecção de vasos e morfologia matemática. In V Escola Regional de Computação dos Estados do Ceará, Maranhão e Piauí. [Doi 2007] Doi, K. (2007). Computer-aided diagnosis in medical imaging: Historical review, current status and future potential. Computerized Medical Imaging and Graphics, 31:198–211. [Ferreira et al. 2011] Ferreira, E. A., Duarte, M., Maldonado, E. P., Bersanetti, A. A., e Marques, A. P. (2011). Quantitative assessment of postural alignment in young adults based on photographs of anterior, posterior, and lateral views. Journal of Manipulative and Physiological Therapeutics, 34(6):371–380. [Ferreira et al. 2010] Ferreira, E. A. G., Duarte, M., Maldonado, E. P., Burke, T. N., e Marques, A. P. (2010). Postural assessment software (PAS/SAPO): Validation and reliabiliy. Clinics, 65:675–681. [F.F. et al. 1991] F.F., Y., M.L., G., K., D., C.E., M., C.J., V., e R.A., S. (1991). Computerized detection of masses in digital mammograms: analysis of bilateralsubtraction images. Med Phys, páginas 55–63. [Fleming et al. 2007] Fleming, A. D., Goatman, K. A., Philip, S., Olson, J. A., e Sharp, P. F. (2007). Automatic detection of retinal anatomy to assist diabetic retinopathy screening. Physics in Medicine and Biology, 52(2):331–345. [Furlaneto et al. 2007] Furlaneto, T. S., Candotti, C. T., e Loss, J. F. (2007). Desenvolvimento de uma metodologia digital para avaliação postural no plano sagital. In Anais do XII Congresso Brasileiro de Biomecânica, São Pedro - SP. [Gagnon et al. 2001] Gagnon, L., Lalonde, M., Beaulieu, M., e Boucher, M. C. (2001). Procedure to detect anatomical structures in optical fundus images. In Proceedings of Conference Medical Imaging, volume 4322, páginas 1218–1225, San Diego. [Gomes e Velho 1994] Gomes, J. e Velho, L. (1994). Computação Gráfica: Imagem. IMPA. [Gonzalez e Woods 2007] Gonzalez, R. C. e Woods, R. E. (2007). Digital Image Processing. Prentice Hall, Nova York, EUA, 3a edição. [Hajer et al. 2006] Hajer, J., Kamel, H., e Noureddine, E. (2006). Blood vessels segmentation in retina image using mathematical morphology and the STFT analysis. Information and Communications Technologies, 1:1130–1134. 238 [Hong Shao 2004] Hong Shao, Wen-Cheng Cui, H. Z. (2004). Automatic analysis of brain pathology based on image content. In IEEE International Conference on Systems. [Hoover e Goldbaum 2003] Hoover, A. e Goldbaum, M. (2003). Locating the optic nerve in a retinal image using the fuzzy convergence of the blood vessels. IEEE Transactions on Medical Imaging, 22(8):951–958. [Jain et al. 2000] Jain, A. K., Duin, R. P., e Mao, J. (2000). Statistical pattern recognition: A review. IEEE Transactions on Pattern Analysis and Machine Intelligence, 22(1):4– 37. [Kavitha e Shenbaga 2005] Kavitha, D. e Shenbaga, S. (2005). A cellular neurofuzzy network for contrast enhancement of fundus images with retinopathies. In Proceeding ICS’06 Proceedings of the 10th WSEAS international conference on Systems. [Li et al. 2006] Li, Q., Zhang, L., Zhang, D., e Bhattacharya, P. (2006). A new approach to automated retinal vessel segmentation using multiscale analysis. In ICPR, páginas 77–88. IEEE Computer Society. [Marques 2001] Marques, P. (2001). Diagnóstico auxiliado por computador na radiologia. Radiol Bras, 34(5):285–293. [Marques et al. 2004] Marques, R., Carvalho, E., Costa, R., e Medeiros, F. (2004). Filtering effects on sar images segmentation. Lecture Notes in Computer Science, 3124:1041–1046. [Martins et al. 2008] Martins, C., R.M.S.Veras, G.L.B.Ramalho, F.N.S.Medeiros, e Ushizima, D. M. (2008). Automatic microaneurysm detection and characterization through digital color fundus images. In II Workshop on Computational Intelligence SBRN, páginas 45–50. [Martins et al. 2009] Martins, C. I. O., Medeiros, F. N. S., e ans F. N. Bezerra ans R. M. Cesar Jr., R. M. S. V. (2009). Evaluation of retinal vessel segmentation methods for microaneurisms detection. In International Conference on Image Processing (ICIP), páginas 3365–3368. [Niemeijer et al. 2004] Niemeijer, M., Staal, J. J., van Ginneken, B., Loog, M., e Abràmoff, M. D. (2004). Comparative study of retinal vessel segmentation methods on a new publicly available database. In Fitzpatrick, J. M. e Sonka, M., editors, SPIE Medical Imaging, volume 5370, páginas 648–656. [Passarinho et al. 2006] Passarinho, C. J. P., Cintra, L. H. S., Medeiros, F. N. S., Oliveira, I. N. S., e Paula, I. C. J. (2006). Análise de similaridade e correspondência de formas aplicada à reeducação postural global. In Anais do XX Congresso Brasileiro de Engenharia Biomédica, páginas 117–120, São Pedro - SP. [Paula 2009] Paula, I. C. J. (2009). Livro Texto dos Minicursos - Ercemapi 2009, capítulo Técnicas de Processamento Digital de Imagens com Java. SBC, Parnaíba - PI. 239 [Paula et al. 2011] Paula, I. C. J., Medeiros, F. N. S., Bezerra, F. N., e Ushizima, D. M. (2011). Corner detection within a multiscale framework. In Proceedings of Sibgrapi 2011 (XXIV Conference on Graphics, Patterns and Images), Maceió - AL. IEEE. [Silva et al. 2011] Silva, R., Veras, R., e de Araújo, F. (2011). Avaliação de métodos para detecção da mácula em imagens da retina. In V Escola Regional de Computação dos Estados do Ceará, Maranhão e Piauí. [Soares et al. 2006] Soares, J. V. B., Leandro, J. J. G., Cesar-Jr., R. M., Jelinek, H. F., e Cree, M. J. (2006). Retinal vessel segmentation using the 2-D Gabor wavelet and supervised classification. IEEE Transactions on Medical Imaging, 25:1214–1222. [Sopharak et al. 2010] Sopharak, A., Dailey, N., Uyyanonvara, B., Barman, S., Williamson, T., New, T. N., e Moe, A. Y. (2010). Machine learning approach to automatic exudate detection in retinal images from diabetic patients. Journal of Modern Optics, (57):124–135. [Thurfjell et al. 1994] Thurfjell, E. L., Lernevall, K. A., e Taube, A. A. (1994). Benefit of independent double reading in a population-based mammography screening program. Radiology, 191:241–244. [Tobin et al. 2007] Tobin, K. W., Chaum, E., Govindasamy, V. P., e Karnowski, T. P. (2007). Detection of anatomic structures in human retinal imagery. IEEE Transactions on Medical Imaging, 26(16):1729–39. [Velho et al. 2009] Velho, L., Frery, A., e Gomes, J. (2009). Image Processing for Computer Graphics and Vision. Springer, 2a edição. [Veras et al. 2011] Veras, R., Araújo, F., Silva, R., Medeiros, F., e Aires, K. (2011). omparação e avaliação de métodos de detecção do disco Óptico. In XXXVII Conferencia Latinoamericana de Informática. [Walter et al. 2002] Walter, T., Klevin, J., Massin, P., e Erginay, A. (2002). A contribution of image processing to the diagnosis of diabetic retinopathy-detection of exudates in color fundus images of the human retina. IEEE Transactions on Medical Imaging, 21(10):1236 – 1243. [Winder et al. 2009] Winder, R., Morrow, P., McRitchie, I., Bailie, J., e Hart, P. (2009). Algorithms for digital image processing in diabetic retinopathy. Computerized Medical Imaging and Graphics, page 15. [Xu et al. 2007] Xu, J., Chutatape, O., e Chew, P. (2007). Automated optic disk boundary detection by modified active contour model. IEEE Transactions on Biomedical Engineering, 54(3):473–482. [Zana e Klein 2001] Zana, F. e Klein, J. C. (2001). Segmentation of vessel-like patterns using mathematical morphology and curvature evaluation. IEEE Transactions on Image Processing, 10:1010–1019. 240