An introduction to the SeBootstrap Spec and the RESTEasy Implementation

SeBootstrap is a feature defined by the Jakarta Spec:

Here is the javadoc of the jakarta.ws.rs.SeBootstrap interface:

The above javadoc explains the purpose of SeBootstrap. Now let’s see its design.

The design of the SeBootstrap

Here is the class diagram of the SeBootstrap interfaces in the spec:

Here is the start() method inside the SeBootstrap interface:

We can see it will use a RuntimeDelegate instance to bootstrap the Application instance. The RuntimeDelegate should be implemented by the implementation frameworks. For example, RESTEasy is one of the implementation frameworks and it provides org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl to implement the interface. To understand how does the framework implementation loading process work, you can check this article:

Next let’s see how RESTEasy implements the SeBootstrap feature.

The RESTEasy SeBootstrap Implementation

Here is the class diagram related with the implementation of RESTEasy SeBootstrap feature:

Here are a few notes to the above classes:

The core part of the implementation is the ResteasySeInstance class, which create a concrete SeBootstrap Instance for use. Here is the code of the create() method in the ResteasySeInstance class:

/**
 * Creates a new {@link Instance} based on the {@linkplain Application application} and
 * {@linkplain Configuration configuration} passed in.
 * <p>
 * Note that if your {@link Application} does not override the {@link Application#getClasses()} or
 * {@link Application#getSingletons()} a {@linkplain Index Jandex index} is used to find resources and providers.
 * It's suggested that your application has a {@code META-INF/jandex.idx} or you provide an index with the
 * {@link ConfigurationOption#JANDEX_INDEX} configuration option. If neither of those exist, the class path itself
 * is indexed which could have significant performance impacts.
 * </p>
 *
 * @param application   the application to use for this instance
 * @param configuration the configuration used to configure the instance
 *
 * @return a {@link CompletionStage} which asynchronously produces and {@link Instance}
 */
public static CompletionStage<Instance> create(final Application application,
                                               final Configuration configuration) {
    final ExecutorService executor = ContextualExecutors.threadPool();
    return CompletableFuture.supplyAsync(() -> {
        try {
            final Configuration config = ResteasySeConfiguration.from(configuration);
            final EmbeddedServer server = EmbeddedServers.findServer(config);
            final ResteasyDeployment deployment = server.getDeployment();
            deployment.setRegisterBuiltin(ConfigurationOption.REGISTER_BUILT_INS.getValue(config));
            deployment.setApplication(application);
            try {
                scanForResources(deployment, application, config);
            } catch (IOException e) {
                throw Messages.MESSAGES.failedToScanResources(e);
            }
            deployment.start();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debugf("Application %s used for %s", deployment.getApplication(), server);
                deployment.getResourceClasses()
                        .forEach(name -> LOGGER.debugf("Resource %s found for %s", name, server));
                deployment.getProviderClasses()
                        .forEach(name -> LOGGER.debugf("Provider %s found for %s", name, server));
            }
            server.start(config);
            return new ResteasySeInstance(server, config, executor);
        } catch (Throwable t) {
            throw new CompletionException(t);
        }
    }, executor);
}

From the above code we can see the create() method of the ResteasySeInstance class takes the duty to setup the RESTEasy engine properly for use. And this line of the code takes the core role:

final EmbeddedServer server = EmbeddedServers.findServer(config);

As the code shown above, RESTEasy defines a EmbeddedServer interface, and then try to find a server implementation from the runtime, and loads it. Finally it starts the server and wraps it into ResteasySeInstance:

server.start(config);
return new ResteasySeInstance(server, config, executor);

Here is the class diagram of the EmbeddedServer:

And RESTEasy already provides several implementations to the above interface:

As the implementation list shown above, we can see RESTEasy provides several server backends for using. Until now, we have learned the design of the Bootstrap interfaces and its implementation in the RESTEasy framework. As this article is about the design of the RESTEasy SeBootstrap feature, not its usages, so I won’t explain its usages. I’ll put the introduction of the usages in a separate article.

My Github Page: https://github.com/liweinan

Powered by Jekyll and Theme by solid

If you have any question want to ask or find bugs regarding with my blog posts, please report it here:
https://github.com/liweinan/liweinan.github.io/issues