Há diversos requisitos para desenvolver sistemas modulares. No mundo Java, assim como em muitos outros campos, a Open Services Gateway initiative (OSGi) foi reconhecida como uma estrutura madura para sistemas modulares, o que inclui aplicativos de desktop, aplicativos da web, aplicativos móveis e middleware. OSGi oferece uma infraestrutura subjacente para desenvolver aplicativos modulares, dinâmicos e orientados a serviço. A maioria dos recursos e capacidades são definidos e oferecidos por meio de serviços na especificação e implementação OSGi. Entendendo os conceitos e uso dos diversos serviços OSGi principais, podemos utilizar esses serviços, bem como o IDE Eclipse, para desenvolver aplicativos modulares leves que atendam requisitos complicados.
O aplicativo de amostra é um coletor de dados que recupera dados de diferentes tipos de origens e os analisa em um formato de dados unificado e predefinido para processamento posterior. Há diversos sistemas com definições bem diferentes de formato de dados e maneiras de recuperar dados. Em um exemplo, o produtor ou proprietário do aplicativo espera que fornecedores terceiros implementem lógica de processo para origens de dados particulares com base em API publicada pelo aplicativo. O ideal é que o cliente coletor integre com código de terceiros e o execute sem qualquer mudança no código existente, na configuração ou na estrutura de implementação. Esse é um requisito comum ao desenvolver um sistema modular, e nós apresentaremos como conseguir isso com serviços principais OSGi.
Para obter o potencial completo de OSGi, é necessário design cuidadoso da arquitetura do aplicativo, embora isso não seja complicado. Muitos artigos foram publicados sobre os princípios de design de aplicativos OSGi, e eu recomendo que os leia. (Consulte a seção Recursos.)
A Figura 1 mostra a arquitetura de um aplicativo coletor de dados de amostra. Esse aplicativo é formado por três tipos de pacotes configuráveis: o pacote da estrutura do coletor de dados, os pacotes de analisador de texto e os pacotes de coletor.
Figura 1. Arquitetura do aplicativo coletor de dados de amostra
O pacote configurável da estrutura age como o componente principal do aplicativo inteiro. É um pacote configurável (fornecendo APIs de UI de coletor e assistente para outros pacotes), ou é um cliente (consumindo serviços de outros pacotes funcionais). Ao fundir pacotes configuráveis de API e cliente em um único pacote, outros desenvolvedores de coletores de dados podem importar um número mínimo de jars de pacote configurável em seu IDE para implementar APIs, e em seguida executar e testar o aplicativo inteiro no Eclipse. Pacotes configuráveis de coletor fornecem serviços de coleta de dados e serviços de página do assistente. Por outro lado, pacotes configuráveis também agem como um cliente, chamando a API do analisador de texto fornecida pelo pacote configurável da estrutura, para a qual um pacote configurável analisador separado irá fornecer o serviço.
Todos esses pacotes configuráveis estão fracamente acoplados e interagem uns com os outros por meio de serviços principais OSGi. Como resultado, todos os pacotes configuráveis, exceto o pacote da estrutura, poderiam ser incluídos, removidos, suspensos ou atualizados facilmente mesmo após o aplicativo ser implementado. Esse é um recurso importante para um processo de entrega de software automatizado.
Vamos demonstrar o processo e as práticas para implementar um aplicativo OSGi pequeno, porém completamente formado.
Definir o layout do projeto OSGi
Precisamos criar um novo Projeto de Plug-in para cada pacote configurável no Eclipse. As etapas abaixo consideram que o leitor tem experiência anterior ou conhecimento sobre criação de pacotes configuráveis OSGi no Eclipse. Além disso, não deixe de fazer download do release mais recente do IDE Eclipse e de um Equinox SDK separado. Recomenda-se Eclipse 3.4 ou superior.
Como descrito para a Figura 1, ao menos três projetos de pacote configurável são necessários - o pacote da estrutura, um pacote de analisador de texto e um pacote de coletor de dados. Definir o layout do projeto, como design de hierarquia de pacote e pasta, é muito importante. Vamos tomar o projeto de pacote configurável de estrutura como exemplo na Figura 2.
Figura 2. Layout de projeto de amostra
A pasta OSGI-INF é criada para armazenar todos os arquivos de definição de componente OSGi nesse pacote configurável. (O conceito de componente será introduzido posteriormente.) Pacotes são criados de acordo com categorias funcionais. Precisamos planejar pacotes que sejam visíveis para outros pacotes configuráveis no estágio inicial, para evitar que os pacotes configuráveis acoplem pesadamente. No pacote configurável da estrutura de amostra, os dois pacotes a seguir são exportados.
-
dw.sample.dc.api– contém todas as APIs que serão implementadas. -
dw.sample.dc.consts– contém constantes e enums compartilhados entre pacotes configuráveis, tais como parâmetros de evento.
Injetar Declarative Services de OSGi em serviços
Modularidade e orientado a serviço são conceitos importantes nas especificações de OSGi. A exposição de classes de implementação é suportada pela estrutura OSGi, mas não é recomendada. Em vez disso, da perspectiva da programação, espera-se que pacotes configuráveis interajam uns com os outros por meio da publicação e do consumo de serviços ou instâncias de classes. OSGi fornece duas abordagens para isso:
- Registrar e recuperar serviços no registro de serviço por meio do objeto
BundleContextfornecido pela camada de serviço OSGi. - Alavancar os Declarative Services na especificação OSGi R4.
Declarative Services (DS) declara o conceito de Modelo de Componente. Um componente é um objeto Java definido com um arquivo de definição de componente, que é usado para fornecer serviços e recuperar serviços de referência.
O ciclo de vida de componentes é gerenciado pelo contêiner OSGi. Além disso, instâncias de serviço de referência são injetadas dinamicamente em componentes. DS lida com a consulta de serviços, ligando ou desligando ao componente de acordo com a estratégia predefinida. Como resultado, desenvolvedores podem produzir um sistema altamente dinâmico com um código organizado.
Criar o primeiro componente de serviço
Vamos começar com um caso simples: o pacote configurável da estrutura de coletor de dados define uma API de analisador de texto, e um pacote configurável de analisador de texto fornece o serviço de analisador padrão.
No pacote configurável de estrutura, defina a API de serviço no pacote dw.sample.dc.api exportado. Consulte a Listagem 1.
Listagem 1. API de analisador de texto
public interface ITextParser {
public String parseText(String input);
}
|
No projeto de pacote configurável do analisador de texto, o pacote dw.sample.dc.api precisa ser primeiro importado no manifesto, para garantir que a classe de interface possa ser carregada pelo carregador de classe de pacote configurável atual. Em segundo lugar, crie uma classe para implementar a interface acima.
A última e mais importante etapa é definir a classe de implementação como um componente, o que exige criar um arquivo de definição de componente e registrá-lo no manifesto de pacote configurável. Para simplificar o processo, vamos usar o assistente fornecido pelo Eclipse (pode não estar disponível em releases antigos):
- Clique com o botão direito na pasta OSGI-INF no pacote configurável do analisador e selecione
New>Component Definition. - Especifique o arquivo de classe de implementação recém-criado como a classe de componente, bem como o arquivo de definição e nomes de componente, e clique em Finish.
- Na janela de configuração gráfica da definição de componente, passe para a guia Services e especifique a classe de interface de serviço do pacote configurável de estrutura como sendo o serviço fornecido desse componente. Consulte a Figura 3. abaixo.
Figura 3. Especifique os serviços fornecidos
O assistente irá gerar um novo textParserComponent.xml e registrar o componente no manifesto do pacote configurável, como mostra a Listagem 2.
Listagem 2. Configuração do novo componente gerado
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
name="dw.sample.textparser.defaultparser">
<implementation class="dw.sample.textparser.service.DataCollectorDefaultTextParser"/>
<service>
<provide interface="dw.sample.dc.api.ITextParser"/>
</service>
</scr:component>
MANIFEST.MF
...
Service-Component: OSGI-INF/textParserComponent.xml
|
Nós acabamos de implementar um componente simples, porém completo, que fornece serviço no tempo de execução quando um cliente chama a API do pacote configurável de estrutura.
Em seguida, iremos demonstrar um componente mais complicado e mostrar como consumir serviços de outros componentes.
Vamos considerar um cenário no qual usuários devem interagir com o aplicativo coletor de dados por meio de GUI, o que significa que eles podem inserir os dados necessários após uma série de páginas de assistente. Em seguida, eles iniciam o processo de coleta e veem o progresso na UI. Obviamente a estrutura não sabe quais dados são requeridos por coletores de dados de terceiros. Para atender o requisito, os pacotes configuráveis de coletor de dados também devem fornecer ao menos uma página do assistente de configuração. Também é necessário que a estrutura integre e apresente todos os assistentes de terceiros de forma integrada e dinâmica após o aplicativo ser implementado no ambiente do usuário. Parece legal, não é? Vamos ver como fazer isso alavancando o poder de Declarative Services de OSGi. (Consulte a Figura 4.)
Figura 4. Sequência de assistente dinâmico
Como no caso anterior, é preciso definir uma API. Para esse caso, precisamos projetar cuidadosamente a interface para evitar aumentar a complexidade do sistema de assistente, e para assegurar que a API seja capaz de cobrir requisitos funcionais importantes no lado do coletor de dados. Portanto o design da API precisa satisfazer ao menos o seguinte:
- Não permitir qualquer comunicação e dependência entre serviços de assistente de diferentes coletores de dados, mas não restringir isso naqueles dentro do mesmo coletor de dados.
- Salvar a entrada do usuário no estágio em que o usuário sai da última página do assistente de um coletor de dados.
Essa é a etapa mais importante neste caso. Um design de API bem formado deve fornecer interfaces claras enquanto mantém um serviço altamente coerente, que não se preocupa com a lógica e a complexidade do código dentro de uma implementação.
SWT e JFace são usados como bibliotecas subjacentes do assistente de aplicativo, bem como outros componentes de GUI. Se você planejar usar recursos e classes entre diversos pacotes nessas duas bibliotecas, basta incluí-las como o pacote configurável necessário nos arquivos de manifesto dos pacotes configuráveis de estrutura e coletor de dados. Além disso, importe o pacote org.osgi.service.component, pois iremos chamar classes nesse pacote mais tarde.
A API de assistente definida no pacote configurável de estrutura é formada por duas classes, como mostra a Listagem 3.
Listagem 3. Defina a API de serviço de assistente
public interface ICollectorWizardGenerator {
public List<CollectorWizardPage> getWizardSequence();
public int getWizardPositionIndex();
}
---
/**
* Inherit the jface WizardPage by adding two customized methods
*/
public abstract class CollectorWizardPage extends WizardPage {
…
/**
* Trigger the customized logic when the next button on the wizard is
* pressed
*
* @return whether to jump to next page.
*/
public abstract boolean nextPressedTrigger();
/**
* Save user input in the current wizard.
*/
public abstract void saveWizardInput();
}
|
No pacote configurável de coletor de dados, implemente a API de assistente e defina a classe que implementa a interface ICollectorWizardGenerator como o componente de serviço. (Consulte a Listagem 4.)
Listagem 4. O componente de serviço de assistente
public class ProfileWizardGeneratorImpl implements ICollectorWizardGenerator {
private int wizardPosition = 10;
protected void activate(ComponentContext context) {
try {
wizardPosition = ((Integer) context.getProperties().get(
"wizardPosition")).intValue();
} catch (NumberFormatException e) {
wizardPosition = 10;
}
}
public List<CollectorWizardPage> getWizardSequence() {
List<CollectorWizardPage> wizardPageSeq = new ArrayList<CollectorWizardPage>();
wizardPageSeq.add(new ProfileCollectorWizardPage1());
wizardPageSeq.add(new ProfileCollectorWizardPage2());
return wizardPageSeq;
}
public int getWizardPositionIndex() {
return wizardPosition;
}
}
---
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
name="dw.sample.dc.profile.wizardgenerator">
<implementation class="dw.sample.dc.profile.service.ProfileWizardGeneratorImpl"/>
<service>
<provide interface="dw.sample.dc.api.ICollectorWizardGenerator"/>
</service>
<property name="wizardPosition" type="Integer" value="2"/>
</scr:component>
|
Observe que o método activate é substituído na classe de componente. O método, assim como o método deactivate, é chamado pela estrutura OSGi quando o componente é ativado ou desativado. O objeto ComponentContext é passado para o método activate para que possamos obter informações de componente, como propriedades de componente, definidos no arquivo XML. Aqui o coletor de dados pode definir o índice de posição de assistente, assim o pacote configurável de estrutura poderia arranjar a sequência de aparecimento de assistentes de acordo.
Até agora nós criamos dois componentes como provedores de serviços.
No modelo Declarative Services, serviços são injetados dinamicamente em componentes. O código na Listagem 5. abaixo demonstra como referenciar serviços e consumi-los no pacote configurável.
Listagem 5. O componente de carregador de serviço do analisador
public class TextParserLoader {
private static TextParserLoader instance;
... ...
private ITextParser parser;
public static TextParserLoader getInstance() {
return instance;
}
protected void activate(ComponentContext context) {
try {
locker.lock();
instance = this;
} finally {
locker.unlock();
}
}
public void setTextParser(ITextParser parser) {
this.parser = parser;
}
public void unsetTextParser(ITextParser parser) {
if (this.parser != parser) {
return;
}
this.parser = null;
}
public ITextParser getTextParser() {
return parser;
}
}
---
<implementation class="dw.sample.dc.profile.service.TextParserLoader"/>
<reference bind="setTextParser" cardinality="1..1"
interface="dw.sample.dc.api.ITextParser" name="ITextParser"
policy="dynamic" unbind="unsetTextParser"/>
|
Aqui o padrão do singleton é aplicado à classe do componente. Outros objetos não componentes no pacote configurável são capazes de obter e consumir serviços da instância exclusiva TextParserLoader e livrar-se se código específico de OSGi. A classe de carregador age, de alguma forma, da maneira como uma classe de factory em aplicativos Java tradicionais, mas a mágica é que ela não precisa lidar com as complicações do carregamento de classe, reflexo e iniciação de instância.
DS suporta diferentes estratégias de referência de serviço definidas no arquivo XML de componente:
- Política: A propriedade
policytem dois valores - estático e dinâmico. A política estática é o padrão, o que significa que o componente será reativado após os serviços de referência serem alterados. Por sua vez, a política dinâmica apenas chama os métodos predefinidos de ligação e desligamento, por isso é mais bem recomendada. - Cardinalidade: A propriedade tem quatro valores disponíveis: 1..1, 1..n, 0..1, 0..n. Dado que pode haver diversos serviços disponíveis para a mesma API, o número final indica se todos os serviços serão injetados no componente ou se apenas um será, e o número inicial indica se serviços disponíveis são obrigatórios para ativar o componente.
A cardinalidade 1..1 é o valor padrão, e nós vimos como ela foi usada no exemplo acima. Para o caso 0..n, ele também pode ser bem manipulado juntamente com o padrão de singleton, como mostra a Listagem 6.
Listagem 6. Injetar diversos serviços
public class DataCollectorWizardSequence {
...
private List<ICollectorWizardGenerator> wizardGeneratorSeq =
new ArrayList<ICollectorWizardGenerator>();
...
public void addCollectorWizardGenerator(ICollectorWizardGenerator wizardGenerator) {
wizardGeneratorSeq.add(wizardGenerator);
}
public void removeCollectorWizardGenerator(ICollectorWizardGenerator wizardGenerator)
{
wizardGeneratorSeq.remove(wizardGenerator);
}
public List<CollectorWizardPage> getDataCollectorWizardSequence() {
List<CollectorWizardPage> collectorWizardSequence =
new ArrayList<CollectorWizardPage>();
try {
locker.lock();
/*
* Arrange the wizard sequence according to position indexes
* defined by each wizard services
*/
Collections.sort(wizardGeneratorSeq,
new CollectorWizardSequenceComparator());
for (ICollectorWizardGenerator generator : wizardGeneratorSeq) {
collectorWizardSequence.addAll(generator.getWizardSequence());
}
return collectorWizardSequence;
}
finally {
locker.unlock();
}
}
}
---
<reference bind="addCollectorWizardGenerator" cardinality="0..n"
interface="dw.sample.dc.api.ICollectorWizardGenerator"
name="ICollectorWizardGenerator" policy="dynamic"
unbind="removeCollectorWizardGenerator"/>
|
Agora que entendemos como alavancar componentes introduzidos em Declarative Services para publicar e consumir serviços, vamos aprender a fazer uso efetivo de outros serviços principais de OSGi com este modelo.
Gerenciar configuração de pacote configurável com o Configuration Admin Service
O Configuration Admin Service fornece uma abordagem unificada para que aplicativos OSGi gerenciem dados de configuração locais ou remotos, no nível de serviço ou no nível de pacote configurável. Começaremos com o caso mais comum - armazenar, recuperar e atualizar dados de configuração de pacote configurável localmente.
Primeiro, verifique se org.eclipse.equinox.cm_<version>.jar está na pasta de plugins do Eclipse; essa é a implementação Equinox para o Configuration Admin Service de OSGi. Caso não esteja, coloque o arquivo transferido por download em separado do Equinox SDK nessa pasta. Em seguida, importe o pacote org.eclipse.equinox.cm no manifesto do pacote configurável de destino.
Para usar o Configuration Admin Service, o primeiro e mais importante requisito é que a API ManagedService esteja implementada e publicada como um serviço pelo pacote configurável de destino. Há apenas um método updated a ser implementado. Diferentes ManagedServices são diferenciados por SERVICE_PID registrado com os serviços, portanto o serviço correspondente será acionado quando sua configuração for alterada. Para gerenciar a configuração no nível do pacote configurável, um objeto apropriado para fornecer ManagedService é o ativador de pacote configurável na Listagem 7.
Listagem 7. O ativador de pacote configurável que fornece ManagedService
public class Activator implements BundleActivator {
private ServiceRegistration cmSvrReg;
public void start(BundleContext context) throws Exception {
cmSvrReg = context.registerService(ManagedService.class.getName(),
this, initMgrServiceProp());
}
public void stop(BundleContext context) throws Exception {
cmSvrReg.unregister();
}
public void updated(Dictionary properties) throws ConfigurationException {
if (cmSvrReg == null) {
return;
}
if (properties == null) {
cmSvrReg.setProperties(initMgrServiceProp());
} else {
cmSvrReg.setProperties(properties);
}
}
public static Dictionary initMgrServiceProp() {
Dictionary result = new Hashtable();
// Suppose that the Activator class name is unique across bundles…
result.put(Constants.SERVICE_PID, Activator.class.getName());
return result;
}
}
|
A seguir, como mostra a Listagem 8, crie um componente que carrega o Configuration Admin Service publicado pela estrutura OSGi.
Listagem 8. O componente ConfigManager
public class DCFrameworkConfigManager {
private ConfigurationAdmin cm;
...
protected void activate(ComponentContext context) {
...
Configuration config = getFrameworkConfig();
try {
writeLock.lock();
if (config.getProperties() == null) {
config.update(context.getProperties());
}
} finally {
writeLock.unlock();
}
}
public void setConfigAdmin(ConfigurationAdmin cm) {
this.cm = cm;
}
public void unsetConfigAdmin(ConfigurationAdmin cm) {
this.cm = null;
}
public Configuration getFrameworkConfig() throws IOException {
try {
readLock.lock();
return cm.getConfiguration(Activator.class.getName());
} finally {
readLock.unlock();
}
}
public void updateFrameworkConfig(Dictionary<String, String> properties)
throws IOException {
try {
writeLock.lock();
Configuration config = getFrameworkConfig();
if (config != null) {
config.update(properties);
}
} finally {
writeLock.unlock();
}
}
}
---
<reference bind="setConfigAdmin" cardinality="1..1"
interface="org.osgi.service.cm.ConfigurationAdmin" name="ConfigurationAdmin"
policy="dynamic" unbind="unsetConfigAdmin"/>
<properties entry="OSGI-INF/initConfig.properties"/>
|
Após a ativação, o componente ConfigManager inicia a configuração do pacote configurável a partir de um arquivo de propriedades e fornece métodos que podem ser usados em qualquer ponto dentro do pacote para ler e salvar a configuração a qualquer momento. Eles são protegidos por bloqueios para lidar com o caso de chamada simultânea.
Lidar com eventos com o Event Admin Service
Para muitos sistemas, é essencial implementar um modelo de manipulação de eventos. A estrutura orientada a serviços do OSGi fornece o mecanismo de implementação por meio do Event Admin Service.
Para usar corretamente o Event Admin Service, lembre-se destas três coisas:
- O publicador de evento precisa alavancar o serviço de referência
EventAdminpara enviar o objetoEvent. - O assinante do evento fornece o serviço
EventHandlerimplementando o métodohandleEventpara lidar com eventos recebidos. - A propriedade
event.topicsdo objetoEventdetermina quais manipuladores de evento podem receber o evento.
O Event Admin Service de OSGi implementa um modelo de programação de manipulação de eventos tradicional de forma mais dinâmica, mas não introduz novos conceitos no modelo. Como outros serviços OSGi introduzidos neste artigo, nós iremos recuperar o serviço por meio de objetos componentes. Declarative Services permite que um componente seja provedor de serviço e consumidor de serviço, o que possibilita que um componente seja publicador de evento e manipulador de evento ao mesmo tempo.
Considere este requisito: usuários devem poder ver o progresso da coleta de dados na UI do aplicativo e interromper o processo a qualquer momento com o controle da UI. Para um pacote configurável de coletor de dados, isso significa tecnicamente que deve haver eventos de canal duplo enviados e manipulados nos lados do pacote configurável de estrutura e do pacote configurável de coletor de dados do aplicativo. (Consulte a Listagem 9 abaixo.)
Vamos tomar o lado do pacote configurável de coletor de dados como exemplo. Novamente, verifique primeiro a pasta de plugins do Eclipse, copie org.eclipse.equinox.event_<version>.jar
se não estiver lá, e importe o pacote no manifesto do pacote configurável.
Listagem 9. O componente publicador e manipulador de evento
public class ProfileDataCollectorImpl implements IDataCollector, EventHandler {
public final static String EVENT_TOPIC =
"dw/sample/dc/event/collector/ProfileDataCollectorImpl/status";
private EventAdmin eventAdmin;
...
public int collectAndOutput() {
while (...) {
if (isCancelled()) {
cancelProcess(false);
return -1;
}
...
publishEvent(GUIMsg.STATUS_AND_PROGRESS.flag(),
"Collecting profile data...", 0, true);
...
}
...
}
private void publishEvent(int status, String statusMsg, int progress,
boolean async) {
Dictionary<String, String> props = new Hashtable<String, String>();
props.put(FrameworkEventConsts.PARAM_STATUS, String.valueOf(status));
props.put(FrameworkEventConsts.PARAM_STATUS_MSG, statusMsg);
props.put(FrameworkEventConsts.PARAM_PROGRESS, String.valueOf(progress));
if (!async) {
eventAdmin.sendEvent(new Event(EVENT_TOPIC, props));
} else {
eventAdmin.postEvent(new Event(EVENT_TOPIC, props));
}
}
public void handleEvent(Event event) {
String action = (String) event
.getProperty(FrameworkEventConsts.PARAM_ACTION);
if (FrameworkEventConsts.VAL_CANCEL.equalsIgnoreCase(action)) {
cancelProcess(true);
}
}
...
}
---
<property name="event.topics" type="String"
value="dw/sample/dc/event/framework/UserCmdEventPublisher/uicmd"/>
<service>
<provide interface="org.osgi.service.event.EventHandler"/>
<provide interface="dw.sample.dc.api.IDataCollector"/>
</service>
<reference bind="setEventAdmin" cardinality="1..1"
interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="dynamic"
unbind="unsetEventAdmin"/>
|
Nesse exemplo, observe o formato do valor da propriedade event.topics. Além disso, podemos ver que o Event Admin Service permite que desenvolvedores definam os dados a serem transmitidos através do objeto Event.
Especificar o modelo de ativação com o Application Admin Service
Até agora nós abordamos os principais aspectos do desenvolvimento de um aplicativo OSGi, e o aplicativo de amostra está pronto para executar. Como mostra a Figura 1, um aplicativo OSGi nativo é formado por pacotes configuráveis executando acima do contêiner OSGi. Por padrão, o console OSGi aparecerá quando o aplicativo for iniciado. Não há problema se ele executar no lado do servidor. Mas se o aplicativo deve ser entregue como aplicativo cliente, surge a questão de como empacotá-lo de modo que possa comportar-se como um aplicativo moderno, ou seja, iniciar de uma forma mais "profissional" mantendo a modularidade e a natureza dinâmica internas.
Aqui nós introduzimos um método simples ao implementar o modelo de aplicativo Equinox com base no Application Admin Service de OSGi.
A primeira etapa é incluir o pacote configurável org.eclipse.equinox.app como Require-bundle no manifesto do pacote configurável de estrutura do aplicativo. Crie uma classe que implemente a interface
IApplication como na Listagem 10.
Listagem 10. Implemente a interface IApplication
public class GUIApplication implements IApplication {
public Object start(IApplicationContext context) {
// Invoke the entry GUI object here
...
return IApplication.EXIT_OK;
}
public void stop() {
// Add the logic when the application quits.
}
}
|
Em seguida, defina o pacote configurável de estrutura de aplicativo como um aplicativo Eclipse, declarando a extensão no plugin.xml, como na Listagem 11.
Listagem 11. plugin.xml
<plugin>
<extension id="GUIApp"
point="org.eclipse.core.runtime.applications">
<application cardinality="1">
<run
class="dw.sample.dc.launcher.GUIApplication">
</run>
</application>
</extension>
</plugin>
|
A cardinalidade do aplicativo especifica que ele pode ter apenas uma instância por vez. Como o pacote configurável declarou uma extensão, é necessário anexar a diretiva de singleton no cabeçalho de manifesto Bundle-Symbolic, assim:
Bundle-SymbolicName: dw.sample.dc;singleton:=true |
Em seguida, crie uma nova configuração para iniciar o aplicativo OSGi no IDE Eclipse. (Consulte a Figura 5.)
Figura 5. Crie uma nova configuração
Na guia Bundles, precisamos clicar em Add Required Bundles para incluir todas as dependências obrigatórias como pacotes configuráveis de plataforma. Na guia Arguments, precisamos incluir dois novos argumentos para ativar o modelo de aplicativo. Observe que o valor do argumento -application deve estar no formato: "bundle symbolicname"."extension id".
Quando pudermos executar o aplicativo e verificar que todas as funções funcionam como esperado no Eclipse, isso indica que concluímos o trabalho de desenvolvimento para desenvolver um aplicativo OSGi. Assim podemos exportar cada projeto de pacote configurável para um arquivo jar, implementá-lo e executar testes integrados no ambiente real, o que está fora do escopo deste artigo.
Este artigo demonstra etapas detalhadas para usar serviços principais OSGi para desenvolver e iniciar um aplicativo modular no Eclipse. Exemplos neste artigo mostram que o Modelo de Componente Orientado a Serviço incluído com Declarative Services está tendo um papel importante ao ajudar desenvolvedores a usar efetivamente outros serviços principais OSGi para melhorar a dinâmica de aplicativos modulares.
Aprender
- OSGi Alliance Specifications: Localize as especificações para todos os releases.
- Práticas recomendadas para desenvolver e trabalhar com aplicativos OSGi (developerWorks, julho de 2010) Saiba 14 práticas para trabalhar com aplicativos OSGi efetivamente.
- Documentos do Equinox: Acesse recursos do Equinox em eclipse.org.
- Demo de Modelo de Aplicativo do Equinox: Acompanhe como aplicativos Eclipse podem ser gerenciados usando a especificação Application Admin Service de OSGi.
- Artigos sobre Eclipse no developerWorks: Leia outros artigos sobre Eclipse.
- DeveloperWorks no Twitter: siga-nos para acompanhar as últimas notícias.
- Zona de software livre do developerWorks: Encontre muitas instruções, ferramentas e atualizações de projeto para ajudar a desenvolver com tecnologias de software livre e usá-las com produtos IBM.
- Eventos interessantes: confira futuras conferências, exposições e webcasts interessantes para desenvolvedores de software livre IBM.
- Podcasts do developerWorks: escute entrevistas e explicações interessantes para desenvolvedores de software
- Demos gratuitas on demand do developerWorks: Acompanhe nossas demos gratuitas e saiba mais sobre as tecnologias IBM e de software livre e funções dos produtos.
Obter produtos e tecnologias
- O Eclipse Marketplace é um portal conveniente em que é possível encontrar ofertas comerciais e de software livre relacionadas ao Eclipse.
- Eclipse e Equinox SDK: Faça download dos releases mais recentes.
Discutir
- Comunidade do developerWorks: Conecte-se a outros usuários do developerWorks enquanto explora os blogs, fóruns, grupos e wikis voltados para desenvolvedores. Ajude a desenvolver o software livre do mundo real na comunidade do developerWorks.

Sun, Yuan Tao é analista de desempenho de software para a equipe de desenvolvimento Lotus de Xangai do IBM China Software Development Lab e sua prioridade émedir e melhorar o desempenho de produtos sociais Lotus, incluindo Lotus Connections e LotusLive iNotes. Antes de fazer parte da equipe de desempenho, foi desenvolvedor de software experiente em padrão de design de software, Eclipse RCP e desenvolvimento de aplicativos Java 2 Platform, Enterprise Edition.