Appendix A: Integrating Weld into other environments

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.

Note
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

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.

Weld uses services to communicate with its environment. A service is a java class that implements the org.jboss.weld.bootstrap.api.Service interface and is explicitly registered. A service may be BDA-specific or may be shared across the entire application.

public interface Service {
   public void cleanup();
}

Certain services are only used at bootstrap and need to be cleaned up afterwards in order not to consume memory. A service that implements the specialized org.jboss.weld.bootstrap.api.BootstrapService interface receives a cleanupAfterBoot() method invocation once Weld initialization is finished but before the deployment is put into service.

public interface BootstrapService extends Service {
    void cleanupAfterBoot();
}

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.

Deployment structure

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 Archives (BAs)—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 and uses Bean Deployment Archives (BDA) in its deployment structure SPI.

Each deployment exposes the containing BDAs that form a graph. A node in the graph represents a BDA. Directed edges between nodes designate visibility. Visibility is not transitive (i.e. a bean from BDA A can only see beans in BDAs with which A is directly connected by a properly oriented edge).

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.

Note

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 a data structure representing the beans.xml deployment descriptor for 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.

To aid container integrator, Weld provides a built-in beans.xml parser. To parse a beans.xml into the data-structure required by BeanDeploymentArchive, the container should call Bootstrap.parse(URL). Weld can also parse multiple beans.xml files, merging them to become a single data-structure. This can be achieved by calling Bootstrap.parse(Iterable<URL>).

When multiple beans.xml files are merged, Weld keeps duplicate enabled entries (interceptors, decorators or alternatives). This may cause validation problems when multiple physical archives which define an overlapping enabled entries are merged. A version of the Bootstrap.parse() method that provides control over whether duplicate enabled entries are remove or not is provided: Bootstrap.parse(Iterable<URL> urls, boolean removeDuplicates).

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.

Note
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().

Note
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.

The contract for Deployment requires the container to specify the portable extensions (see chapter Packaging and deployment of the CDI specification) which should be loaded by the application. To aid the container integrator, Weld provides the method Bootstrap.loadExtensions(ClassLoader) which will load the extensions for the specified classloader.

EE Modules

In Java EE environment, description of each Java EE module that contains bean archives deployment should be provided. This applies to:

  • web modules (wars)

  • EJB modules

  • connector modules (rar)

  • application client modules

  • enterprise archive libraries (ear/lib)

For each such module the integrator should create an instance of the EEModuleDescriptor which describes the module. EEModuleDescriptorImpl is provided for convenience.

An EEModuleDescriptor instance that represents a given module should be registered as a per bean archive service in each BeanDeploymentArchive that belongs to the given module. This applies both to physical bean archives deployed within the given module and also to logical bean archives that belong to the module. Bean archives that are not part of a Java EE module (e.g. built-in server libraries) are not required to have a EEModuleDescriptor service registered.

EJB descriptors

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.

By default, Weld uses the EJB component class when creating new EJB instances. This may not always be desired especially if the EJB container uses subclassing internally. In such scenario, the EJB container requires that the subclass it generated is used for creating instances instead of the component class. An integrator can communicate such layout to Weld by additionally implementing the optional SubclassedComponentDescriptor interface in the EjbDescriptor implementation. The return value of the SubclassedComponentDescriptor.getComponentSubclass() method determines which class will be used by Weld when creating new EJB instances.

EE resource injection and resolution services

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 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 EJB services, Resource Services and JPA services.

Important
CDI only provides annotation-based EE resource injection; if you wish to provide deployment descriptor (e.g. ejb-jar.xml) injection, you must use 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.

Tip
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!

Weld registers resource injection points with EjbInjectionServices, JpaInjectionServices, ResourceInjectionServices and JaxwsInjectionServices implementations upfront (at bootstrap). This allows validation of resource injection points to be performed at boot time rather than runtime. For each resource injection point Weld obtains a ResourceReferenceFactory which it then uses at runtime for creating resource references.

public interface ResourceReferenceFactory<T> {
    ResourceReference<T> createResource();
}

A ResourceReference provides access to the resource reference to be injected. Furthermore, ResourceReference allows resource to be release once the bean that received resource injection is destroyed.

public interface ResourceReference<T> {
    T getInstance();
    void release();
}

EJB services

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.

EjbInjectionServices.registerEjbInjectionPoint(InjectionPoint injectionPoint) registers an @EJB injection point (on a managed bean) and returns a ResourceReferenceFactory as explained above. This service is not required if the implementation of Injection Services takes care of @EJB injection.

Note
EJBInjectionServices.resolveEjb(InjectionPoint ij), which allows @EJB injection point to be resolved without prior registration was deprecated in Weld 2 and should no longer be used. An injection point should be registered properly using EjbInjectionServices.registerEjbInjectionPoint(InjectionPoint injectionPoint) instead.

JPA services

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 org.jboss.weld.injection.spi.JpaInjectionServices interface should be implemented. This service is not required if the implementation of Injection Services takes care of @PersistenceContext injection.

Note

The following methods were deprecated in Weld 2:

* JpaInjectionServices.resolvePersistenceContext(InjectionPoint injectionPoint)

* JpaInjectionServices.resolvePersistenceUnit(InjectionPoint injectionPoint)

An injection point should instead be registered properly using the following methods:

* JpaInjectionServices.registerPersistenceContextInjectionPoint(InjectionPoint injectionPoint) * JpaInjectionServices.registerPersistenceUnitInjectionPoint(InjectionPoint injectionPoint)

Transaction Services

Weld delegates JTA activities to the container. The SPI provides a couple hooks to easily achieve this with the TransactionServices interface.

Any jakarta.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.

Resource Services

The resolution of @Resource (for injection into managed beans) is delegated to the container. You must provide an implementation of ResourceInjectionServices which provides these operations. This service is not required if the implementation of Injection Services takes care of @Resource injection.

Note

The following methods were deprecated in Weld 2:

* ResourceInjectionServices.resolveResource(InjectionPoint injectionPoint)

* ResourceInjectionServices.resolveResource(String jndiName, String mappedName)

An injection point should instead be registered properly using the following methods:

* ResourceInjectionServices.registerResourceInjectionPoint(InjectionPoint injectionPoint)

* ResourceInjectionServices.registerResourceInjectionPoint(String jndiName, String mappedName)

Web Service Injection Services

The resolution of @WebServiceRef (for injection into managed beans) is delegated to the container. An integrator must provide an implementation of JaxwsInjectionServices. This service is not required if the implementation of Injection Services takes care of @WebServiceRef injection.

Injection Services

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); interceptor 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.

Resource injection point validation

For each

  • @Resource injection point

  • @PersistenceContext injection point

  • @PersistenceUnit injection point

  • @EJB injection point

  • @WebServiceRef injection point

Weld calls the InjectionServices.registerInjectionTarget() method. That allows the integrator to validate resource injection points before the application is deployed.

Security Services

In order to obtain the Principal representing the current caller identity, the container should provide an implementation of SecurityServices.

Initialization and shutdown

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.WeldBootstrap which implements org.jboss.weld.bootstrap.api.CDI11Bootstrap which extends the above mentioned Bootstrap. Then you need to tell it about the services in use, and finally request the container start.

public interface Bootstrap {
    public Bootstrap startContainer(Environment environment, Deployment deployment);
    public Bootstrap startInitialization();
    public Bootstrap deployBeans();
    public Bootstrap validateBeans();
    public Bootstrap endInitialization();
    public void shutdown();
    public WeldManager getManager(BeanDeploymentArchive beanDeploymentArchive);
    public BeansXml parse(URL url);
    public BeansXml parse(Iterable<URL> urls);
    public BeansXml parse(Iterable<URL> urls, boolean removeDuplicates);
    public Iterable<Metadata<Extension>> loadExtensions(ClassLoader classLoader);
}

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, programmatically, 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.getManager().getServices().add(JpaServices.class, new MyJpaServices()).

Having called startInitialization(), the org.jboss.weld.manager.api.WeldManager 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()

Note
Integrators can set org.jboss.weld.bootstrap.allowOptimizedCleanup configuration property using Defining external configuration to allow to perform efficient cleanup and further optimizations after bootstrap. In this case, Bootstrap.endInitialization() must be called after all EE components which support injection are installed (that means all relevant ProcessInjectionTarget events were already fired).

To shutdown the container you call Bootstrap.shutdown(). This allows the container to perform any cleanup operations needed.

Resource loading

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.resources.spi.ResourceLoader.

import org.jboss.weld.bootstrap.api.Service;

public interface ResourceLoader extends Service {
    public Class<?> classForName(String name);
    public URL getResource(String name);
    public Collection<URL> getResources(String name);
}

ClassFileServices

Integrators with bytecode-scanning capabilities may implement an optional ClassFileServices service.

Bytecode-scanning is used by some application servers to speed up deployment. Compared to loading a class using ClassLoader, bytecode-scanning allows to obtain only a subset of the Java class file metadata (e.g. annotations, class hierarchy, etc.) which is usually loaded much faster. This allows the container to scan all classes initially by a bytecode scanner and then use this limited information to decide which classes need to be fully loaded using ClassLoader. Jandex is an example of a bytecode-scanning utility.

ClassFileServices may be used by an integrator to provide container’s bytecode-scanning capabilities to Weld. If present, Weld will try to use the service to avoid loading of classes that do not need to be loaded. These are classes that:

  • are not CDI managed beans and

  • are not assignable to any ProcessAnnotatedType observer

This usually yields improved bootstrap performance especially in large deployments with a lot of classes in explicit bean archives.

public interface ClassFileServices extends BootstrapService {
    ClassFileInfo getClassFileInfo(String className);
}
public interface ClassFileInfo {
    String getClassName();
    String getSuperclassName();
    boolean isAnnotationDeclared(Class<? extends Annotation> annotationType);
    boolean containsAnnotation(Class<? extends Annotation> annotationType);
    int getModifiers();
    boolean hasCdiConstructor();
    boolean isAssignableFrom(Class<?> javaClass);
    boolean isAssignableTo(Class<?> javaClass);
    boolean isVetoed();
    boolean isTopLevelClass();
    NestingType getNestingType();

See the JavaDoc for more details.

Registering services

The standard way for an integrator to provide Service implementations is via the deployment structure. Alternatively, services may be registered using the ServiceLoader mechanism. This is useful e.g. for a library running in weld-servlet environment. Such library may provide TransactionServices implementation which would not otherwise be provided by weld-servlet.

A service implementation should be listed in a file named META-INF/services/org.jboss.weld.bootstrap.api.Service

A service implementation can override another service implementation. The priority of a service implementation is determined from the jakarta.annotation.Priority annotation. Service implementations with higher priority have precedence. A service implementation that does not define priority explicitly is given implicit priority of 4500.

The contract with the container

There are a number of requirements that Weld places on the container for correct functioning that fall outside implementation of APIs.

Classloader isolation

If you are integrating Weld into an environment that supports deployment of multiple applications, you must enable, automatically, or through user configuration, classloader isolation for each CDI application.

Servlet

If you are integrating Weld into a Servlet environment you must register org.jboss.weld.servlet.WeldInitialListener and org.jboss.weld.servlet.WeldTerminalListener as Servlet listeners, either automatically, or through user configuration, for each CDI application which uses Servlet.

You must ensure that WeldInitialListener is called before any other application-defined listener is called and that WeldTerminalListener is called only after all application-defined listeners have been called.

You must ensure that WeldInitialListener.contextInitialized() is called after beans are deployed is complete (Bootstrap.deployBeans() has been called).

CDI Conversation Filter

A CDI implementation is required to provide a Servlet filter named ``CDI Conversation Filter''. The filter may be mapped by an application in the web descriptor. That allows application to place another filter around the CDI filter for dealing with exceptions.

Weld provides this filter with a fully qualified class name of`org.jboss.weld.servlet.ConversationFilter`.

If the application contains a filter mapping for a filter named CDI Conversation Filter'', the integrator is required to register org.jboss.weld.servlet.ConversationFilter as a filter with CDI Conversation Filter'' as its filter name. If no such mapping exists in the application, the integrator is not required to register the filter. In that case, WeldInitialListener will take care of conversation context activation/deactivation at the beginning of HTTP request processing.

JSF

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.

Tip
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.

Note
Weld 3 only supports JSF 2.2 and above.
Note
org.jboss.weld.servlet.ConversationPropagationFilter was deprecated and should no longer be used.

JSP

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.

Tip
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.

Session Bean Interceptor

org.jboss.weld.ejb.SessionBeanInterceptor takes care of activating the request scope around EJB method invocations in a non-servlet environment, such as message-driven bean invocation, @Asynchronous invocation or @Timeout. If you are integrating Weld into an EJB environment you must register the aroundInvoke method of 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.2 environment, you should register this as an around-timeout interceptor as well.

In addition, since CDI 1.1 the aroundInvoke method of SessionBeanInterceptor should be invoked around @PostConstruct callbacks of EJBs.

Important
You must register the SessionBeanInterceptor as the outer most interceptor in the stack for all EJBs.

The 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.

Binding the manager in JNDI

You should bind the bean manager for the bean deployment archive into JNDI at java:comp/BeanManager. The type should be jakarta.enterprise.inject.spi.BeanManager. To obtain the correct bean manager for the bean deployment archive, you may call bootstrap.getBeanManager(beanDeploymentArchive)

CDIProvider

CDI 1.1 provides a simplified approach to accessing the BeanManager / CDI container from components that do not support injection. This is done by the CDI class API. The integrating part can either use org.jboss.weld.AbstractCDI or org.jboss.weld.SimpleCDI provided by Weld core and register it using jakarta.enterprise.inject.spi.CDIProvider file that is visible to the CDI API classes or use the CDI.setCDIProvider(CDIProvider provider) method method early in the deployment.

Alternatively, an integrating part may provide a specialized implementation such as the one provided by WildFly integration.

Performing CDI injection on Java EE component classes

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
AnnotatedType<?> at = weldBeanManager.createAnnotatedType(clazz);
InjectionTarget it = weldBeanManager.fireProcessInjectionTarget(at);

// 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(instance);
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<T> ejbDescriptor = beanManager.<T>getEjbDescriptor(ejbName);

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

// Create AnnotatedType from the implementation class of the EJB descriptor
AnnotatedType<T> at = beanManager.createAnnotatedType(descriptorImplClazz);

// Create the injection target
InjectionTarget<T> it = beanManager.createInjectionTarget(ejbDescriptor);

// Fire ProcessInjectionTarget event and store new IT as it can be modified by extensions
it = beanManager.fireProcessInjectionTarget(at, it);

// Per instance required, create the creational context
WeldCreationalContext<T> cc = beanManager.createCreationalContext(bean);

// register an AroundConstructCallback if needed
cc.setConstructorInterceptionSuppressed(true);
cc.registerAroundConstructCallback(new AroundConstructCallback<T>() {
    public T aroundConstruct(ConstructionHandle<T> handle, AnnotatedConstructor<T> constructor, Object[] parameters,
            Map<String, Object> data) throws Exception {
        // TODO: invoke @AroundConstruct interceptors
        return handle.proceed(parameters, data);
    }
});

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

// 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(instance);
cc.release();

Around-construct interception

Weld implements support for constructor call interception and invokes interceptors that are associated with the particular component either using an interceptor binding or the @Interceptors annotation.

This can be suppressed by calling WeldCreationalContext.setConstructorInterceptionSuppressed(true)

In addition, an integrator may register a callback in which it performs additional operations around the constructor call. This way an integrator may for example implement support for additional interceptors (e.g. those bound using the deployment descriptor).

Optimized cleanup after bootstrap

Weld can perfom additional cleanup operations after bootstrap, in order to conserve resources. See for example Memory consumption optimization - removing unused beans .

However, this feature is disabled by default. An integrator may enable this feature provided the following requirements are met:

  • org.jboss.weld.bootstrap.api.Bootstrap#endInitialization() must be called after all EE components which support injection are installed (that means all relevant ProcessInjectionTarget events were already fired)

Table 1. Supported configuration properties
Configuration key Default value Description

org.jboss.weld.bootstrap.allowOptimizedCleanup

false

If set to true Weld is allowed to perform efficient cleanup and further optimizations after bootstrap

Note
This property can only be set by integrators through Defining external configuration .

Migration notes

This part of the appendix documents the changes in Weld from previous major version and the changes in current minor releases that an integrator should be aware of. These changes mostly touch changes in the SPI or in the container contract. For information on migration to older Weld versions, please consult their respective documentation.

Migration from Weld 4.0 to 5.0

Weld 5 is a compatible implementation of Jakarta CDI 4.0 which is a major version bump that brings new APIs, removal of deprecated API, breaking changes and the introduction of CDI Lite.

Java 11

Both, Weld 5.0 and CDI 4.0 require at least Java 11 for compile time and runtime but both are made to run with JDK 17 as well.

CDI Lite and CDI Full

By far the biggest change in CDI 4.0 is the introduction of CDI Lite. In short, CDI Lite is a feature subset of CDI Full that broadens the amount of environments in which CDI can execute; more precisely, it enables CDI in build time oriented frameworks such as Micronaut or Quarkus. What does that means for Weld users? Nothing much, Weld is and will be a runtime oriented implementation and you can keep using CDI as you did up until now. To be exact, Weld is so called CDI Full implementation meaning it provides each and every CDI feature and while there were some deprecations and removals, the rest stays the same.

Empty beans.xml discovery mode

CDI 4 changes the default discovery mode used for empty beans.xml - it is now annotated mode. This aligns nicely with recommendations from the past and saves memory and boot times by discovering only annotated beans as opposed to scanning everything.

For existing applications with empty beans.xml, this might be a breaking change. This can typically be fixed via:

  • Making sure all needed beans have required annotations. This mostly boils down to adding bean defining annotations to classes that are supposed to be beans but have none of them.

    • Typically, you will see a lot of unsatisfied dependency injections which should give you a hint at which beans are most likely missing an annotation

  • Changing beans.xml to non-empty and adding <beans> element with attribute bean-discovery-mode="all". This will revert the discovery mode to previously used all mode.

  • In EE servers, CDI specification requires integrators to provide a compatibility switch that triggers previous behavior

    • Note that this is a temporary measure to ensure backward compatibility and it will be removed in the future. Applications are encouraged to eventually apply one of the above solutions.

    • Consult the documentation of each integrator (such as WildFly) to learn how to enable this option.

Notes for integrators

If you are an integrator looking to implement this switch with Weld, here are a few leads. Depending on how exactly you integrate Weld, you are likely using one of the following ways to parse beans.xml:

  • Directly invoking parser via org.jboss.weld.xml.BeansXmlStreamParser.

    • BeansXmlStreamParser now has new constructor methods which allow passing a BeanDiscoveryMode parameter telling Weld how to interpret empty beans.xml.

    • All former constructors were retained and default to BeanDiscoveryMode.ANNOTATED .

  • Indirectly invoking it via an implementation of org.jboss.weld.bootstrap.api.Bootstrap and its parse methods.

    • New variantions of the parse methods were added and accept BeanDiscoveryMode parameter telling Weld how to interpret empty beans.xml.

    • All former variations of parse methods were retained and default to BeanDiscoveryMode.ANNOTATED.

Build Compatible Extensions

With the addition of CDI Lite, the one big new feature are so-called Build Compatible Extensions (BCE). Their purpose is very similar to that of Portable Extensions (PE) - synthetic alteration of the bean model. Unlike PE which are inherently runtime oriented, BCE can be used in more restricted environments while retaining portability. If you want to learn more, take a look at this article.

How to make Weld understand Build Compatible Extensions?

Since BCE use a different language model but are otherwise similar, Weld internally maps their execution to a special Portable Extension named LiteExtensionTranslator. This extension is inside a newly added Maven module - weld-lite-extension-translator. The GAV of this artifact is org.jboss.weld:weld-lite-extension-translator:5.0.0.Final.

Just like any Portable Extension, it needs an entry in META-INF or an instance of this extensions has to be added programmaticaly during Weld bootstrap. This is where it gets tricky; it doesn’t work out of the box in all environments!

  • Weld SE - works out of the box, the extension is automagically added

  • Weld Servlet - works out of the box, the extensions is automagically added

  • EE - doesn’t work without integrator registering the extension

  • Any other custom integration - doesn’t work without integrator registering the extension

In complex/custom environments, Weld doesn’t have enough information to decide when and how to register the extension, hence it instead lets the integrator decide.

Registering LiteExtensionTranslator

Registering the LiteExtensionTranslator can be as easy as adding a META-INF entry with it into some archive in the deployment. An example of this would be arquillian-container-weld, a test container for Weld applications. As you can see from this pull request, it basically adds a META-INF entry which is sufficient to support new extension model. Since it a test tool, the extension is always registered without any attempt to optimize it.

The above solution is nice and clean, but won’t cut it for any EE integrators. Instead, EE integrators will want to register this extension by adding it programmatically as part of the CDI deployment. This extension should to be registered at most once per deployment. It is therefore recommended to put it directly into org.jboss.weld.bootstrap.spi.CDI11Deployment implementation.

Lastly, this process can and should be optimized so that the extension is only registered when there is at least one BCE detected. The reason being that the extension monitors a wide variety of generic events, such ProcessAnnotatedType<?>, which can be time consuming for bigger applications. There is a utility class to help with this - org.jboss.weld.lite.extension.translator.BuildCompatibleExtensionLoader - which allows performing a standard service discovery process with given ClassLoader and returns the set of discovered BCE classes. Integrators can then instantiate LiteExtensionTranslator via a constructor that takes this set as an input; or skip registering it if there are no BCE at all.

Deprecations and removals

This section mostly focuses on removals in Weld itself. If you are looking for deprecation removals in CDI API, take a look at this GH issue.

  • Multiple methods from org.jboss.weld.serialization.spi.ProxyServices were removed.

    • If you are an integrator, you probably implemented this interface at some point in time; these methods weren’t used by Weld anyway, so they are now gone for good.

    • Here is a JIRA issue with more details and PR link.

  • Any integrators making use of Jandex likely implemented ClassFileInfo; its isTopLevelClass() has been removed.

    • Note that this method was redundant and can be replaced by getClassNestingType().

  • WeldInstance.Handler class is now deprecated and so are multiple methods returning this type from WeldInstance

    • The reason for this removal is that the very same feature was accepted into CDI specification so you can now use CDI API interfaces to achieve the same.

    • For more information, take a look at Instance.Handle class from CDI API.