This project consists of a CDI portable
extension that
embeds a Helidon
WebServer
into a CDI environment and starts
it.
The portable extension will look for any
Service
class it can find that is
detectable.
It will instantiate it and call its
update(Routing.Rules)
method prior to starting the WebServer
.
You may be thinking: why would I want to put a web server in a CDI container? This is probably because you are thinking of CDI as a component of a Java EE application server. But CDI 2.0 is usable in Java SE.
If you start the CDI container from your own main
method like so:
try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
// deliberately empty
}
...or, equivalently, use the microbean-main
project's Main
class to do this
for you, then a CDI container comes up and goes down. In between all of your
CDI beans are instantiated and wired appropriately.
So don't think of a CDI container as something that lives inside a web server or alongside one. Think instead of a web server as just another Java component in the CDI ecosystem.
At this point you can probably see that if Helidon SE is just another component
in the CDI ecosystem, and your Service
implementations are just another
component in the CDI ecosystem, then the CDI container can instantiate and
autowire all of them equivalently.
This gives you all the benefits of Helidon SE and all the benefits of CDI at the expense of roughly 50 milliseconds of additional startup time while letting you write a simple Java SE application from a component-oriented perspective.
Suppose you write a
Service
like this:
import io.helidon.webserver.Service;
import io.helidon.webserver.Routing;
import io.helidon.webserver.ServerRequest;
import io.helidon.webserver.ServerResponse;
@ApplicationScoped
public class MyService implements Service {
private final SomeClientForSomething client;
@Inject
public MyService(final SomeClientForSomething client) {
super();
this.client = Objects.requireNonNull(client);
}
@Override
public void update(final Routing.Rules rules) {
rules.get("/foo", this::handleFoo); // handle GET requests with the handleFoo method
}
private void handleFoo(final ServerRequest request,
final ServerResponse response) {
// You can use this.client here.
}
}
The presence of this class on the classpath will cause the CDI
container to create an instance of MyService
, supply it with an
instance of SomeClientForSomething
assuming that this bean is
present in the CDI container, and call its update
method.
A Helidon
WebServer
will then be created and
started
using default
configuration.
The webserver will stop when it receives a CTRL-C
signal.
All intermediate objects required to create and start a WebServer
are treated as CDI beans as well, with the end user's versions of
those beans (if any) being preferred over the built-in defaults. So,
for example, if you were to supply your own WebServer
producer
method, that WebServer
instance would be used instead.
If you do nothing, a default
Config.Builder
will be created to build a
Config
object. The Config
object so built will be used to create a
ServerConfiguration.Builder
,
which, in turn, will be used to build a
ServerConfiguration
.
This general pattern applies all the way up to the
WebServer
itself (built by a
WebServer.Builder
).
Each of these objects is treated as a CDI bean. If the CDI bean in question does not exist, then a "normal" instance of the object in question is registered as a bean and instantiated appropriately by the CDI container.
In the case of such default instantiations, you can customize them if
you want. For example, maybe you don't want to actually supply an
alternate Config
implementation—the default one is just
fine—but you do want to slightly change how it's made. To
perform simple customization of this sort, you declare an observer
method that receives the relevant Builder
object as its observed
parameter. For example, somewhere in your CDI application, you can
write:
private static final void customizeConfigBuilder(@Observes final Config.Builder configBuilder) {
builder.disableSystemPropertiesSource(); // or whatever
}
The Builder
so customized will be used internally by this project to
produce Config
objects (as CDI beans) that may be required by other
Helidon objects.