SeamFramework.orgCommunity Documentation

Apêndice A. Integrando o Weld em outros ambientes

Se você quer usar o Weld em um outro ambiente, você precisará fornecer certas informações ao Weld por meio da integração SPI. neste Apêndice nós discutiremos brevemente os passos necessários.

Serviços Corporativos

Se você quer somente utilizar beans gerenciados, e não quer tirar vantagem dos serviços corporativos (injeção de recursos EE, injeção CDI em classes de componente EE, eventos transacionais, suporte a serviços CDI nos EJBs) e implantações non-flat, então o suporte genérico por servlet provido pela extensão "Weld: Servlets" será suficiente e funcionará em qualquer contêiner que suporte a Servlet API.

Todos SPIs e APIs descritas possuem um extensivo JavaDoc, o qual explicita o contrato detalhado entre o contêiner e o Weld.

A Weld SPI está localizada no módulo weld-spi, e empacotado como weld-spi.jar. Algumas SPIs são opcionais e deveriam ser implementadas somente se você precisar de substituir o comportamento padrão; outras são obrigatórias.

Todas as interfaces na SPI suportam o padrão decorador e fornecem a classe Forwarding localizada no sub-pacote helpers. Adicionalmente, as comumente utilizadas classes utilitárias e implementações padrão, também estão localizadas no sub-pacote helpers.

O Weld suporta múltiplos ambientes. Um ambiente é definido por uma implementação da interface Environment. Uma série de ambientes padrões já estão embutidos e descritos pela enumeração Environments. Os diferentes ambientes requerem diferentes serviços presentes (por exemplo um contêiner Servlet não requer transação, EJB ou serviços JPA). Por padrão um ambiente EE é assumido, mas você pode ajustar o ambiente chamando bootstrap.setEnvironment().

O Weld utiliza um registro de serviço com tipagem genérica para permitir que os serviços sejam registrados. Todos os serviços implementam a interface Service. O registro de serviço permite que os serviços sejam acrescentados e recuperados.

Uma aplicação é normalmente composta por uma série de módulos. Por exemplo, uma aplicação Java EE pode conter vários módulos EJB (contendo lógica de negócio) e módulos war (contendo a interface de usuário). Um contêiner pode obrigar certas regras de acessibilidade como limitar a visibilidade de classes entre módulos. CDI permite que estas mesmas regras sejam aplicadas a resolução de beans e métodos observadores. Como as regras de acessibilidade variam entre contêineres, o Weld requer que o contêiner descreva a estrutura de implantação, por meio da SPI Deployment.

A especificação CDI aborda os Bean Deployment Archives (BDAs)—arquivos que são marcados como possuindo beans que devem ser implantados no contêiner CDI, e os tornam disponíveis para injeção e resolução. O Weld reusa esta descrição de Bean Deployment Archives em sua estrutura SPI de implantação. Cada implantaçao expõe os BDAs que ela contém; cada BDA pode também referenciar outros que ele pode acessar. Conjuntamente, o percurso transitivo deste grafo forma os beans que são implantados na aplicação.

Para descrever a estrutura de implantação para o Weld, o contêiner deve fornecer uma implementação de Deployment. O método Deployment.getBeanDeploymentArchives() permite que o Weld descubra os módulos que compõem a aplicação. A especificação CDI também permite que beans sejam especificado programaticamente como parte da implantação do bean. Estes beans podem, ou não, estar em um BDA existente. Por esta razão, o Weld chamará Deployment.loadBeanDeploymentArchive(Class clazz) para cada bean descrito programaticamente.

Como os beans programaticamente descritos podem resultar em BDAs adicionais sendo inseridos ao grafo, o Weld descobrirá a estrutura BDA cada vez que um BDA desconhecido for retornado por Deployment.loadBeanDeploymentArchive.

O BeanDeploymentArchive fornece três métodos que permitem que seu conteúdo seja descoberto pelo Weld—BeanDeploymentArchive.getBeanClasses() deve retornar todas as classes no BDA, BeanDeploymentArchive.getBeansXml() deve retornar uma estrutura de dados representando o descritor beans.xml de implantação para o archive, e BeanDeploymentArchive.getEjbs() deve fornecer um descritor EJB para cada EJB no BDA, ou uma lista vazia se ele não for um arquivo EJB.

Para auxiliar o integrador do contêiner, o Weld fornece um parser do beans.xml embutido. Para analisar um beans.xml para a estrutura de dados requerida pelo BeanDeploymentArchive, o contêiner deve chamar Bootstrap.parseBeansXml(URL). O Weld também pode analisar vários arquivos beans.xml, mesclando-os para se tornar uma única estrutura de dados. Isto pode ser realizado chamando Bootstrap.parseBeansXml(Iterable<URL>).

Um BDA X também pode referenciar um outro BDA Y cujos beans podem ser resolvidos e injetados dentro de qualquer bean em BDA X. Estes são os BDAs acessíveis, e todo BDA que é diretamente acessível pelo BDA X deve ser retornado. Um BDA também terá BDAs que são transitivamente acessíveis, e o percurso transitivo do sub-grafo de BDA X descreve todos os beans resolvíveis pelo BDA X.

Para especificar os BDAs diretamente acessíveis, o contêiner deve fornecer uma implementação de BeanDeploymentArchive.getBeanDeploymentArchives().

Certos serviços são fornecidos para a implantação inteira, embora alguns sejam providos por-BDA. Os serviços BDA são fornecidos utilizando BeanDeploymentArchive.getServices() e somente aplicados aos BDAs que eles são providos.

O contrato de Deployment requer ao contêiner especificar as extensões portáveis (veja o capítulo 12 da especificação CDI) que deveriam ser carregadas pela aplicação. Para auxiliar o integrador do contêiner, o Weld fornece o método Bootstrap.loadExtensions(ClassLoader) que carregará as extensões para o classloader especificado.

Todos os serviços de recursos EE são serviços por-BDA, e podem ser providos utilizando um de dois métodos. Qual método utilizar está a critério do integrador.

The integrator may choose to provide all EE resource injection services themselves, using another library or framework. In this case the integrator should use the EE environment, and implement the Seção A.1.8, “Serviços de Injeção” SPI.

Alternatively, the integrator may choose to use CDI to provide EE resource injection. In this case, the EE_INJECT environment should be used, and the integrator should implement the Seção A.1.4, “Serviços EJB”, Seção A.1.7, “Serviços de Recursos” and Seção A.1.5, “Serviços JPA”.

Importante

CDI only provides annotation-based EE resource injection; if you wish to provide deployment descriptor (e.g. ejb-jar.xml) injection, you must use Seção A.1.8, “Serviços de Injeção”.

Se o contêiner realiza injeção de recursos EE, os recursos injetados devem ser serializáveis. Se a injeção de recursos EE for fornecida pelo Weld, o recurso resolvido deve ser serializável.

Dica

Se você usa um ambiente não-EE, então você pode implementar qualquer uma das SPIs de serviço EE, e o Weld proverá a funcionalidade associada. Não existe necessidade de implementar aqueles serviços que você não utilizará!

A interface org.jboss.weld.bootstrap.api.Bootstrap define a inicialização para o Weld, a implantação e validação de beans. Para iniciar o Weld, você deve criar uma instância de org.jboss.weld.bootstrap.WeldBeansBootstrap (que implementa Boostrap), indicar quais serviços usará, e então solicitar que o contêiner inicie.

Toda a inicialização é separada em fases, a inicialização do contêiner, implantação dos beans, validação dos beans, e desligamento. A inicialização criará um gerenciador, adicionará os contextos embutidos e examinará a estrutura de implantação. A implantação de beans implantará todos os beans (definidos usando anotações, programaticamente ou embutidos). A validação de beans validará todos os beans.

Para inicializar o contêiner, você chama Bootstrap.startInitialization(). Antes de chamar startInitialization(), você deve registrar todos os serviços requeridos pelo ambiente. Você pode fazer isto chamando, por exemplo, bootstrap.getServices().add(JpaServices.class, new MyJpaServices()). Você também deve fornecer o armazenador de beans do contexto da aplicação.

Tendo chamado startInitialization(), o Manager de cada BDA pode ser obtido chamando Bootstrap.getManager(BeanDeploymentArchive bda).

Para implantar os beans descobertos, chame Bootstrap.deployBeans().

Para validar os beans implantados, chame Bootstrap.validateBeans().

Para colocar o contêiner em um estado onde ele pode servir requisições, chame Bootstrap.endInitialization().

Para encerrar o contêiner você chama Bootstrap.shutdown(). Isto permite que o contêiner realize qualquer operação de limpeza necessária.

Existe uma série de requisitos que o Weld coloca sobre o contêiner para o correto funcionamento que não se enquadram na implementação de APIs.

Isolamento de Classloader (Classloader isolation)

Se você está integrando o Weld em um ambiente que suporta implantação de várias aplicações, você de deve habilitar, automaticamente ou por configuração, o isolamento do classloader para cada aplicação CDI.

Servlet

Se você esta integrando o Weld em um ambiente Servlet você deve registrar org.jboss.weld.servlet.WeldListener como um Servlet listener, seja automaticamente ou por configuração, para cada aplicação CDI que utiliza Servlet.

Você deve assegurar que WeldListener.contextInitialized() seja chamada depois dos beans serem completamente implantados (garantir que Bootstrap.deployBeans() tenha sido chamado).

JSF

Se você está integrando o Weld em um ambiente JSF você deve registrar org.jboss.weld.jsf.WeldPhaseListener como um phase listener.

Se você etá integrando o Weld em um ambiente JSF você deve registrar org.jboss.weld.el.WeldELContextListener como um listener do contexto EL.

Se você está integrando o Weld em um ambiente JSF você deve registrar org.jboss.weld.jsf.ConversationAwareViewHandler como um manipulador de visão delegante.

Se você está integrando o Weld em um ambiente JSF você deve obter o gerenciador de beans para o módulo e então chamar BeanManager.wrapExpressionFactory(), passando Application.getExpressionFactory() como argumento. A fábrica de expressão envolvida deve ser usada em todas as avaliações de expressões EL realizadas por JSF nesta aplicação web.

Se você está integrando o Weld em um ambiente JSF você precisa obter o gerenciador de beans para o módulo e então chamar BeanManager.getELResolver(). O EL resolver retornado deve ser registrado com o JSF para esta aplicação web.

Se você está integrando o Weld em um ambiente JSF você deve registrar org.jboss.weld.servlet.ConversationPropagationFilter como um Servlet listener, seja automaticamente ou por configuração, para cada aplicação CDI que utiliza JSF. Este filtro pode ser registrado em qualquer implantações Servlet sem problemas.

JSP

Se você está integrando o Weld em um ambiente JSP você deve registrar org.jboss.weld.el.WeldELContextListener como um listener do contexto EL.

Se você está integrando o Weld em um ambiente JSP você deve obter o gerenciador de beans para o módulo e então chamar BeanManager.wrapExpressionFactory(), passando Application.getExpressionFactory() como argumento. A fábrica de expressão envolvida deve ser usada em todas as avaliações de expressões EL realizadas por JSP.

Se você está integrando o Weld em um ambiente JSP você deve obter o gerenciador de beans para o módulo e então chamar BeanManager.getELResolver(). O EL resolver retornado deve ser registrado no JSP para esta aplicação web.

Interceptador de Session Bean

Se você está integrando o Weld em um ambiente EJB você deve registrar o método aroundInvoke de org.jboss.weld.ejb.SessionBeanInterceptor como um interceptador EJB around-invoke para todos EJBs na aplicação, seja automaticamente ou por configuração, para cada aplicação CDI que utiliza beans corporativos. Se você está rodando em um ambiente EJB 3.1, você deve registrar este como um interceptador around-timeout também.

O weld-core.jar

O Weld pode residir dentro de um classloader isolado ou em um classloader compartilhado. Se você escolher utilizar um classloader isolado, o padrão SingletonProvider, IsolatedStaticSingletonProvider, pode ser usado. Se você escolher utilizar um classloader compartilhado, então você precisará escolher outra estratégia.

Você pode fornecer sua própria implementação de Singleton e SingletonProvider e registrá-la utilizando SingletonProvider.initialize(SingletonProvider provider).

O Weld também fornece uma implementação da estratégia por aplicação com Thread Context Classloader, por meio de TCCLSingletonProvider.

Vinculando o gerenciador em JNDI

Você deveria vincular o gerenciador de beans ao arquivo de implantação de beans dentro de JNDI em java:comp/BeanManager. O tipo deve ser javax.enterprise.inject.spi.BeanManager. Para obter o gerenciador de beans correto para o arquivo de implantação de beans, você pode chamar bootstrap.getBeanManager(beanDeploymentArchive).

Realizando injeção CDI em classes de componente Java EE

A especificação CDI requer que o contêiner forneça injeção dentro de recursos não-contextuais para todas classes de componentes Java EE. O Weld delega esta responsabilidade ao contêiner. Isto pode ser alcançado utilizando o SPI de CDI InjectionTarget já definido. Além disso, você deve realizar esta operação sobre o gerenciador de beans correto para o arquivo de implantação de beans que contenha a classe de componente EE.

A especificação CDI também requer que um evento ProcessInjectionTarget seja disparado para cada classe de componente Java EE. Além disso, se um observador chamar ProcessInjectionTarget.setInjectionTarget() o contêiner deve usar o alvo de injeção especificado para realizar a injeção.

Para ajudar o integrador, o Weld fornece o método WeldManager.fireProcessInjectionTarget(), o qual retorna o InjectionTarget a ser utilizado.

// Fire ProcessInjectionTarget, returning the InjectionTarget
// to use
InjectionTarget it = weldBeanManager.fireProcessInjectionTarget(clazz);

// Per instance required, create the creational context
CreationalContext<?> cc = beanManager.createCreationalContext(null);

// Produce the instance, performing any constructor injection required
Object instance = it.produce();

// Perform injection and call initializers
it.inject(instance, cc);

// Call the post-construct callback
it.postConstruct(instance);

// Call the pre-destroy callback
it.preDestroy(instance);

// Clean up the instance
it.dispose();
cc.release();

O contêiner pode intercalar outras operações entre estas chamadas. Além disso, o integrador pode escolher implementar qualquer dessas chamadas de uma outra maneira, assumindo que o contrato seja cumprido.

Ao realizar injeções em EJBs você deve utilizar o SPI definido pelo Weld, WeldManager. Além disso, você deve realizar esta operação sobre o gerenciador de beans correto para o arquivo de implantação de beans que contenha o EJB.

// Obtain the EjbDescriptor for the EJB
// You may choose to use this utility method to get the descriptor
EjbDescriptor<?> ejbDescriptor = beanManager.getEjbDescriptor(ejbName);

// Get an the Bean object
Bean<?> bean = beanManager.getBean(ejbDescriptor);

// Create the injection target
InjectionTarget it = deploymentBeanManager.createInjectionTarget(ejbDescriptor);

// Per instance required, create the creational context
CreationalContext<?> cc = deploymentBeanManager.createCreationalContext(bean);

// Perform injection and call initializers
it.inject(instance, cc);

// You may choose to have CDI call the post construct and pre destroy
// lifecycle callbacks

// Call the post-construct callback
it.postConstruct(instance);

// Call the pre-destroy callback
it.preDestroy(instance);

// Clean up the instance
it.dispose();
cc.release();