SeamFramework.orgCommunity Documentation
If you want to use Weld in another environment, you will need to provide certain information to Weld via the integration SPI. In this Appendix we will briefly discuss the steps needed.
If you just want to use managed beans, and not take advantage of enterprise services (EE resource injection, CDI injection into EE component classes, transactional events, support for CDI services in EJBs) and non-flat deployments, then the generic servlet support provided by the "Weld: Servlets" extension will be sufficient, and will work in any container supporting the Servlet API.
All SPIs and APIs described have extensive JavaDoc, which spell out the detailed contract between the container and Weld.
The Weld SPI is located in the weld-spi
module, and packaged as
weld-spi.jar
. Some SPIs are optional, and should only be implemented if you need to override
the default behavior; others are required.
All interfaces in the SPI support the decorator pattern and provide a Forwarding
class
located in the helpers
sub package. Additional, commonly used, utility classes, and standard
implementations are also located in the helpers
sub package.
Weld supports multiple environments. An environment is defined by an implementation of the
Environment
interface. A number of standard environments are built in, and described by the
Environments
enumeration. Different environments require different services to be present
(for example a Servlet container doesn't require transaction, EJB or JPA services). By default an EE
environment is assumed, but you can adjust the environment by calling
bootstrap.setEnvironment()
.
Weld uses a generic-typed service registry to allow services to be registered. All services implement the
Service
interface. The service registry allows services to be added and retrieved.
An application is often comprised of a number of modules. For example, a Java EE deployment may contain a
number of EJB modules (containing business logic) and war modules (containing the user interface). A
container may enforce certain accessibility rules which limit the visibility of classes
between modules. CDI allows these same rules to apply to bean and observer method resolution. As the
accessibility rules vary between containers, Weld requires the container to describe
the deployment structure, via the Deployment
SPI.
The CDI specification discusses Bean Deployment Archives (BDAs)—archives which are marked as containing beans which should be deployed to the CDI container, and made available for injection and resolution. Weld reuses this description of Bean Deployment Archives in its deployment structure SPI. Each deployment exposes the BDAs which it contains; each BDA may also reference other which it can access. Together, the transitive closure of this graph forms the beans which are deployed in the application.
To describe the deployment structure to Weld, the container should provide an implementation of
Deployment
. Deployment.getBeanDeploymentArchives()
allows Weld to
discover the modules which make up the application. The CDI specification also allows beans to be specified
programmatically as part of the bean deployment. These beans may, or may not, be in an existing BDA. For
this reason, Weld will call Deployment.loadBeanDeploymentArchive(Class clazz)
for each
programmatically described bean.
As programmatically described beans may result in additional BDAs being added to the graph, Weld will
discover the BDA structure every time an unknown BDA is returned by
Deployment.loadBeanDeploymentArchive
.
In a strict container, each BDA might have to explicitly specify which other BDAs it can access. However
many containers will allow an easy mechanism to make BDAs bi-directionally accessible (such as a library
directory). In this case, it is allowable (and reasonable) to describe all such archives as a single,
'virtual' BeanDeploymentArchive
.
A container, might, for example, use a flat accessibility structure for the application. In this case, a
single BeanDeploymentArchive
would be attached to the Deployment
.
BeanDeploymentArchive
provides three methods which allow it's contents to be discovered
by Weld—BeanDeploymentArchive.getBeanClasses()
must return all the classes in the
BDA, BeanDeploymentArchive.getBeansXml()
must return all the deployment descriptors in
the archive, and BeanDeploymentArchive.getEjbs()
must provide an EJB descriptor for every
EJB in the BDA, or an empty list if it is not an EJB archive.
BDA X may also reference another BDA Y whose beans can be resolved by, and injected into, any bean in BDA X. These are the accessible BDAs, and every BDA that is directly accessible by BDA X should be returned. A BDA will also have BDAs which are accessible transitively, and the transitive closure of the sub-graph of BDA X describes all the beans resolvable by BDA X.
In practice, you can regard the deployment structure represented by Deployment
, and
the virtual BDA graph as a mirror of the classloader structure for a deployment. If a class can from BDA
X can be loaded by another in BDA Y, it is accessible, and therefore BDA Y's accessible BDAs should
include BDA X.
To specify the directly accessible BDAs, the container should provide an implementation of
BeanDeploymentArchive.getBeanDeploymentArchives()
.
Weld allows the container to describe a circular graph, and will convert a graph to a tree as part of the deployment process.
Certain services are provided for the whole deployment, whilst some are provided per-BDA. BDA services are
provided using BeanDeploymentArchive.getServices()
and only apply to the BDA on which
they are provided.
Weld delegates EJB 3 bean discovery to the container so that it doesn't duplicate the work done by the EJB container, and respects any vendor-extensions to the EJB definition.
The EjbDescriptor
should return the relevant metadata as defined in the EJB
specification. Each business interface of a session bean should be described using a
BusinessInterfaceDescriptor
.
All the EE resource services are per-BDA services, and may be provided using one of two methods. Which method to use is at the discretion of the integrator.
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
Section A.1.8, “Injection Services” 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 Section A.1.4, “EJB services”, Section A.1.7, “Resource Services” and Section A.1.5, “JPA services”.
CDI only provides annotation-based EE resource injection; if you wish to provide deployment descriptor
(e.g. ejb-jar.xml
) injection, you must use Section A.1.8, “Injection Services”.
If the container performs EE resource injection, the injected resources must be serializable. If EE resource injection is provided by Weld, the resolved resource must be serializable.
If you use a non-EE environment then you may implement any of the EE service SPIs, and Weld will provide the associated functionality. There is no need to implement those services you don't need!
EJB services are split between two interfaces which are both per-BDA.
EJBServices
is used to resolve local EJBs used to back session beans, and must always be
provided in an EE environment. EJBServices.resolveEjb(EjbDescriptor ejbDescriptor)
returns a wrapper—SessionObjectReference
—around the EJB reference. This
wrapper allows Weld to request a reference that implements the given business interface, and, in the case of
SFSBs, both request the removal of the EJB from the container and query whether the EJB has been previously
removed.
EJBResolutionServices.resolveEjb(InjectionPoint ij)
allows the resolution of
@EJB
(for injection into managed beans). This service is not required if the
implementation of Section A.1.8, “Injection Services” takes care of @EJB
injection.
Just as EJB resolution is delegated to the container, resolution of @PersistenceContext
for injection into managed beans (with the InjectionPoint
provided), is delegated to the
container.
To allow JPA integration, the JpaServices
interface should be implemented. This service
is not required if the implementation of Section A.1.8, “Injection Services” takes care of
@PersistenceContext
injection.
Weld delegates JTA activities to the container. The SPI provides a couple hooks to easily achieve this with
the TransactionServices
interface.
Any javax.transaction.Synchronization
implementation may be passed to the
registerSynchronization()
method and the SPI implementation should immediately register
the synchronization with the JTA transaction manager used for the EJBs.
To make it easier to determine whether or not a transaction is currently active for the requesting thread,
the isTransactionActive()
method can be used. The SPI implementation should query the
same JTA transaction manager used for the 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 Section A.1.8, “Injection Services”
takes care of @Resource
injection.
An integrator may wish to use InjectionServices
to provide additional field or method
injection over-and-above that provided by Weld. An integration into a Java EE environment may use
InjectionServices
to provide EE resource injection for managed beans.
InjectionServices
provides a very simple contract, the
InjectionServices.aroundInject(InjectionContext ic);
intercepter will be called for every
instance that CDI injects, whether it is a contextual instance, or a non-contextual instance injected by
InjectionTarget.inject()
.
The InjectionContext
can be used to discover additional information about the injection
being performed, including the target
being injected. ic.proceed()
should be called to perform CDI-style injection, and call initializer methods.
In order to obtain the Principal
representing the current caller identity, the container
should provide an implementation of SecurityServices
.
In order to obtain the default ValidatorFactory
for the application deployment, the
container should provide an implementation of ValidationServices
.
When a client makes a request to an application which uses Weld, the request may be addressed at any of the BDAs in the application deployment. To allow Weld to correctly service the request, it needs to know which BDA the request is addressed at. Where possible, Weld will provide some context, but use of these by the integrator is optional.
Most Servlet contains use a classloader-per-war, this may provide a good way to identify the BDA in use for web requests.
When Weld needs to identify the BDA, it will use one of these services, depending on what is servicing the request:
ServletServices.getBeanDeploymentArchive(ServletContext ctx)
Identify the war in use. The ServletContext
is provided for additional context.
Weld uses a map like structure to store bean instances -
org.jboss.weld.context.api.BeanStore
. You may find
org.jboss.weld.context.api.helpers.ConcurrentHashMapBeanStore
useful.
Weld expects the Application Server or other container to provide the storage for each application's
context. The org.jboss.weld.context.api.BeanStore
should be implemented to provide an
application scoped storage.
The org.jboss.weld.bootstrap.api.Bootstrap
interface defines the initialization for Weld,
bean deployment and bean validation. To boot Weld, you must create an instance of
org.jboss.weld.bootstrap.WeldBeansBootstrap
(which implements
Boostrap
), tell it about the services in use, and then request the container start.
The bootstrap is split into phases, container initialization, bean deployment, bean validation and shutdown. Initialization will create a manager, and add the built-in contexts, and examine the deployment structure. Bean deployment will deploy any beans (defined using annotations, programtically, or built in). Bean validation will validate all beans.
To initialize the container, you call Bootstrap.startInitialization()
. Before calling
startInitialization()
, you must register any services required by the environment. You
can do this by calling, for example, bootstrap.getServices().add(JpaServices.class, new
MyJpaServices())
. You must also provide the application context bean store.
Having called startInitialization()
, the Manager
for each BDA can be
obtained by calling Bootstrap.getManager(BeanDeploymentArchive bda)
.
To deploy the discovered beans, call Bootstrap.deployBeans()
.
To validate the deployed beans, call Bootstrap.validateBeans()
.
To place the container into a state where it can service requests, call
Bootstrap.endInitialization()
To shutdown the container you call Bootstrap.shutdown()
. This allows the container to
perform any cleanup operations needed.
Weld needs to load classes and resources from the classpath at various times. By default, they are loaded
from the Thread Context ClassLoader if available, if not the same classloader that was used to load Weld,
however this may not be correct for some environments. If this is case, you can implement
org.jboss.weld.spi.ResourceLoader
.
There are a number of requirements that Weld places on the container for correct functioning that fall outside implementation of APIs.
If you are integrating Weld into an environment that supports deployment of multiple applications, you must enable, automatically, or through user configuation, classloader isolation for each CDI application.
If you are integrating Weld into a Servlet environment you must register
org.jboss.weld.servlet.WeldListener
as a Servlet listener, either
automatically, or through user configuration, for each CDI application which uses Servlet.
You must ensure that that WeldListener.contextInitialized()
is called
after beans are deployed is complete (Bootstrap.deployBeans()
has been called).
If you are integrating Weld into a JSF environment you must register
org.jboss.weld.jsf.WeldPhaseListener
as a phase listener.
If you are integrating Weld into a JSF environment you must register
org.jboss.weld.el.WeldELContextListener
as an EL Context listener.
If you are integrating Weld into a JSF environment you must register
org.jboss.weld.jsf.ConversationAwareViewHandler
as a delegating view handler.
If you are integrating Weld into a JSF environment you must obtain the bean manager for the module and
then call BeanManager.wrapExpressionFactory()
, passing
Application.getExpressionFactory()
as the argument. The wrapped expression factory
must be used in all EL expression evaluations performed by JSF in this web application.
If you are integrating Weld into a JSF environment you must obtain the bean manager for the module and
then call BeanManager.getELResolver()
, The returned EL resolver should be
registered with JSF for this web application.
There are a number of ways you can obtain the bean manager for the module. You could call
Bootstrap.getManager()
, passing in the BDA for this module. Alternatively, you
could use the injection into Java EE component classes, or look up the bean manager in JNDI.
If you are integrating Weld into a JSF environment you must register
org.jboss.weld.servlet.ConversationPropagationFilter
as a Servlet listener,
either automatically, or through user configuration, for each CDI application which uses JSF.
This filter can be registered for all Servlet deployment safely.
Weld only supports JSF 1.2 and above.
If you are integrating Weld into a JSP environment you must register
org.jboss.weld.el.WeldELContextListener
as an EL Context listener.
If you are integrating Weld into a JSP environment you must obtain the bean manager for the module and
then call BeanManager.wrapExpressionFactory()
, passing
Application.getExpressionFactory()
as the argument. The wrapped expression factory
must be used in all EL expression evaluations performed by JSP.
If you are integrating Weld into a JSP environment you must obtain the bean manager for the module and
then call BeanManager.getELResolver()
, The returned EL resolver should be
registered with JSP for this web application.
There are a number of ways you can obtain the bean manager for the module. You could call
Bootstrap.getManager()
, passing in the BDA for this module. Alternatively, you
could use the injection into Java EE component classes, or look up the bean manager in JNDI.
If you are integrating Weld into an EJB environment you must register the aroundInvoke
method of
org.jboss.weld.ejb.SessionBeanInterceptor
as a EJB around-invoke interceptor for all EJBs in the
application, either automatically, or through user configuration, for each CDI application which
uses enterprise beans. If you are running in a EJB 3.1 environment, you should register this as an around-timeout
interceptor as well.
You must register the SessionBeanInterceptor
as the inner most interceptor in
the stack for all EJBs.
weld-core.jar
Weld can reside on an isolated classloader, or on a shared classloader. If you choose to use an
isolated classloader, the default SingletonProvider
,
IsolatedStaticSingletonProvider
, can be used. If you choose to use a shared
classloader, then you will need to choose another strategy.
You can provide your own implementation of Singleton
and
SingletonProvider
and register it for use using
SingletonProvider.initialize(SingletonProvider provider)
.
Weld also provides an implementation of Thread Context Classloader per application strategy, via the
TCCLSingletonProvider
.
You should bind the bean manager for the bean deployment archive into JNDI at
java:comp/Manager
. The type should be
javax.enterprise.inject.spi.BeanManager
. To obtain the correct bean manager for the
bean deployment archive, you may call
bootstrap.getBeanManager(beanDeploymentArchive)
The CDI specification requires the container to provide injection into non-contextual resources for
all Java EE component classes. Weld delegates this responsibility to the container. This can be
achieved using the CDI defined InjectionTarget
SPI. Furthermore, you must perform
this operation on the correct bean manager for the bean deployment archive containing the EE component
class.
The CDI specification also requires that a ProcessInjectionTarget
event is fired
for every Java EE component class. Furthermore, if an observer calls
ProcessInjectionTarget.setInjectionTarget()
the container must use the
specified injection target to perform injection.
To help the integrator, Weld provides WeldManager.fireProcessInjectionTarget()
which returns the InjectionTarget
to use.
// 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();
The container may intersperse other operations between these calls. Further, the integrator may choose to implement any of these calls in another manner, assuming the contract is fulfilled.
When performing injections on EJBs you must use the Weld-defined SPI,
WeldManager
. Furthermore, you must perform this operation on the correct bean
manager for the bean deployment archive containing the 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();