A estrutura OSGi, que fornece excelentes mecanismos para desenvolvimento de aplicativos modulares e dinâmicos, tem crescido em popularidade. As recentes especificações do OSGi Service Platform Release 4 V4.2 introduziram uma especificação chamada Blueprint Container.
A especificação Blueprint Container define uma estrutura de injeção de dependência para o OSGi. Ela é projetada para lidar com a natureza dinâmica do OSGi, na qual os serviços podem ser disponibilizados e indisponibilizados a qualquer momento. A especificação também é projetada para funcionar com objetos Java simples antigos (POJOs) para que os mesmos objetos possam ser usados dentro e fora da estrutura OSGi. Os arquivos XML de Blueprint que definem e descrevem os vários componentes de um aplicativo são fundamentais para o modelo de programação Blueprint. A especificação descreve como os componentes são instanciados e conectados entre si para formar um aplicativo em execução.
A especificação Blueprint Container usa um padrão de extensor por meio do qual um pacote configurável do extensor monitora o estado dos pacotes configuráveis na estrutura e executa ações em nome desses pacotes configuráveis com base em seu estado. O pacote configurável do extensor do Blueprint aguarda a ativação dos pacotes configuráveis e verifica se eles são pacotes configuráveis do Blueprint. Um pacote configurável é considerado um pacote configurável do Blueprint quando contém um ou mais arquivos XML de Blueprint. Esses arquivos XML estão em um local fixo sob o diretório OSGI-INF/blueprint/ ou são especificados explicitamente no cabeçalho do manifesto
Bundle-Blueprint .
Depois que o extensor determina que um pacote configurável é um pacote configurável do Blueprint, ele cria um Blueprint Container em nome desse pacote configurável. O Blueprint Container é responsável por:
- Analisar os arquivos XML de Blueprint
- Instanciar
- Conectar os componentes
Durante a inicialização, o Blueprint Container certifica-se de que as referências de serviço obrigatórias sejam satisfeitas, registra todos os serviços no registro de serviço e cria instâncias do componente inicial. O pacote configurável do extensor do Blueprint também destrói o Blueprint Container para um pacote configurável quando o pacote configurável é interrompido.
Este artigo foca o Blueprint XML. Saiba, com vários exemplos, sobre as definições XML do componente e seu uso.
O arquivo XML de Blueprint é identificado por um elemento de nível superior
blueprint , como mostrado na Lista 1.
Lista 1. Fragmento do arquivo XML de Blueprint
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns=”http://www.osgi.org/xmlns/blueprint/v1.0.0”>
...
</blueprint>
|
O arquivo XML de Blueprint contém definições de vários gerenciadores de componente. A especificação Blueprint Container define quatro gerenciadores de componente principais: um gerenciador de bean, um gerenciador de serviço e dois gerenciadores de referência de serviço. Cada gerenciador é responsável por criar e gerenciar o ciclo de vida dos componentes que eles criam. Quando solicitados, os gerenciadores fornecem uma instância do componente. Cada gerenciador tem um elemento XML correspondente que descreve as propriedades do gerenciador. Os gerenciadores podem ser gerenciadores de nível superior ou sequenciais dentro de outras definições do gerenciador. Os gerenciadores também compartilham alguns atributos comuns.
id- Define o ID do gerenciador. O atributo
idé opcional. Se ele não for especificado, um ID exclusivo será automaticamente gerado e designado a um gerenciador de nível superior. Os gerenciadores sequenciais são considerados anônimos e a eles não é permitida a configuração do atributoid. Os IDs de gerenciador devem ser exclusivos para gerenciadores de nível superior dentro do Blueprint Container. Os IDs são usados para que os gerenciadores possam fazer referência aos outros. Por exemplo, durante a injeção, o gerenciador pedirá aos gerenciadores referenciados para fornecerem um objeto que será injetado no componente que o gerenciador está criando. ativação- Este atributo opcional define o modo de ativação para o gerenciador. Dois modos de ativação são suportados:
eager, em que o gerenciador é ativado durante a inicialização do Blueprint Container.lazy, em que o gerenciador é ativado sob demanda.
eageré assumido. Porém, o modo de ativação padrão para todos os gerenciadores dentro do arquivo XML de Blueprint pode ser alterado configurando-se o atributodefault-activationno elementoblueprint. Um gerenciador é ativado quando for solicitado o fornecimento de sua primeira instância do componente. Um gerenciador é desativado quando o Blueprint Container estiver sendo destruído. Cada gerenciador tem suas próprias etapas de ativação e desativação. -
dependsOn - Este atributo opcional especifica uma lista de IDs do gerenciador. Os gerenciadores listados serão ativados primeiro, antes que o gerenciador seja ativado. Um gerenciador pode ter dependências explícitas e implícitas. O atributo
dependsOndefine as dependências explícitas. As dependências implícitas são definidas pelas referências a outros gerenciadores dentro de uma definição de gerenciador.
Um gerenciador de bean cria uma instância de um objeto Java com os argumentos e propriedades fornecidos. Um gerenciador de bean pode criar instâncias de objeto únicas ou múltiplas dependendo das configurações do escopo. Ele também pode gerenciar o ciclo de vida de um objeto e notificá-lo quando todas as propriedades tiverem sido injetadas ou quando ele estiver sendo destruído.
No XML de Blueprint, um elemento bean define um gerenciador de bean. Os argumentos usados para a construção do objeto são especificados pelos elementos argument ; as propriedades injetadas são especificadas pelos subelementos property .
Durante a construção do objeto, o Blueprint Container deve primeiro localizar o construtor correto ou um factory method com um conjunto compatível de parâmetros que corresponda aos argumentos especificados no XML. Por padrão, o Blueprint Container usa o número e ordem dos elementos argument no XML para localizar o construtor ou método correto. Se os elementos argument
não puderem ser mapeados para os parâmetros na ordem em que estão, o Blueprint Container tentará reordenar os elementos
argument e localizar a disposição que melhor se ajuste.
Para ajudar o Blueprint Container a escolher o construtor, método ou disposição de parâmetro correto, atributos adicionais, como index ou typepodem ser especificados no elemento argument . Por exemplo, o atributo type especifica um nome de classe usado para corresponder ao elemento argument para um parâmetro pelo tipo exato.
O elemento property especifica o nome e valor da propriedade para injetar. O nome da propriedade corresponde a um nome de método setter na classe Java. Por exemplo, se o nome da propriedade for
foo, o método setter correspondente é setFoo(arg). Os nomes de propriedade e nomes de método setter correspondentes seguem o padrão de design para propriedades conforme definido na especificação JavaBeans.
O valor para os elementos argument e property pode ser especificado usando um atributo value ou ref , ou ele pode ser sequencial. O atributo ref especifica o ID de um gerenciador de nível superior e é usado para obter um objeto do gerenciador referenciado como o valor do argumento ou da propriedade. O valor sequencial pode ser qualquer um dos valores XML descritos na seção "Valores de Objeto".
Um bean pode ser construído de três maneiras usando:
- Um construtor de classe
- Um factory method estático
- Um factory method de instância
As Listas 2, 3 e 4 demonstram três maneiras de construir objetos Java. Cada lista mostra uma classe Java parcial e o extrato XML de Blueprint correspondente que direciona a criação do objeto.
A Lista 2 mostra um exemplo de criação de construtor simples. Neste exemplo, o atributo class especifica o nome da classe Java a instanciar. O Blueprint Container criará o objeto Account passando 1 como o argumento para o construtor e chamando o método setDescription()
para injetar a propriedade de descrição.
Lista 2. Exemplo de construtor
public class Account {
public Account(long number) {
...
}
public void setDescription(String description) {
...
}
public String getDescription() {
...
}
}
<bean id=”accountOne” class=“org.apache.geronimo.osgi.Account”>
<argument value=”1”/>
<property name=”description” value=”#1 account”/>
</bean>
|
A Lista 3 mostra uma construção de factory method estático. Neste exemplo, o atributo class especifica o nome da classe que contém um factory method estático. O nome do factory method estático é especificado pelo atributo factory-method
. O Blueprint Container chamará o método estático createAccount() na classe AccountFactory e passará 2 como o argumento para criar o objeto Account . Depois que factory retornar o objeto criado, o contêiner o injetará com a propriedade de descrição.
Lista 3. Exemplo de factory method estático
public class StaticAccountFactory {
public static Account createAccount(long number) {
return new Account(number);
}
}
<bean id=”accountTwo” class=“org.apache.geronimo.osgi.StaticAccountFactory”
factory-method=“createAccount”>
<argument value=”2”/>
<property name=”description” value=”#2 account”/>
</bean>
|
Com uma construção de factory method de instância, como mostrado na Lista 4, são usados dois gerenciadores. Um gerenciador é um factory e o outro usa o factory para criar um objeto. O atributo factory-ref é usado para especificar o ID de um bean de nível superior ou um gerenciador de referência que age como um factory. O objeto factory fornecido deve ter um factory method como especificado pelo atributo
factory-method .
No exemplo, o gerenciador de bean accountFactory é o factory. O Blueprint Container criará primeiro a instância AccountFactory com seus próprios argumentos e propriedades. Neste caso, somente um único argumento é especificado: o nome de factory. O Blueprint Container então chamará o método createAccount() na instância AccountFactory e passará 3 como o argumento para criar o objeto Account . Depois que factory retornar o objeto criado, o contêiner o injetará com a propriedade de descrição.
Lista 4. Exemplo de factory method de instância
public class AccountFactory {
public AccountFactory(String factoryName) {
...
}
public Account createAccount(long number) {
return new Account(number);
}
}
<bean id=”accountFactory” class=“org.apache.geronimo.osgi.AccountFactory”>
<argument value=”account factory”/>
</bean>
<bean id=”accountThree”
factory-ref=“accountFactory”
factory-method=“createAccount”>
<argument value=”3”/>
<property name=”description” value=”#3 account”/>
</bean>
|
Dependendo da configuração do escopo, um gerenciador de bean pode criar instâncias de objeto únicas ou múltiplas. A especificação Blueprint Container define dois escopos principais:
-
singleton - O gerenciador de bean cria uma instância única do bean e retorna a instância sempre que for solicitado ao gerenciador que forneça um objeto.
-
prototype - O gerenciador de bean cria uma nova instância do bean sempre que for solicitado ao gerenciador que forneça um objeto.
Por padrão, o escopo singleton é assumido para gerenciadores de bean de nível superior. O atributo scope não pode ser configurado como um gerenciador de bean sequencial, portanto, os gerenciadores sequenciais sempre são considerados como tendo escopo prototype .
O atributo scope é usado para especificar as configurações de escopo. A Lista 5 mostra duas definições de bean com diferentes configurações de escopo.
Lista 5. Exemplos de escopo
<bean id=”prototypeAccount” class=“org.apache.geronimo.osgi.Account”
scope=”prototype”>
<argument value=”4”/>
</bean>
<bean id=”singletonAccount” class=“org.apache.geronimo.osgi.Account”
scope=”singleton”>
<argument value=”5”/>
</bean>
|
Retornos de chamada de ciclo de vida
Um gerenciador de bean também pode gerenciar o ciclo de vida do objeto criado e notificá-lo quando todas as propriedades tiverem sido injetadas ou quando ele estiver sendo destruído. A especificação Blueprint Container define dois métodos de retorno de chamada:
-
init-method - Especifica um método a ser chamado depois que todas as propriedades tiverem sido injetadas.
-
destroy-method - Especifica um método a ser chamado depois que o Blueprint Container estiver destruindo a instância do objeto.
O retorno de chamada destroy-method não é suportado para beans no escopo prototype . É responsabilidade do aplicativo destruir essas instâncias. Os dois métodos de ciclo de vida devem ser públicos, não devem tomar nenhum argumento e não devem retornar nenhum valor.
A Lista 6 mostra um exemplo de uma classe Java com métodos de ciclo de vida e uma entrada bean de XML de Blueprint que especifica os atributos init-method e
destroy-method .
Lista 6. Exemplo de retorno de chamada de ciclo de vida
public class Account {
public Account(long number) {
...
}
public void init() {
...
}
public void destroy() {
...
}
}
<bean id=”accountFour” class=“org.apache.geronimo.osgi.Account”
init-method=”init” destroy-method=”destroy”>
<argument value=”6”/>
<property name=”description” value=”#6 account”/>
</bean>
|
Gerenciadores de referência de serviço
Os gerenciadores de referência de serviço fornecem acesso aos serviços registrados no registro de serviço OSGi. Existem dois gerenciadores de referência de serviço definidos pela especificação Blueprint: referência e lista de referência.
O gerenciador de referência fornece um objeto que é um proxy para o serviço real registrado no registro de serviço. Um proxy permite que o objeto injetado permaneça o mesmo enquanto o serviço de recuperação possa ir e vir ou ser substituído por outro serviço. Chamadas em um proxy que não tem um serviço de recuperação serão bloqueadas até que um serviço seja disponibilizado ou ocorra um tempo limite.
O gerenciador de referência é definido por um elemento reference
. A quantidade de tempo que um proxy aguardará para que um serviço de recuperação seja disponibilizado é especificada usando o atributo timeout
. Se o atributo timeout não for especificado, o tempo limite padrão de cinco minutos é assumido. O tempo limite padrão também pode ser alterado para todos os gerenciadores de referência no arquivo XML de Blueprint usando o atributo default-timeout no elemento blueprint . Todos os valores de tempo limite são especificados em milissegundos; um valor de 0 significa tempo limite indefinido.
A Lista 7 mostra um exemplo simples de gerenciador de referência. O proxy de serviço terá um tempo limite de 30 segundos.
Lista 7. Exemplo de gerenciador de referência
<reference id=”serviceReferenceOne”
interface=”java.io.Serializable”
timeout=”30000”/>
|
Gerenciador de lista de referência
O gerenciador de lista de referência fornece um objeto List que contém os objetos proxy de serviço ou objetos ServiceReference
, dependendo da configuração do tipo de membro. O objeto List fornecido é dinâmico, pois ele pode crescer e encolher à medida que serviços correspondentes são incluídos ou removidos do registro de serviço. O objeto List é somente leitura e suporta somente um subconjunto da API List .
Os proxies em um gerenciador de lista de referência são diferentes dos proxies em um gerenciador de referência. Eles têm um serviço de recuperação fixo, não possuem um tempo limite e lançam ServiceUnavailableException imediatamente se o serviço de recuperação for indisponibilizado.
O gerenciador de lista de referência é definido por um elemento reference-list . O tipo dos membros no objeto List fornecido é especificado usando o atributo member-type . O atributo member-type suporta dois valores:
-
service-object - Injeta uma lista de objetos proxy de serviço, assumidos por padrão.
-
service-reference - Injeta uma lista de objetos
ServiceReference.
A Lista 8 mostra um exemplo simples do gerenciador de lista de referência. Os membros List serão proxies de serviço.
Lista 8. Exemplo de gerenciador de lista de referência
<reference-list id=”serviceReferenceListOne”
interface=”java.io.Serializable”
member-type=”service-object”/>
|
A referência e os gerenciadores de lista de referência compartilham vários atributos. Três atributos compartilhados são usados para a seleção de serviço: interface,
component-name e um filter.
É possível usar o atributo interface para especificar uma classe de interface. A classe de interface é usada para dois propósitos: para seleção de serviço e para proxies de serviço. O atributo interface é opcional, mas, se configurado, ele deve especificar uma classe de interface. Para seleção de serviço, a classe de interface é usada para selecionar serviços do registro de serviço registrado com o nome da interface. Para proxies de serviço, os proxies retornados pelos gerenciadores de referência de serviço devem implementar todos os métodos definidos por esta classe de interface. Se o atributo interface não for especificado, o proxy se comporta como se ele implementasse uma interface sem quaisquer métodos.
Também é possível usar os atributos component-name e filter para a seleção de serviço. O atributo component-name é apenas uma maneira conveniente de incluir uma expressão osgi.blueprint.compname=<component-name>
no filtro de seleção. De forma semelhante, o atributo filter especifica a expressão de filtro OSGi não processada a ser incluída no filtro de seleção. Os atributos interface,
component-name e filter são combinados para criar uma expressão de filtro OSGi principal usada para a seleção de serviço.
Por exemplo, o filtro de seleção para a referência na Lista 7 é (&(objectClass=java.io.Serializable)), enquanto o filtro de seleção para a referência na Lista 9 é (&(objectClass=java.io.Serializable)(osgi.blueprint.compname=myAccount)(mode=shared)).
Lista 9. Exemplo de seleção de serviço
<reference id=”serviceReferenceTwo”
interface=”java.io.Serializable”
component-name=”myAccount”
filter=”(mode=shared)”/>
|
Um gerenciador de referência de serviço pode precisar que pelo menos um serviço que corresponda aos seus critérios de seleção exista antes que a inicialização do Blueprint Container possa continuar. Esse requisito é controlado com o atributo availability . O atributo availability pode ter dois valores:
-
optional - Serviços que correspondam aos critérios de seleção podem ou não existir.
-
mandatory - Pelo menos um serviço que corresponda aos critérios de seleção deve existir.
Por padrão, a disponibilidade mandatory é assumida. A configuração de disponibilidade padrão também pode ser alterada para todos os gerenciadores de referência de serviço no arquivo XML de Blueprint usando um atributo default-availability no elemento blueprint .
Um gerenciador de referência de serviço com disponibilidade mandatory
que tenha um serviço correspondente é considerado satisfeito. Um gerenciador de referência de serviço com disponibilidade optional
sempre é considerado satisfeito mesmo se ele não tiver nenhum serviço correspondente. A inicialização de Blueprint Container será atrasada até que todos os gerenciadores de referência de serviço obrigatórios sejam satisfeitos.
É importante entender que a disponibilidade mandatory
somente é considerada durante a inicialização do Blueprint Container.
Após a inicialização, a referência de serviço obrigatória pode se tornar insatisfeita à medida que os serviços entrem e saiam em qualquer ponto.
A Lista 10 mostra um exemplo de um gerenciador de referência com disponibilidade
mandatory .
Lista 10. Exemplo de disponibilidade
<reference id=”serviceReferenceThree”
interface=”java.io.Serializable”
timeout=”30000”
availability=”mandatory”/>
|
Ambos os gerenciadores de referência de serviço podem ter zero ou mais listeners de referência.
Listeners de referência são objetos em que métodos de retorno de chamada são invocados quando o serviço é selecionado pelo gerenciador de referência de serviço ou quando o serviço não é mais usado pelo gerenciador de referência de serviço. Um listener de referência é especificado usando o elemento reference-listener . Os atributos bind-method e unbind-method especificam os métodos de retorno de chamada. O objeto que fornece os métodos de retorno de chamada pode ser sequenciado dentro do elemento reference-listener ou especificado como uma referência a um gerenciador de nível superior.
Ambos os métodos de retorno de chamada de conexão e desconexão podem ter qualquer uma das seguintes assinaturas. (anyMethod representa um nome de método arbitrário.)
-
void anyMethod(ServiceReference) - O argumento é um objeto
ServiceReferencedo serviço que está sendo conectado ou desconectado. -
void anyMethod(? super T) - O argumento é o proxy do objeto de serviço que está sendo conectado ou desconectado. O tipo T deve ser designável a partir do objeto de serviço.
-
void anyMethod(? super T, Map) - O primeiro argumento é o proxy do objeto de serviço que está sendo conectado ou desconectado. O tipo T deve ser designável a partir do objeto de serviço.O segundo argumento fornece as propriedades de serviço associadas ao serviço.
Se um listener de referência tiver múltiplos métodos sobrecarregados para um retorno de chamada, todos os métodos com uma assinatura correspondente são invocados.
Para um gerenciador de lista de referência, os retornos de chamada do listener são invocados sempre que um serviço correspondente for incluído ou removido do registro de serviço. Porém, para um gerenciador de referência, o retorno de chamada de conexão não será invocado quando o gerenciador já estiver conectado a um serviço e um serviço correspondente com classificação inferior for incluído no registro do serviço. De forma semelhante, o retorno de chamada de desconexão não será chamado quando o serviço ao qual o gerenciador está conectado partir e ele puder ser imediatamente substituído por outro serviço correspondente.
É importante entender que ao usar um gerenciador de referência e interagir com serviços stateful, você deve usar listeners de referência para controlar os serviços de recuperação do proxy para gerenciar o estado do serviço apropriadamente.
A Lista 11 mostra um exemplo de listener de registro simples. A classe ReferenceListener tem dois métodos de retorno de chamada de conexão e um método de retorno de chamada de desconexão, que serão chamados à medida que os serviços forem conectados e desconectados do gerenciador de lista de referência de serviço.
Lista 11. Exemplo de listener de referência
public class ReferenceListener {
public void bind(ServiceReference reference) {
...
}
public void bind(Serializable service) {
...
}
public void unbind(ServiceReference reference) {
...
}
}
<reference-list id=”serviceReferenceListTwo” interface=”java.io.Serializable”
availability=”optional”>
<reference-listener
bind-method=”bind” unbind-method=”unbind”>
<bean class=“org.apache.geronimo.osgi.ReferenceListener”/>
</reference-listener>
</reference-list>
|
Um gerenciador de serviço registra um serviço no registro de serviço OSGi. O gerenciador de serviço registra e cancela o registro do serviço se todos os gerenciadores de referência de serviço obrigatória em suas dependências forem satisfeitos. Como as referências de serviço obrigatórias podem se torna insatisfeitas em qualquer ponto, um gerenciador de serviço retorna um objeto proxy ServiceRegistration que delega as chamadas para o objeto
ServiceRegistration real.
Durante a inicialização, um Blueprint Container registra serviços baseados em ServiceFactory para cada gerenciador de serviço no Blueprint Container em vez dos objetos de serviço reais.
Esses serviços permitem que o Blueprint Container atrase a criação do objeto à medida que eles interceptam os pedidos de serviço e instanciam o objeto do serviço real somente quando necessário. O objeto do serviço real é fornecido por outro gerenciador, geralmente um gerenciador de bean. O objeto do serviço real pode implementar a interface ServiceFactory do OSGi.
No XML de Blueprint, um elemento service define um gerenciador de serviço. O gerenciador que fornece o objeto de serviço pode ser referenciado usando o atributo ref ou ser sequenciado dentro do elemento service . A Lista 12 mostra dois serviços: um com um gerenciador referenciado e um com um gerenciador sequenciado.
Lista 12. Exemplos de gerenciador de serviço
<service id=”serviceOne” ref=”account” ... />
<service id=”serviceTwo” … >
<bean class=“org.apache.geronimo.osgi.Account”>
<argument value=”123”/>
</bean>
</service>
|
Cada serviço deve ser registrado sob um ou mais nomes de interface. A lista de nomes da interface para o serviço pode ser especificada explicitamente ou determinada automaticamente a partir do objeto de serviço com base na configuração de exportação automática. Os nomes da interface podem ser configurados explicitamente usando:
- O atributo
interface, que somente pode especificar um único nome de interface. - O subelemento
interfaces, que permite que qualquer número de interface seja configurado.
A configuração de exportação automática é especificada pelo atributo auto-export e suporta as quatro opções abaixo.
-
disabled - O valor-padrão se o atributo
auto-exportnão for especificado. A lista de interfaces deve ser especificada usando o atributointerfaceou subelementosinterfaces. -
interfaces - Registre o serviço usando todas as interfaces públicas implementadas pela classe de serviço e quaisquer de suas super classes.
-
class-hierarchy - Registre o serviço usando a classe de serviço e quaisquer de suas super classes públicas.
-
all-classes - Combina as opções
interfaceseclass-hierarchy.
A Lista 13 mostra serviços com três maneiras de especificar as interfaces de serviço. Neste exemplo, os serviços serviceOne
e serviceTwo são registrados como uma única classe de interface Serializable , enquanto o serviço serviceThree é registrado com as classes MyAccount, Account e Serializable .
Lista 13. Exemplos de interface de serviço
public class MyAccount extends Account implements java.io.Serializable {
...
}
<bean id=”myAccount” class=”org.apache.geronimo.osgi.MyAccount”>
<argument value=”7”/>
<property name=”description” value=”MyAccount”/>
</bean>
<service id=”serviceOne” ref=”myAccount” interface=”java.io.Serializable”/>
<service id=”serviceTwo” ref=”myAccount”>
<interfaces>
<value>java.io.Serializable</value>
</interfaces>
</service>
<service id=”serviceThree” ref=”myAccount” autoExport=”all-classes”/>
|
Um serviço também pode ser registrado com um conjunto de propriedades que podem ser especificadas usando o subelemento service-properties
. O elemento service-properties contém múltiplos subelementos entry que representam as propriedades individuais. A chave da propriedade é especificada usando um atributo key , mas o valor da propriedade pode ser especificado como um atributo value ou sequenciado dentro do elemento. Os valores da propriedade de serviço podem ser de tipos diferentes, mas somente primitivas, classes do wrapper primitivas, coleções ou arrays de tipos primitivos são permitidos.
A Lista 14 mostra um exemplo de um registro de serviço com duas propriedades de serviço. A propriedade de serviço active tem um valor de tipo java.lang.Boolean . A propriedade mode é do tipo String .
Lista 14. Exemplos de propriedades de serviço
<service id=”serviceFour” ref=”myAccount” autoExport=”all-classes”>
<service-properties>
<entry key=”mode” value=”shared”/>
<entry key=”active”>
<value type=”java.lang.Boolean”>true</value>
</entry>
</service-properties>
</service>
|
Uma propriedade osgi.blueprint.compname é automaticamente incluída nas propriedades de serviço se o gerenciador que fornece o objeto de serviço para o serviço for um gerenciador de nível superior. O valor da propriedade osgi.blueprint.compname é o ID do gerenciador de nível superior que fornece a instância de serviço. Por exemplo, dado o exemplo de serviço na Lista 14, o valor da propriedade
osgi.blueprint.compname seria configurado para myAccount.
É possível usar o atributo ranking para expor o serviço com uma classificação específica. Se o atributo de classificação não for especificado, o serviço será registrado sem uma classificação. Em casos assim, a estrutura OSGi assume 0 como a classificação padrão para o serviço.
Um exemplo de registro de serviço com uma classificação é mostrado abaixo.
Lista 15. Exemplo de classificação de serviço
<service id=”serviceFive” ref=”myAccount” auto-export=”all-classes” ranking=”3”/>
|
Um gerenciador de serviço pode ter zero ou mais listeners de registro, que são objetos cujos métodos de retorno de chamada são invocados logo após o registro do serviço ou logo antes do cancelamento do registro do serviço. Um listener de registro é especificado usando o subelemento registration-listener . Os atributos registration-method e unregistration-method especificam os métodos de retorno de chamada. O objeto que fornece os métodos de retorno de chamada pode ser sequenciado dentro do elemento registration-listener
ou especificado como uma referência a um gerenciador de nível superior.
A assinatura dos métodos de retorno de chamada de registro e cancelamento de registro depende de se o objeto de serviço implementa a interface ServiceFactory . Se o serviço implementar a interface ServiceFactory , ambos os métodos devem ter uma assinatura void anyMethod(ServiceFactory, Map) , em que anyMethod representa um nome de método arbitrário.
Se o serviço não implementar a interface ServiceFactory , ambos os métodos de retorno de chamada devem corresponder à assinatura anyMethod(? super T, Map)
de anulação, em que o tipo T é designável a partir do tipo de objeto de serviço.
O primeiro argumento para o método de retorno de chamada é uma instância do objeto de serviço e o segundo argumento são as propriedades de registro. Se um listener de registro tiver múltiplos métodos sobrecarregados para um retorno de chamada, então todos os métodos com uma assinatura correspondente serão invocados.
A Lista 16 mostra um exemplo de listener de registro simples.
Lista 16. Exemplo de listener de registro
public class RegistrationListener {
public void register(Account account, Map properties) {
...
}
public void unregister(Account account, Map properties) {
...
}
}
<service id=”serviceSix” ref=”myAccount” auto-export=”all-classes”>
<registration-listener
registration-method=”register” unregistration-method=”unregister”>
<bean class=“org.apache.geronimo.osgi.RegistrationListener”/>
</registration-listener>
</service>
|
A especificação Blueprint Container também define um número de gerenciadores de ambiente especiais que possuem IDs configurados e fornecem acesso a componentes ambientais. Eles não possuem definição XML e não podem ser substituídos, já que seus IDs são reservados e não podem ser usados com outros gerenciadores. Os objetos fornecidos por gerenciadores de ambiente somente podem ser injetados em outros gerenciadores usando uma referência. A especificação Blueprint Container define quatro gerenciadores de ambiente:
-
blueprintBundle - Fornece o objeto
Bundledo pacote configurável. -
blueprintBundleContext - Fornece o objeto
BundleContextdo pacote configurável. -
blueprintContainer - Fornece o objeto
BlueprintContainerpara o pacote configurável. -
blueprintConverter - Fornece o objeto
Converterpara o pacote configurável que fornece acesso ao recurso de conversão do tipo Blueprint Container. Conversão de tipo tem mais informações.
A Lista 17 mostra um exemplo simples no qual o objeto
Bundle fornecido pelo gerenciador de ambiente blueprintBundle é injetado no bean accountManagerOne .
Lista 17. Exemplo de gerenciador de ambiente
public class AccountManager {
public void setManagerBundle(Bundle bundle) {
...
}
}
<bean id=”accountManagerOne” class=”org.apache.geronimo.osgi.AccountManager”>
<property name=”managerBundle” ref=”blueprintBundle”/>
</bean>
|
A especificação Blueprint Container define muitos elementos XML que descrevem diferentes tipos de valores de objeto. Esses elementos de valor XML são usados dentro das definições do gerenciador. Por exemplo, eles podem ser usados em um gerenciador de bean para especificar valores de argumento ou de propriedade ou em um gerenciador de serviço para especificar os valores das propriedades de serviço. Os elementos de valor XML são convertidos nos objetos de valor reais e injetados nos componentes do gerenciador.
O elemento ref define uma referência para um gerenciador de nível superior. O atributo component-id especifica um ID de um gerenciador de nível superior. O valor injetado será o objeto retornado pelo gerenciador referenciado.
A Lista 18 mostra um exemplo do elemento de valor ref . A instância bean accountOne é injetada na propriedade managedAccount do bean accountManagerTwo .
Lista 18. Exemplo de valor de referência
public class AccountManager {
...
public void setManagedAccount(Account account) {
...
}
}
<bean id=”accountOne” class=“org.apache.geronimo.osgi.Account”>
<argument value=”1”/>
<property name="description" value="#1 account"/>
</bean>
<bean id=”accountManagerTwo” class=“org.apache.geronimo.osgi.AccountManager”>
<property name=”managedAccount”>
<ref component-id=”accountOne”/>
</property>
</bean>
|
O elemento idref define um ID de um gerenciador de nível superior. O valor injetado é o ID de componente conforme especificado pelo atributo component-id . O elemento idref é usado para garantir que o gerenciador do ID especificado realmente existe antes da ativação do gerenciador.
O elemento value representa um objeto a ser criado a partir do conteúdo da cadeia de caracteres do elemento. É possível usar o atributo opcional type para especificar um tipo no qual o conteúdo da cadeia de caracteres deve ser convertido. Se o atributo type não for especificado, o conteúdo da cadeia de caracteres será convertido no tipo no qual está sendo injetado.
O elemento null representa o nulo do Java.
Os elementos list, set e array são coletas e representam um objeto java.util.List , objeto java.util.Set e array Object[] , respectivamente. Os subelementos dessas coletas podem ser qualquer um dos elementos de valor XML descritos nesta seção. O atributo opcional value-type
pode ser configurado nesses elementos para especificar um tipo padrão para subelementos da coleta.
O exemplo na Lista 19 mostra como os elementos de valor XML podem ser combinados para criar uma lista. A lista criada conterá os seguintes quatro itens:
- Uma
String"123" - Um objeto
java.math.BigIntegercom valor 456 - Um nulo
- Um objeto
java.util.Setcom dois valores do tipojava.lang.Integer
Lista 19. Exemplo de valor de lista complexa
<list>
<value>123</value>
<value type=”java.math.BigInteger”>456</value>
<null/>
<set value-type=”java.lang.Integer”>
<value>1</value>
<value>2</value>
</set>
</list>
|
O elemento props representa um objeto java.util.Properties no qual as chaves e valores são do tipo String . Os subelementos prop representam as propriedades individuais. A chave da propriedade é especificada usando um atributo key , mas o valor da propriedade pode ser especificado como um atributo value ou como um conteúdo do elemento.
A Lista 20 mostra um exemplo do elemento de valor props .
Lista 20. Exemplo de valor de propriedades
<props>
<prop key=”yes”>good</prop>
<prop key=”no” value=”bad”/>
</props>
|
O elemento map representa um objeto java.util.Map no qual as chaves e os valores podem ser objetos arbitrários. Os subelementos entry representam as propriedades individuais. A chave pode ser especificada como um atributo key , atributo key-ref , ou sequenciada dentro de um subelemento key . O valor pode ser especificado como um atributo value , atributovalue-ref , ou ser sequenciado. O valor sequenciado pode ser especificado como qualquer um dos elementos de valor XML descritos nesta seção. Porém, a chave sequenciada pode ser especificada como qualquer um dos elementos de valor XML, exceto o elemento null . Chaves nulas não são permitidas nos elementos map na Especificação Blueprint. Os atributos key-ref e value-ref especificam um ID de um gerenciador de nível superior e são usados para obter um objeto a partir do gerenciador especificado como a chave ou o valor de propriedade. O elemento map
pode especificar os atributos key-type e value-type para definir um tipo padrão para chaves e valores.
A Lista 21 mostra um exemplo do elemento de valor map . Ela também mostra como as entradas de um objeto map podem ser construídas de várias maneiras. O objeto map criado conterá as seguintes entradas:
- Uma chave de
StringmyKey1 que é mapeada paraStringmyValue. - Uma chave que é um objeto retornado pelo gerenciador de bean
accounté mapeada paraStringmyValue. - Uma chave que é um objeto
java.lang.Integercom valor 123 é mapeada paraStringmyValue. - Uma chave de
StringmyKey2 é mapeada para um valor que é um objeto retornado pelo gerenciador de beanaccount. - Uma chave de
StringmyKey3 é mapeada para um valor que é um objetojava.lang.Longcom valor 345. - Uma chave que é um objeto
java.net.URIcom valor urn:ibm é mapeada para um valor que é um objetojava.net.URLcom valor http://ibm.com.
Lista 21. Exemplo de valor de
map
<map>
<entry key=”myKey1” value=”myValue”/>
<entry key-ref=”account” value=”myValue”/>
<entry value=”myValue”>
<key>
<value type=”java.lang.Integer”>123</value>
<key/>
</entry>
<entry key=”myKey2” value-ref=”account”>
<entry key=”myKey3”>
<value type=”java.lang.Long”>345</value>
</entry>
<entry>
<key>
<value type=”java.net.URI”>urn:ibm</value>
<key/>
<value type=”java.net.URL”>http://ibm.com</value>
</entry>
</map>
|
Cada gerenciador também pode ser sequenciado como um valor. A Lista 22 mostra um exemplo de gerenciador bean sequenciado.
Lista 22. Exemplo de valor de gerenciador sequenciado
<bean id=”accountManagerThree” class=“org.apache.geronimo.osgi.AccountManager”>
<property name=”managedAccount”>
<bean class=“org.apache.geronimo.osgi.Account”>
<argument value=”10”/>
<property name="description" value="Inlined Account"/>
</bean>
</property>
</bean>
|
Durante a injeção, o Blueprint Container converte os elementos de valor XML em objetos reais. Ele converte os elementos com base no tipo de propriedade injetada. Por exemplo, na Lista 2, o Blueprint Container converte o valor da Cadeia de Caracteres 123 em um valor long
. O Blueprint Container fornece diversas conversões integradas, como a conversão de:
- Valores de cadeia de caracteres em todos os tipos primitivos, tipos de wrapper ou quaisquer tipos que tenham um construtor público que tome um valor de Cadeia de Caracteres.
- Elementos
arrayem objetos de coleta (com tipos de membros compatíveis). - Elementos
listousetem objetos de array (com tipos de membros compatíveis).
O Blueprint Container também suporta genéricos. Se as informações sobre os genéricos estiverem disponíveis, o Blueprint Container usará essas informações para executar as conversões. Por exemplo, na Lista 23, o elemento list será convertido em uma lista de objetos java.util.Long .
Lista 23. Exemplo de conversão de genéricos
public class AccountManager {
...
public void setAccountNumbers(List<Long> accounts) {
...
}
}
<bean id=”accountManagerFour” class=“org.apache.geronimo.osgi.AccountManager”>
<property name=”accountNumbers”>
<list>
<value>123</value>
<value>456</value>
<value>789</value>
</list>
</property>
</bean>
|
Além das conversões integradas, um pacote configurável Blueprint pode fornecer seus próprios conversores. Os conversores customizados são gerenciadores de bean que fornecem um objeto que implementa a interface Converter do Blueprint. Os conversores customizados são especificados dentro do elemento type-converters
sob o elemento blueprint . Os conversores de tipo são inicializados primeiro durante a inicialização do Blueprint Container para que outros gerenciadores possam aproveitar os conversores customizados. A especificação do Blueprint Container tem mais informações sobre a gravação de conversores customizados.
Para experimentar aplicativos Blueprint, são necessárias duas coisas: uma plataforma OSGi e a implementação do Blueprint Container. Neste artigo, usaremos o Equinox como a plataforma OSGi, mas outras — como a Apache Felix — também funcionariam bem. Para o Blueprint Container, usaremos uma implementação a partir do projeto da incubadora do Apache Aries. A implementação do Blueprint em Aries foi originalmente desenvolvida no projeto Apache Geronimo, mas foi movida para o Aries, no qual está sendo atualmente desenvolvida.
A amostra anexada fornece um bom ponto de início para experimentar o Blueprint. Ela contém dois módulos: blueprint-sample e blueprint-assembly. O módulo blueprint-sample
é um pacote configurável Blueprint de amostra. Ele contém muitas das classes Java e definições XML de Blueprint mostradas neste artigo. O módulo blueprint-assembly compacta a estrutura Equinox, a implementação Apache Aries Blueprint e o pacote configurável de amostra juntos em um contêiner fácil de usar.
Faça o download e descompacte a origem da amostra anexada no diretório de sua preferência. Use o Apache Maven para construir a origem da amostra. Execute os comandos na Lista 24 para construir a amostra.
Lista 24. Construindo a amostra
cd sample mvn install |
O Maven automaticamente construirá ambos os módulos na amostra. Depois que a amostra inteira for construída com êxito, o diretório target do módulo blueprint-assembly conterá diversos arquivos JAR. Ele conterá a implementação do Apache Aries Blueprint, o pacote configurável da amostra e alguns outros JARs que fornecem recursos de criação de log para a estrutura OSGi. Ele também conterá o arquivo osgi-3.5.0.v20090520.jar, que fornece a estrutura Equinox. Depois de iniciada, a estrutura Equinox automaticamente instalará e iniciará todos os outros JARs no diretório de destino. Para iniciar a estrutura Equinox, execute os comandos da Lista 25.
Lista 25. Iniciando o Equinox
cd blueprint-assembly/target java -jar osgi-3.5.0.v20090520.jar -console |
O console do Equinox aparecerá após a inicialização bem-sucedida. Use o comando ss para exibir os pacotes configuráveis instalados; a Figura 1 mostra a saída do comando ss .
Figura 1. Saída dos pacotes configuráveis instalados
Executando o pacote configurável de amostra
O pacote configurável de amostra será automaticamente instalado na estrutura Equinox, mas não será iniciado. Para iniciar o pacote configurável de amostra, execute o comando start 5 no console do Equinox; o número 5 é o ID do pacote configurável de amostra org.apache.aries.sample_1.0.0.SNAPSHOT . Depois que o pacote configurável for iniciado com êxito, você deve ver a seguinte saída no console, com mostrado na Figura 2.
Figura 2. Saída do pacote configurável de amostra
A especificação Blueprint Container, introduzida na especificação OSGi mais recente, fornece um modelo de programação simples e fácil para a criação de aplicativos dinâmicos no ambiente OSGi sem incluir complexidade no código Java. Com a implementação Aries Blueprint Container, é possível iniciar a construção de aplicativos baseados em Blueprint hoje mesmo.
| Descrição | Nome | Tamanho | Método de download |
|---|---|---|---|
| Sample source | os-osgiblueprint-sample.zip | 11KB | HTTP |
Informações sobre métodos de download
Aprender
-
Confira o tutorial Bundles for
beginners de OSGi do JavaWorld. Saiba como criar, executar e gerenciar pacotes configuráveis em um contêiner OSGi.
-
Leia mais sobre injeção de dependência.
-
Saiba mais sobre o OSGi Alliance e obtenha as notícias, eventos, fóruns e especificações mais recentes e mais.
-
Apache Geronimo está produzindo uma estrutura de tempo de execução de servidor que reúne alternativas de software livre para criar tempos de execução que satisfaçam as necessidades de desenvolvedores e administradores de sistema.
-
Saiba tudo sobre o Apache Felix.
-
Obtenha uma visão geral do Apache Aries.
-
Os nomes de propriedade e nomes de método setter correspondentes seguem o padrão de design para propriedades conforme definido na especificação JavaBeans
.
- Saiba como os Módulos Dinâmicos Spring
facilitam a construção de aplicativos Spring que são executados em uma estrutura OSGi.
-
Para ouvir entrevistas e discussões interessantes para desenvolvedores de software, confira os podcasts do developerWorks.
-
Mantenha-se atualizado com os eventos e webcasts técnicos do developerWorks.
-
Siga o developerWorks no Twitter.
-
Confira as conferências, trade shows, webcasts e outros Eventos futuros no mundo todo que são de interesse dos desenvolvedores de software livre da IBM.
-
Visite a zona de software livre do developerWorks para obter informações extensivas sobre instruções, ferramentas e atualizações de projeto para ajudá-lo no desenvolvimento de tecnologias de software livre e em seu uso com produtos da IBM, assim como nossos mais populares artigos e tutoriais.
-
Assista e aprenda sobre a IBM e tecnologias de software livre e funções de produto com as demos gratuitas do developerWorks On demand.
Obter produtos e tecnologias
-
Faça o download das especificações OSGi V4.2 a partir do OSGi Alliance. A Especificação Blueprint Container está em Compendium Specification.
- Para instalar o Karaf, faça o download do arquivo de distribuição binário para a sua plataforma.
- Obtenha o Apache Maven para construir a origem do pacote configurável de amostra.
-
Inove seu próximo projeto de desenvolvimento de software livre com o software de avaliação IBM, disponível para download ou em DVD.
- Faça o download de versões de avaliação de produto IBM
ou explore as avaliações on-line no IBM SOA Sandbox e tenha em mãos ferramentas de desenvolvimento de aplicativos e produtos de middleware do DB2®, Lotus®, Rational®, Tivoli® e WebSphere®.
Discutir
-
Participe dos blogs do developerWorks e faça parte da comunidade do developerWorks.
