Introdução ao OSGi

Introdução ao OSGi

1. Introdução

Vários aplicativos de missão crítica e middleware Java têm alguns requisitos tecnológicos rígidos.

Alguns precisam oferecer suporte à implantação a quente, para não interromper os serviços em execução - e outros devem poder trabalhar com versões diferentes do mesmo pacote para oferecer suporte a sistemas legados externos.

As plataformasOSGi representam uma solução viável para suportar este tipo de requisitos.

The Open Service Gateway Initiative is a specification defining a Java-based component system. Atualmente é gerenciado porOSGi Alliance, e sua primeira versão remonta a 1999.

Desde então, provou ser um ótimo padrão para sistemas de componentes e é amplamente utilizado hoje em dia. OEclipse IDE, por exemplo, é um aplicativo baseado emOSGi.

Neste artigo, vamos explorar alguns recursos básicos deOSGi aproveitando a implementação fornecida porApache.

2. OSGi Basics

No OSGi, um único componente é chamado de pacote.

Logicamente,a bundle is a piece of functionality that has an independent lifecycle –, o que significa que pode ser iniciado, interrompido e removido independentemente.

Tecnicamente, um pacote é apenas um arquivo jar com um arquivoMANIFEST.MF contendo alguns cabeçalhos específicos do OSGi.

A plataformaOSGi fornece uma maneira de receber notificações sobre pacotes se tornando disponíveis ou quando eles são removidos da plataforma. Isso permitirá que um cliente projetado adequadamente continue trabalhando, talvez com funcionalidade degradada, mesmo quando um serviço do qual depende, estiver momentaneamente indisponível.

Por isso, um bundle deve declarar explicitamente a quais pacotes ele precisa ter acesso e a plataformaOSGi irá iniciá-lo apenas se as dependências estiverem disponíveis no próprio bundle ou em outros bundles já instalados na plataforma.

3. Obtendo as ferramentas

Começaremos nossa jornada emOSGi baixando a versão mais recente deApache Karaf dethis link. Apache Karaf é uma plataforma que executa aplicativos baseados emOSGi-; é baseado na implementação deApache da especificaçãoOSGi chamadaApache Felix.

Karaf oferece alguns recursos úteis além deFelix que nos ajudarão a nos familiarizar comOSGi, por exemplo, uma interface de linha de comando que nos permitirá interagir com a plataforma.

Para instalarKaraf, você pode seguir as instruções de instalação deofficial documentation.

4. Ponto de entrada do pacote

Para executar um aplicativo em um ambiente OSGi, temos que empacotá-lo como um pacoteOSGi e definir o ponto de entrada do aplicativo, e esse não é o métodopublic static void main(String[] args) usual.

Então, vamos começar construindo um aplicativo "Hello World" baseado emOSGi-.

Começamos configurando uma dependência simples no núcleoOSGi API:


    org.osgi
    org.osgi.core
    6.0.0
    provided

A dependência é declarada comoprovided porque estará disponível no tempo de execuçãoOSGi e o pacote não precisa incorporá-la.

Vamos agora escrever a classeHelloWorld simples:

public class HelloWorld implements BundleActivator {
    public void start(BundleContext ctx) {
        System.out.println("Hello world.");
    }
    public void stop(BundleContext bundleContext) {
        System.out.println("Goodbye world.");
    }
}

BundleActivator é uma interface fornecida porOSGi que deve ser implementada por classes que são pontos de entrada para um pacote.

O métodostart() é invocado pela plataformaOSGi quando o pacote contendo esta classe é iniciado. Por outro lado,stop() é invocado antes de o pacote ser interrompido.

Vamos ter em mente que cada pacote pode conter no máximo umBundleActivator. O objetoBundleContext fornecido para ambos os métodos permite interagir com o tempo de execuçãoOSGi. Voltaremos a ele em breve.

5. Construindo um Pacote

Vamos modificar opom.xmle torná-lo um pacote OSGi real.

Em primeiro lugar, temos que declarar explicitamente que vamos construir um pacote, não um jar:

bundle

Em seguida, aproveitamos a cortesiamaven-bundle-plugin, da comunidadeApache Felix, para empacotar a classeHelloWorld como um pacoteOSGi:


    org.apache.felix
    maven-bundle-plugin
    3.3.0
    true
    
        
            
                ${pom.groupId}.${pom.artifactId}
            
            ${pom.name}
            ${pom.version}
            
                com.example.osgi.sample.activator.HelloWorld
            
            
                com.example.osgi.sample.activator
            
        
    

Na seção de instruções, especificamos os valores dos cabeçalhosOSGi que queremos incluir no arquivo MANIFESTO do pacote.

Bundle-Activator é o nome totalmente qualificado da implementaçãoBundleActivator que será usada para iniciar e parar o pacote e se refere à classe que acabamos de escrever.

Private-Package não é um cabeçalho OSGi, mas é usado para dizer ao plug-in para incluir o pacote no pacote, mas não torná-lo disponível para outros. Agora podemos construir o pacote com o comando usualmvn clean install.

6. Instalando e executando o pacote

Vamos começarKaraf executando o comando:

/bin/karaf start

onde<KARAF_HOME> é a pasta ondeKaraf está instalado. Quando o prompt do consoleKaraf aparecer, podemos executar o seguinte comando para instalar o pacote:

> bundle:install mvn:com.example/osgi-intro-sample-activator/1.0-SNAPSHOT
Bundle ID: 63

Isso instrui o Karaf a carregar o pacote no repositório local do Maven.

Em troca, o Karaf imprime o ID numérico atribuído ao pacote que depende do número de pacotes já instalados e pode variar. Agora o pacote está instalado, podemos iniciá-lo com o seguinte comando:

> bundle:start 63
Hello World

"Hello World" aparece imediatamente assim que o pacote é iniciado. Agora podemos parar e desinstalar o pacote configurável com:

> bundle:stop 63
> bundle:uninstall 63

“Goodbye World” aparece no console, de acordo com o código no métodostop().

7. Um serviço OSGi

Vamos continuar escrevendo um serviçoOSGi simples, uma interface que expõe um método para saudar as pessoas:

package com.example.osgi.sample.service.definition;
public interface Greeter {
    public String sayHiTo(String name);
}

Vamos escrever uma implementação dele que também éBundleActivator, então seremos capazes de instanciar o serviço e registrá-lo na plataforma quando o pacote for iniciado:

package com.example.osgi.sample.service.implementation;
public class GreeterImpl implements Greeter, BundleActivator {

    private ServiceReference reference;
    private ServiceRegistration registration;

    @Override
    public String sayHiTo(String name) {
        return "Hello " + name;
    }

    @Override
    public void start(BundleContext context) throws Exception {
        System.out.println("Registering service.");
        registration = context.registerService(
          Greeter.class,
          new GreeterImpl(),
          new Hashtable());
        reference = registration
          .getReference();
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Unregistering service.");
        registration.unregister();
    }
}

Usamos oBundleContext como meio de solicitar à plataformaOSGi o registro de uma nova instância do serviço.

Devemos também fornecer o tipo de serviço e um mapa dos parâmetros de configuração possíveis, que não são necessários em nosso cenário simples. Vamos agora prosseguir com a configuração domaven-bundle-plugin:


    org.apache.felix
    maven-bundle-plugin
    true
    
        
            
                ${project.groupId}.${project.artifactId}
            
            
                ${project.artifactId}
            
            
                ${project.version}
            
            
                com.example.osgi.sample.service.implementation.GreeterImpl
            
            
                com.example.osgi.sample.service.implementation
            
            
                com.example.osgi.sample.service.definition
            
        
    

É importante notar que apenas o pacotecom.example.osgi.sample.service.definition foi exportado desta vez, por meio do cabeçalhoExport-Package.

Graças a isso,OSGi permitirá que outros pacotes invoquem apenas os métodos especificados na interface de serviço. O pacotecom.example.osgi.sample.service.implementation está marcado como privado, portanto, nenhum outro pacote poderá acessar os membros da implementação diretamente.

8. Um cliente OSGi

Vamos agora escrever o cliente. Ele simplesmente consulta o serviço na inicialização e o invoca:

public class Client implements BundleActivator, ServiceListener {
}

Vamos implementar o métodoBundleActivator start():

private BundleContext ctx;
private ServiceReference serviceReference;

public void start(BundleContext ctx) {
    this.ctx = ctx;
    try {
        ctx.addServiceListener(
          this, "(objectclass=" + Greeter.class.getName() + ")");
    } catch (InvalidSyntaxException ise) {
        ise.printStackTrace();
    }
}

O métodoaddServiceListener() permite ao cliente solicitar à plataforma o envio de notificações sobre o serviço que está em conformidade com a expressão fornecida.

A expressão usa uma sintaxe semelhante à do LDAP e, em nosso caso, estamos solicitando notificações sobre um serviçoGreeter.

Vamos continuar com o método de retorno de chamada:

public void serviceChanged(ServiceEvent serviceEvent) {
    int type = serviceEvent.getType();
    switch (type){
        case(ServiceEvent.REGISTERED):
            System.out.println("Notification of service registered.");
            serviceReference = serviceEvent
              .getServiceReference();
            Greeter service = (Greeter)(ctx.getService(serviceReference));
            System.out.println( service.sayHiTo("John") );
            break;
        case(ServiceEvent.UNREGISTERING):
            System.out.println("Notification of service unregistered.");
            ctx.ungetService(serviceEvent.getServiceReference());
            break;
        default:
            break;
    }
}

Quando ocorre alguma modificação envolvendo o serviçoGreeter, o método é notificado.

Quando o serviço é registrado na plataforma, obtemos uma referência a ele, o armazenamos localmente e o usamos para adquirir o objeto de serviço e invocá-lo.

Quando o servidor não é mais registrado, usamos a referência armazenada anteriormente para desmarcá-la, o que significa que informamos à plataforma que não a usaremos mais.

Agora só precisamos escrever o métodostop():

public void stop(BundleContext bundleContext) {
    if(serviceReference != null) {
        ctx.ungetService(serviceReference);
    }
}

Aqui, novamente, desassociamos o serviço para cobrir o caso em que o cliente está parado antes que o serviço esteja sendo parado. Vamos dar uma olhada final nas dependências empom.xml:


    com.example
    osgi-intro-sample-service
    1.0-SNAPSHOT
    provided


    org.osgi
    org.osgi.core
    6.0.0

9. Cliente e serviço

Vamos agora instalar os pacotes de cliente e serviço no Karaf fazendo:

> install mvn:com.example/osgi-intro-sample-service/1.0-SNAPSHOT
Bundle ID: 64
> install mvn:com.example/osgi-intro-sample-client/1.0-SNAPSHOT
Bundle ID: 65

Lembre-se sempre de que os números de identificação atribuídos a cada pacote podem variar.

Vamos agora iniciar o pacote do cliente:

> start 65

Portanto, nada acontece porque o cliente está ativo e está aguardando o serviço, com o qual podemos começar:

> start 64
Registering service.
Service registered.
Hello John

O que acontece é que assim que o BundleActivator do serviço é iniciado, o serviço é registrado na plataforma. Isso, por sua vez, notifica o cliente de que o serviço que estava esperando está disponível.

O cliente obtém uma referência ao serviço e o usa para chamar a implementação entregue por meio do pacote de serviços.

10. Conclusão

Neste artigo, exploramos os recursos essenciais do OSGi com um exemplo simples de que é o suficiente para entender o potencial do OSGi.

Concluindo, sempre que temos que garantir que um único aplicativo precise ser atualizado sem qualquer desserviço, o OSGi pode ser uma solução viável.

O código desta postagem pode ser encontradoover on GitHub.