Compreender os Conceitos OSGi. Tente seguir a abordagem do quebra-cabeça

Publicados: 2013-04-20

OSGi tornou-se muito popular hoje, graças à sua abordagem de modularidade e sua capacidade de impor limites lógicos entre os módulos. Quando a descobrimos pela primeira vez, a questão é por onde começar a entender como ela funciona?

Para entender os conceitos do OSGi tentaremos seguir a abordagem do quebra-cabeça, a ideia é começar com a parte trivial desta tecnologia, e buscar outras partes relacionadas às encontradas. E para montar o quebra-cabeça, contaremos com a ajuda do JArchitect, que será útil para detectar o design interno do OSGi.

Para ser mais concreto, analisamos com JArchitect uma aplicação usando a tecnologia OSGi, que diz respeito ao famoso Eclipse IDE que usa o equinócio de contêiner OSGi.

Vamos começar com a definição típica de OSGi:

O OSGi reduz a complexidade fornecendo uma arquitetura modular para os sistemas distribuídos de grande escala atuais, bem como para pequenos aplicativos incorporados. Construir sistemas a partir de módulos internos e de prateleira reduz significativamente a complexidade e, portanto, as despesas de desenvolvimento e manutenção. O modelo de programação OSGi cumpre a promessa de sistemas baseados em componentes.

A parte trivial do OSGi é a modularidade, vamos descobrir quais são os módulos OSGi?

Os módulos OSGI são chamados de Bundles e, portanto, cada aplicativo consiste em pelo menos um bundle.
Esses bundles são executados dentro de um container, e a cada abordagem modular onde um container gerencia os módulos, a questão é qual contrato deve implementar cada módulo para ser integrado ao container?

Vamos pegar como exemplo o bundle org.eclipse.equinox.jsp.jasper e buscar todas as interfaces implementadas, para isso podemos executar a seguinte requisição CQLinq:

eclipse4

A interface BundleActivator do pacote OSGi é implementada, essa interface contém dois métodos start e stop úteis para customizar o início e a parada do pacote configurável.

Outra especificidade de um pacote é seu arquivo de manifesto, aqui está uma parte do arquivo de manifesto org.eclipse.equinox.jsp.jasper:

Como podemos observar, este manifesto contém algumas meta-informações necessárias ao container, como especificar a classe do ativador do pacote que implementa a interface BundleActivator.

O pacote representa nossa primeira peça do quebra-cabeça e aqui está uma representação simplificada de um pacote:

eclipse7

E só para ter uma ideia de todos os bundles usados ​​pelo eclipse, vamos procurar todas as classes que implementam a interface BundleActivator.

eclipse2

Quem gerencia o pacote e invoca os métodos BundleActivator?

Para descobrir isso, vamos procurar métodos que invoquem direta ou indiretamente BundleActivator.start

eclipse6

O pacote é ativado pela estrutura OSGi quando é iniciado. A classe de estrutura é iniciada pelo contêiner equinócio. E para entender melhor o que acontece quando o container inicia, aqui estão algumas ações executadas quando o container é lançado:

eclipse31

Quando o container é lançado, ele inicializa o framework OSGi, o framework obtém todos os bundles instalados, e para cada um ele cria uma instância da classe BundleHost, e armazena em um repositório o bundle encontrado.

A classe BundleHost implementa a interface Bundle, que contém métodos como iniciar, parar, desinstalar e atualizar, esses métodos são necessários para gerenciar o ciclo de vida do pacote.

Portanto, nossa segunda peça do quebra-cabeça é o contêiner OSGi, lançado pelo Equinoxlauncher, que inicializa o framework. A classe do framework é responsável por carregar os bundles e ativá-los.

Depois de descobrir alguns conceitos básicos sobre bundle e container, vamos nos aprofundar nos bundles e descobrir como eles funcionam internamente.

Vamos pegar como exemplo o pacote org.eclipse.equinox.http.servlet e procurar métodos invocados pelo método start de sua classe Activator.

eclipse1

Esse pacote cria um serviço e o registra no contêiner. Um serviço em OSGi é definido por uma classe ou interface Java padrão. Normalmente, uma interface Java é usada para definir a interface de serviço. O serviço é o método preferencial que os bundles devem usar para se comunicarem entre si.

Aqui estão alguns cenários úteis de uso de serviços:

  • Exporte a funcionalidade de um pacote para outros pacotes.
  • Importe a funcionalidade de outros pacotes.
  • Registre ouvintes para eventos de outros bundles.

Outra observação do gráfico de dependência anterior é que uma fábrica de serviço é usada para criar a instância de serviço.

Nossa terceira peça do quebra-cabeça é a camada de serviços OSGi, cada pacote pode usar ou declarar alguns serviços, o que reforça a abordagem de design de componentes, aqui está a nova representação do pacote OSGi.

eclipse8

Se o bundle usa serviços para se comunicar com outros bundles, como ele se comunica com outros jars?

Se desenvolvermos um bundle e tentarmos usar uma classe de outro jar, podemos nos surpreender que não funcionará como esperado, o motivo é que o ClassLoader está enganchado pelo container OSGi, para verificar isso vamos pesquisar qual método invocar java. lang.Thread.setContextClassLoader.

eclipse9

Muitos métodos o invocam, incluindo o EquinoxLauncher. então toda vez que o bundle tentar criar uma instância de classe, o container OSGi irá verificar se o código tem permissão para fazer esta ação ou não, e aí vem o papel do pacote importado e exportado no arquivo manifest.

O pacote declara explicitamente o pacote exportado e importado, e para verificar isso, vamos procurar os pacotes usados ​​pelo pacote org.eclipse.equinox.http.servlet e verificar se ele usa apenas o pacote importado.

eclipse10

Como podemos observar todos os pacotes utilizados estão especificados no arquivo manifest, na seção import package.
O pacote exportado, no entanto, representa o pacote que pode ser usado de outros bundles. ele é representado pela interface ExportedPackage, e podemos pesquisar as classes de contêiner usando essa interface.

eclipse12

O interessante dessa nova capacidade é que o bundle terá um limite bem definido, e o que ele usa e o que expõe como serviços é muito bem especificado.

Podemos impor a verificação de uso de pacotes importados usando outras ferramentas, por exemplo com CQLinq podemos escrever algumas regras avisando cada vez que um projeto usar pacotes diferentes dos especificados, mas é melhor fazer essa verificação no ambiente de execução, para que o desenvolvedor não pode quebrar essas regras.

Essa capacidade de tratar pacotes importados e exportados é gerenciada pela camada de módulos OSGi e foi nossa quarta peça do quebra-cabeça.

Vamos voltar ao container OSGi e descobrir quais serviços ele oferece?

Serviços de contêiner

Como descobrimos antes do container ser lançado pela classe EquinoxLauncher e a classe do framework inicializar e lançar os pacotes, para detectar quais serviços são fornecidos vamos procurar todas as classes usadas no método de inicialização do framework.

eclipse11

Algumas classes já foram descobertas antes, como BundleRepository, BundleHost, PackageAdminImpl e ServiceRegistry.

E as outras aulas:

  • StartLevelManager:
    Cada pacote configurável OSGi está associado a um nível de início que permite ao servidor controlar a ordem relativa de início e parada dos pacotes configuráveis. Somente os bundles que possuem um nível inicial menor ou igual ao nível inicial ativo da estrutura do servidor devem estar ativos. Normalmente, um pacote com um nível inicial menor tende a ser iniciado mais cedo.
  • Administrador de Segurança:
    A camada de segurança lida com os aspectos de segurança limitando a funcionalidade do pacote a recursos predefinidos.
  • Gerente de eventos:
    O serviço Event Admin fornece um modelo de publicação-assinatura para manipulação de eventos. Ele é realizado de acordo com a Especificação do Serviço de Administração de Eventos OSGi. O serviço de Administração de Eventos despacha eventos entre Publicadores de Eventos e Assinantes de Eventos (Manipuladores de Eventos) interpondo um canal de eventos. Os publicadores publicam eventos no canal e o canal de eventos define quais manipuladores precisam ser notificados. Assim, editores e manipuladores não têm conhecimento direto um do outro, o que simplifica o gerenciamento de eventos.

A imagem completa do OSGi

Vamos montar todas as peças do quebra-cabeça descritas anteriormente e teremos a seguinte imagem OSGi:
camadas-osgi

Essa arquitetura tem os seguintes benefícios interessantes:

  • Simples.
  • Complexidade Reduzida.
  • Fácil implantação.
  • Seguro.

O que o torna muito atraente e merece um desvio, e você não perderá seu tempo se o estudar a fundo.