© Luqman Saeed 2020Luqman SaeedIntroducing Jakarta EE CDIhttps://doi.org/10.1007/978-1-4842-5642-8_10
10. The CDI Ecosystem
The Contexts and Dependency Injection (CDI) API is at the heart of the entire Jakarta EE platform. CDI provides a powerful Service Provider Interface (SPI) that allows third-party libraries to create portable extensions for the platform. In effect, CDI makes it easy for other projects to integrate into Jakarta EE.
A portable extension may integrate into the platform by
- Providing its beans, interceptors, and decorators to the container
- Injecting dependencies into its objects using the dependency injection service
- Providing a context implementation for a custom scope
- Augmenting or overriding the annotation-based metadata with metadata from some other source
Eclipse MicroProfile is an Eclipse Foundation-led project aimed at making the Jakarta EE platform a cloud-native enterprise development choice. It features a number of APIs that together form a powerful toolset for creating resilient, portable enterprise Java applications.
The most obvious CDI extension from MicroProfile is the Config API. The MP Config API aims at simplifying application configuration in different environments and from different sources without the need to redeploy or restart the application.
An application might need different port numbers for different requests, or certain features need to be switched on or off based on certain configurations. The Config API aims at externalizing application configuration so that changes can be made without having to restart/redeploy your code.
To get started with MicroProfile, you need to add the dependency to your project, as shown here. Almost all the Jakarta EE app servers support MP.
This code snippet adds the latest MicroProfile API to your project dependencies, giving you access to the entire suite of sub-projects. For example, assume that in the restaurant app, in the SelfService.java class, you need a configurable string that contains a welcome message for self-serve customers.
To achieve this, you first need to create at least one config source—a place to get values from—in the app. The following code snippet shows the microprofile-config.properties file , located in the META-INF folder.
This code snippet shows a key, called self-service, that has a string value. To use this in your code, you simply create a String field and inject this value into it using a qualifier from the MicroProfile Config API, as shown here.
Line 1 of this code snippet uses the @Inject annotation in combination with @ConfigProperty on Line 2 to inject the value of the field defined in the properties file. @ConfigProperty is a MicroProfile Config Qualifier that takes two parameters—name is the key of the config value you want and a default value in the absence of a value in any of the config sources.
In this code, you set the name of the @ConfigProperty annotation to the key of the self-service string. You could as well have defined a default value, as shown next, to be injected into the selfServiceWelcome field in the absence of a value in the properties file or any other config source.
From this code snippet, in the absence of a value, the default value of “Hello, and welcome” will be injected into the selfServiceWelcome field. @ConfigProperty is defined as a CDI Qualifier, as shown here.
Line 1 declares the annotation ConfigProperty as a qualifier. Lines 8 and 10 both declare the two parameters of the annotation—name and defaultValue. As you can see, the @ConfigProperty annotation is a plain CDI qualifier that the MicroProfile Config API leverages to help externalize application configuration.
The use of @ConfigProperty is static in the sense that the values do not change even when the values change in the source of the configuration. So in this example, even if the value of the self-service config property entry changes, the injected field might not necessarily change. To truly get dynamic config values, you can use another CDI construct with the Config API, as shown next.
Lines 1 and 2 use @Inject and @ConfigProperties on the myDynamicValue field. The type of myDyanmicValue, however, is a Provider from the CDI API, defined as shown next.
Provider is a parameterized interface from the CDI API that has one method, get(). It returns the parameterized type. As used in the earlier code, setting the type of myDynamicValue to Provider means any time you call the get() method on it, a new instance of type String is returned with a possibly updated value from the config source. The following code snippet shows the get() method invocation on myDynamicValue to be logged.
The entire MicroProfile project is built on the Jakarta EE platform and as such, it leverages the power of the platform to help you craft cloud-native enterprise Java applications.
Apache Delta Spike
Delta Spike is an Apache top-level project that has modules in the form of CDI extensions that provide additional functionality for your Jakarta EE application. Some of the modules include the JSF, Scheduler, Security, and Data. This section examines the Data module.
The Data module provides capabilities for implementing repository patterns and thereby simplifying the repository layer. Repository patterns are ideal for simple queries that require boilerplate code, enabling centralization of query logic and consequently reducing code duplication and improving testability.3
For the restaurant application, you can convert the ApplicationUser bean to a JPA entity, as shown here.
The ApplicationUser entity has a number of fields that, in a typical database-driven application, you would want to query on. For instance, you might want to find a user by userName, run another query to search by email, and so on. The Delta Spike module helps you reduce such tedious boilerplate code by using CDI-based repositories. You can declare an ApplicationUserRepo that will handle the basic CRUD on the fields of the entity class for you.
Line 1 declares the @Repository annotation from the Delta Spike data module on the ApplicationUserRepo interface, which extends the parameterized EntityRepository, which is also from the same Delta Spike data module. The EntityRepository interface is defined as follows:
This interface has some basic CRUD-based methods that almost every database application implements. For instance, Line 12 declares a findBy() method that takes the primary key of an entity and queries the database for it. All of these methods are implemented by the data module CDI extension. Next, you’ll declare some methods in the interface for CRUD purposes.
Line 5 declares a findByEmail method. This method will search for a user by their email. Lines 7, 9, and 11 declare the findByUserName, findOptionalByHashedPassword, and findByUserNameLike methods, respectively. To use the repository, you just inject it into your QuerySerivce class , as shown here.
Line 5 declares a CDI-injected field of type ApplicationUserRepo into the QueryService class. With this, you can implement most of the tedious CRUD boilerplate code without having to write a fraction of it, as shown next.
QueryService has a number of method declarations that use the ApplicationUserRepo injected on Line 5. As you can see, some of the methods are being used directly from the EntityRepository, such as the findOptionalBy() method, which takes the ID of an entity and returns an Optional object typed to the entity. The findAll method also invokes the findAll method, which is found directly on the EntityRepository interface.
All of these methods are implemented on your behalf using a combination of CDI API constructs. For instance, the Delta Spike module requires a producer field of type EntityManager in order to be able to intercept and implement repository methods. The EntityManager producer method suffices for the requirement.
The repository can also be used for persisting into the database. Remember it comes with a save() method that takes an entity and persists or merges it into the persistent context, depending on the state of the entity. You can use ApplicationUserRepo in your persistence service to persist ApplicationUser entities.
PersistenceService is a stateless EJB that declares a dependency on ApplicationUserRepo on Line 9. Then in the persistUser method, on Line 14, the injected repository is used to persist an ApplicationUser entity. The save() method on the repository is also automatically intercepted and implemented by the Delta Spike module on your behalf.