SeamFramework.orgCommunity Documentation
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.
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
.
Em um contêiner rigoroso, cada BDA deve especificar explicitamente quais outros BDAs ele pode acessar. Entretanto, muitos contêineres permitirão um mecanismo fácil para tornar os BDAs bi-direcionalmente acessíveis (como em um diretório de bibliotecas). Neste caso, é admissível (e razoável) descrever todos tais arquivos como um único e 'virtual' BeanDeploymentArchive
.
Um contêiner, pode, por exemplo, usar uma estrutura de acessibilidade rasa para a aplicação. Neste caso, um único BeanDeploymentArchive
deveria ser anexado ao Deployment
.
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.
Na prática, você pode respeitar a estrutura de implantação representada por Deployment
, e o grafo virtual de BDAs será como um espelho da estrutura do classloader para uma implantação. Se uma classe pode a partir do BDA X ser carregada por outro no BDA Y, ele é acessível, e portanto os BDAs acessíveis do BDA Y deve incluir o BDA X.
Para especificar os BDAs diretamente acessíveis, o contêiner deve fornecer uma implementação de BeanDeploymentArchive.getBeanDeploymentArchives()
.
O Weld permite ao contêiner descrever um grafo circular e converter um grafo para uma árvore como parte do processo de implantação.
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.
O Weld delega a descoberta de beans do EJB 3 ao contêiner, uma vez que ele não duplica o trabalho feito pelo contêiner EJB e respeita qualquer extensão do fornecedor para a definição de EJB.
O EjbDescriptor
deve retornar os metadados relevantes conforme definido na especificação EJB. Cada interface de negócio de um session bean deve ser descrita usando um BusinessInterfaceDescriptor
.
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”.
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.
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á!
Os serviços EJB são separados em duas interfaces que são ambas por-BDA.
EJBServices
é utilizado para resolver EJBs locais usados para apoiar session beans e sempre deve ser fornecido em um ambiente EE. EJBServices.resolveEjb(EjbDescriptor ejbDescriptor)
retornar um envólucro—SessionObjectReference
—sobre a referência do EJB. Este envólucro permite que o Weld solicite um referência que implementa uma dada interface de negócio, e, no caso de SFSBs, também solicitar a remoção do EJB do contêiner e consultar se o EJB tinha sido anteriormente removido.
EJBResolutionServices.resolveEjb(InjectionPoint ij)
allows the resolution of @EJB
(for injection into managed beans). This service is not required if the implementation of Seção A.1.8, “Serviços de Injeção” takes care of @EJB
injection.
Assim como a resolução de EJB é delegada ao contêiner, a resolução de @PersistenceContext
para injeção dentro dos beans gerenciados (com o InjectionPoint
fornecido) é delegada ao contêiner.
To allow JPA integration, the JpaServices
interface should be implemented. This service is not required if the implementation of Seção A.1.8, “Serviços de Injeção” takes care of @PersistenceContext
injection.
O Weld delega as atividades JTA para o contêiner. A SPI fornece vários ganchos para facilmente conseguir isso com a interface TransactionServices
.
Qualquer implementação de javax.transaction.Synchronization
pode ser passada para o método registerSynchronization()
e a implementação SPI deve registrar imediatamente a sincronização com o gerenciador de transação JTA usado pelos EJBs.
Para tornar mais fácil determinar se uma transação está ou não atualmente ativa para a thread solicitante, o método isTransactionActive()
pode ser usado. A implementação SPI deve consultar o mesmo gerenciador de transação JTA usado pelos EJBs.
The resolution of @Resource
(for injection into managed beans) is delegated to the container. You must provide an implementation of ResourceServices
which provides these operations. This service is not required if the implementation of Seção A.1.8, “Serviços de Injeção” takes care of @Resource
injection.
Um integrador pode desejar usar InjectionServices
para fornecer injeção por campo ou método melhor do que o fornecido pelo Weld. Uma integração em um ambiente Java EE pode utilizar InjectionServices
para prover injeção de recursos EE para beans gerenciados.
InjectionServices
fornece um contrato muito simples, o interceptador InjectionServices.aroundInject(InjectionContext ic);
será chamado para cada instância que CDI injeta, se for uma instância contextual, ou não-contextual injetada por InjectionTarget.inject()
.
O InjectionContext
pode ser usado para descobrir informações adicionais sobre a injeção sendo realizada, incluindo o target
sendo injetado. ic.proceed()
deve ser chamado para realizar injeção no estilo CDI e chama os métodos inicializadores.
No intuito de obter um Principal
representando a identidade do requisitante atual, o contêiner deve fornecer uma implementação de SecurityServices
.
A fim de se obter o ValidatorFactory
padrão para a implantação da aplicação, o contêiner deve fornecer uma implementação de ValidationServices
.
Quando um cliente faz uma requisição a uma aplicação que utiliza o Weld, a requisição pode ser endereçada a qualquer dos BDAs na aplicação. Para permitir que o Weld sirva corretamente a requisição, ele precisa saber qual BDA a requisição está endereçada. Onde possível, o Weld fornecerá algum contexto, mas o uso deste pelo integrador é opcional.
A maioria das Servlet usam um classloader por war, isto pode prover um bom meio para identificar o BDA em uso pelas requisições web.
Quando o Weld precisa identificar o BDA, ele usará um destes serviços, dependendo do que está servindo a requisição:
ServletServices.getBeanDeploymentArchive(ServletContext ctx)
Identificar o war em uso. O ServletContext
é fornecido por contexto adicional.
O Weld utiliza um mapa como estrutura para armazenar instâncias de beans - org.jboss.weld.context.api.BeanStore
. Você pode achar org.jboss.weld.context.api.helpers.ConcurrentHashMapBeanStore
útil.
O Weld espera que o Servidor de Aplicação ou outro contêiner forneça o armazenamento para cada contexto da aplicação. O org.jboss.weld.context.api.BeanStore
deve ser implementado para prover um armazenamento em escopo de aplicação.
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.
O Weld precisa carregar classes e recursos a partir do classpath em vários momentos. Por padrão, eles são carregados a partir do ClassLoader do contexto da Thread se disponível, se não o mesmo classloader que foi usado para carregar o Weld, entretanto este pode não ser o correto para alguns embientes. Se este é caso, você pode implementar org.jboss.weld.spi.ResourceLoader
.
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.
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.
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).
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.
Existem uma série de meios que você pode obter o gerenciador de beans para o módulo. Você poderia chamar Bootstrap.getManager()
, passando o BDA deste módulo. Alternativamente, você poderia usar a injeção em classes de componentes Java EE, ou pesquisar o gerenciador de beans em JNDI.
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.
Weld somente suporta JSF 1.2 e versões posteriores.
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.
Existem uma série de meios que você pode obter o gerenciador de beans para o módulo. Você poderia chamar Bootstrap.getManager()
, passando o BDA deste módulo. Alternativamente, você poderia usar a injeção em classes de componentes Java EE, ou pesquisar o gerenciador de beans em JNDI.
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.
Você deve registrar o SessionBeanInterceptor
como o interceptador mais interno na pilha para todos EJBs.
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
.
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)
.
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();