Capítulo 7 Estudo de Caso: FlexTV – Um Middleware Orientado a
7.3 Modelo de Componentes do FlexTV
A arquitetura do FlexTV apresentada na seção anterior define apenas quais os principais grupos funcionais que constituem o sistema. Ou seja, ela define os principais componentes de alto nível do sistema e quais funcionalidades devem ser providas por cada um. Entretanto, para que essa arquitetura possa ser implementada, faz-se necessária a escolha de um modelo para a implementação de cada componente e seus subcomponentes. É o modelo de componentes quem dita os formatos das interfaces e o ciclo de vida dos componentes; os mecanismos de gerenciamento que devem estar presentes no ambiente de execução; etc. No caso do middleware FlexTV, o modelo de componentes deve possuir algumas características que atendam aos requisitos específicos desse projeto:
1. O modelo deve ser leve, com baixo consumo de recursos computacionais, possibilitando a implementação de componentes que possam ser executados em ambientes embarcados e com restrições de recursos computacionais, como é o caso dos televisores digitais.
2. Deve ser possível gerar uma representação do contexto do sistema para viabilizar o processo de reflexão computacional. Essa representação deve contemplar:
2.1. A maneira como os componentes estão conectados – a configuração do sistema, e uma indicação de que componentes podem ou não ser substituídos;
2.2. O estado atual de cada componente – que é capturado através de interfaces específicas de cada componente;
2.3. O estado de cada conexão entre componentes – gerado a partir de interfaces específicas em cada componente;
3. O modelo de componentes deve possibilitar a reconfiguração dos mesmos no sistema em tempo de execução;
4. O sistema deve ser passível de desenvolvimento multi-linguagem e independente de sistema operacional, contando com mecanismos de controle distribuídos.
De forma a atender a todos os requisitos necessários, foi desenvolvido um modelo de componentes específico para o FlexTV. Esse modelo foi baseado em outros já existentes, tais como: COM [Microsoft 1995], EJB [SUN 2006], DynamicTAO [Kon 2000], OpenCOM [Clarke 2001], além de alguns conceitos presentes na arquitetura CORBA [OMG 2003], e resgata de cada um os elementos e características que viabilizam o atendimento aos requisitos desejados para o FlexTV.
No modelo de componentes desenvolvido, cada componente possui um conjunto de interfaces que especificam quais serviços serão providos e como eles podem ser acessados por outros componentes. Tais interfaces poderiam ser pensadas como sendo contratos de serviços que o componente se compromete a prestar. Para que um componente possa
serviços fornecidos por outros componentes no sistema. No modelo de componentes do FlexTV, um determinado componente pode sinalizar que necessita dos serviços de outros componentes através de Interfaces de Acesso, que pode coexistir com as Interfaces
Provedoras do componente. As Interfaces de Acesso podem ser conectadas a interfaces específicas providas por componentes específicos para que o seu componente possa funcionar apropriadamente. Uma representação gráfica de um sistema baseado no modelo de componentes do FlexTV com suas interfaces provedoras e de acesso está presente na Figura 7.2. Nesta representação pode-se observar que o componente A provê a interface I6 e se conecta às interfaces I1 e I2 e o componente B provê a interface I5 e se conecta às interfaces I3 e I4.
Figura 7.2 - Representação Interna de um Sistema Orientado a Componentes
Pode-se observar ainda na Figura 7.2 que as interfaces dos componentes, sejam elas provedoras ou de acesso, podem ser agrupadas em portas. Uma porta de um determinado componente pode conter todas as interfaces que devem ser conectadas a um outro componente qualquer. Assim sendo, uma porta atua como ponto de conexão entre componentes. O uso de portas facilita o processo de interconexão de componentes, tendo em vista que as suas interfaces podem ser conectadas simultaneamente, através de um único comando de conexão executado na própria porta. Ou seja, as portas de dois componentes podem ser conectadas, ocasionando a conexão das suas interfaces de forma apropriada.
Além disso, interfaces específicas disponibilizadas pelas portas podem ser utilizadas para monitorar o estado das conexões entre componentes.
Para que os componentes do FlexTV possam ser utilizados na prática, faz-se necessário que o ambiente no qual eles serão executados possua alguns elementos considerados básicos. Esses elementos estão representados na Figura 7.3.
Figura 7.3 - Principais módulos do ambiente de execução dos componentes do FlexTV
Na Figura 7.3, a camada Serviços ORB viabiliza o desenvolvimento das camadas superiores de forma independente de linguagem de programação e de sistema operacional. O ORB possibilita que os componentes sejam implementados em alguma linguagem de programação e que as suas interfaces sejam especificadas através de uma Linguagem de Descrição de Interfaces (Interface Description Language – IDL). A partir dessa descrição, o ORB pode promover a conexão entre interfaces e conectores compatíveis de componentes distintos, mesmo que estes tenham sido implementados através de diferentes linguagens de programação e estejam sendo executados em ambientes distribuídos. É também através do ORB que são providos para as camadas superiores alguns serviços como: gerenciamento de eventos, segurança, persistência e gerenciamento de transações.
A camada Gerenciamento de Componentes por sua vez, é composta pelos seguintes módulos:
• Container de Componentes – este módulo funciona como uma fábrica de componentes, isto é, ele gerencia o armazenamento local do código binário dos componentes e promove a instanciação destes (a sua execução), quando necessário. • Gerenciador de Ciclo de Vida de Componentes – responsável por gerenciar o ciclo
de vida dos componentes, possibilitando que os mesmos sejam levados a determinados estados que permitam a reconfiguração do sistema sem gerar inconsistência no mesmo. O Gerenciador de Ciclo de Vida de Componentes implementa mecanismos que possibilitam que os componentes do sistema sejam levados aos seguintes estados:
o Loaded – carregado na memória mais ainda não inicializado.
o Paused – funcionamento momentaneamente interrompido e mantendo o
contexto atual de execução.
o Started – componente em execução normal.
o Destroyed – componentes que tiveram a execução interrompida ou foram
destruídos.
Para que possa ser instanciado e ter seu ciclo de vida gerenciado, cada componente deve implementar uma série de interfaces obrigatórias que podem ser acessadas pelos módulos de gerenciamento de forma padronizada. A sintaxe e a semântica dessas interfaces devem ser muito bem definidas para evitar inconsistências no sistema. O modelo de componentes do FlexTV impõe que cada componente implemente as interfaces apresentadas na Figura 7.4. Os métodos das interfaces foram suprimidos para não sobrecarregar a figura.
Figura 7.4 – Diagrama de classes UML representando as interfaces que devem ser implementadas por cada componente do FlexTV
• IUnknown – interface básica para todos os componentes. Todas as interfaces do componente devem herdar dessa interface. Ela é utilizada para controlar e gerenciar as referências para componentes instanciados no sistema. Esta interface permite o acesso a um contador de referências que é utilizado para reconhecer quando nenhuma entidade do sistema está fazendo uso do componente que a implementa. Métodos:
o AddRef() – responsável pelo incremento no contador de referências do
componente. Este método deve ser invocado sempre que alguma interface do componente for acessada pela primeira vez por alguma entidade do sistema. Isso serve para que o componente tenha ciência do número de entidades que o estão acessando a cada momento.
o QueryInterface() – Este método retorna e permite o acesso a outras
interfaces do componente, recebendo como parâmetro um identificador único associado à interface desejada.
o Release() – subtrai 1 do contador de referência do componente. Indicando
que a entidade que fazia uso da interface não o faz mais. Quando o contador chegar a zero, significa que nenhuma entidade está mais fazendo uso do componente e que este pode finalizar sua execução.
IUnknown
IBaseComponnet ILifeCycle
IComponentMetaInfo IConnectionMetaInfo
IComponentSpecific
• ILifeCycle – é a partir desta interface que ocorre o gerenciamento do ciclo de vida dos componentes. O método Initialize() presente na mesma é invocado no momento da execução do componente, sendo responsável por iniciá-lo. A partir daí, os métodos Pause() e Resume() podem ser invocados respectivamente para interromper momentaneamente e retomar a execução do componente. Quando o componente é destruído, o método Destroy() é invocado de forma a possibilitar a liberação de possíveis recursos alocados.
Métodos:
o Initialize() – inicializa o componente;
o Pause() – interrompe a execução do componente, mantendo suas
informações em memória para que ele possa futuramente retomar a execução do ponto em que parou;
o Resume() – retoma a execução do componente. Para tanto, este método
recupera as informações previamente armazenadas pelo componente;
o Destroy() – finaliza o componente, executando os métodos necessários para
a sua remoção segura.
• IBaseComponent – Especifica métodos para controle e gerenciamento das conexões inter-componentes no sistema. Esta interface possibilita o acesso a todas as portas de um componente. É através dela que se pode conectar dois componentes, assegurando compatibilidade entre os mesmos.
Métodos:
o Connect() – conecta as portas de dois componentes; o Disconnect() – desconecta portas de dois componentes;
o GetPorts() – retorna o conjunto de portas presentes em um componente
o Release() – finaliza a execução do componente bem como de seus
subcomponentes e conexões;
• IPort – Essa interface possui métodos que devem ser invocados durante a interconexão de componentes com o intuito de verificar a viabilidade ou não da conexão.
Métodos:
o Connect() – Esse método deve ser invocado no momento da conexão de
uma interface requerida por um componente com uma interface provida por outro componente. O método retorna então se é possível ou não fazer a conexão;
o Disconnect() – Esse método deve ser invocado no momento da desconexão
de uma interface provida por um componente da interface requerida por outro componente, à qual a primeira deve estar conectada. O método retorna então se é possível ou não fazer a desconexão;
o GetInterfaces() – Esse método retorna o conjunto das interfaces presentes
em uma determinada porta;
o GetInterfaceDependency() – Esse método retorna o identificador da
interface da qual uma interface requerida de um componente depende. Essa interface deve ser provida por um outro componente e conectada à interface requerida para que seu componente possa funcionar apropriadamente.
• IComponentMetaInfo e IConnectionMetaInfo – Possibilitam o acesso às informações referentes ao contexto de execução dos componentes e das suas conexões respectivamente. As informações contextuais são caracterizadas por um conjunto de variáveis que possuem uma descrição semântica e um valor atribuído que varia ao longo da execução do componente
Métodos:
o getVarDescription() – Acessa a descrição semântica de uma dada variável
do contexto;
o getVarValue() – Acessa o valor atual da variável do contexto;
o notifyVarChanges() – Registra uma função de callback que deve ser
invocada pelo componente sempre que houver mudança no valor da variável de contexto.