jakarta.ws.rs.SeBootstrap Maven / Gradle / Ivy
/*
* Copyright (c) 2018 Markus KARG. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.ws.rs;
import java.net.URI;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import javax.net.ssl.SSLContext;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.UriBuilder;
import jakarta.ws.rs.ext.RuntimeDelegate;
/**
* Bootstrap class used to startup a JAX-RS application in Java SE environments.
*
* The {@code SeBootstrap} class is available in a Jakarta EE container environment as well; however, support for the Java SE
* bootstrapping APIs is not required in container environments.
*
*
* In a Java SE environment an application is getting started by the following command using default configuration
* values (i. e. mounting application at {@code http://localhost:80/} or a different port (there is no
* particular default port mandated by this specification). As the JAX-RS implementation is free to choose any port
* by default, the caller will not know the actual port unless explicitly checking the actual configuration of the
* instance started:
*
*
*
* Application app = new MyApplication();
* SeBootstrap.Configuration config = SeBootstrap.Configuration.builder().build();
* SeBootstrap.start(app, config).thenAccept(instance -> instance.configuration().port());
*
*
*
* Running instances can be instructed to stop serving the application:
*
*
*
* SeBootstrap.start(app, config).thenAccept(instance -> { ... instance.stop(); } );
*
*
*
* A shutdown callback can be registered which will get invoked once the implementation stops serving the application:
*
*
*
* instance.stop().thenAccept(stopResult -> ...));
*
*
* {@code stopResult} is not further defined but solely acts as a wrapper around a native result provided by the
* particular JAX-RS implementation. Portable applications should not assume any particular data type or value.
*
*
* Protocol, host address, port and root path can be overridden explicitly. As the JAX-RS implementation is bound to
* that values, no querying of the actual configuration is needed in that case:
*
*
*
* SeBootstrap.Configuration.builder().protocol("HTTPS").host("0.0.0.0").port(8443).rootPath("api").build();
*
*
*
* TLS can be configured by explicitly passing a customized {@link SSLContext}:
*
*
*
* SSLContext tls = SSLContext.getInstance("TLSv1.2");
* // ...further initialize context here (see JSSE API)...
* SeBootstrap.Configuration.builder().protocol("HTTPS").sslContext(tls).build();
*
*
*
* In case of HTTPS, client authentication can be enforced to ensure that only trustworthy clients can connect:
*
*
*
* SeBootstrap.Configuration.builder().protocol("HTTPS").sslClientAuthentication(SSLClientAuthentication.MANDATORY).build();
*
*
*
* Implementations are free to support more use cases by native properties, which effectively render the application
* non-portable:
*
*
*
* SeBootstrap.Configuration.builder().property("productname.foo", "bar").build()
*
*
*
* Bulk-loading allows to attach configuration storages easily without the need to write down all properties to be
* transferred. Hence, even properties unknown to the application author will get channeled into the implementation.
* This can be done both, explicitly (hence portable) and implicitly (hence not necessarily portable as no
* particular configuration mechanics are required to be supported by compliant implementations):
*
*
*
* // Explicit use of particular configuration mechanics is portable
* SeBootstrap.Configuration.builder().from((name, type) -> externalConfigurationSystem.getValue(name, type)).build();
*
* // Implicitly relying on the support of particular configuration mechanics by
* // the actual JAX-RS implementation is not necessarily portable
* SeBootstrap.Configuration.builder().from(externalConfigurationSystem).build();
*
*
* @author Markus KARG ([email protected])
* @since 3.1
*/
public interface SeBootstrap {
/**
* Starts the provided application using the specified configuration.
*
*
* This method is intended to be used in Java SE environments only. The outcome of invocations in Jakarta EE container
* environments is undefined.
*
*
* @param application The application to start up.
* @param configuration Provides information needed for bootstrapping the application.
* @return {@code CompletionStage} (possibly asynchronously) producing handle of the running application
* {@link SeBootstrap.Instance instance}.
* @see Configuration
* @since 3.1
*/
static CompletionStage start(final Application application, final Configuration configuration) {
return RuntimeDelegate.getInstance().bootstrap(application, configuration);
}
/**
* Starts the provided application using a default configuration.
*
*
* This method is intended to be used in Java SE environments only. The outcome of invocations in Jakarta EE container
* environments is undefined.
*
*
* @param application The application to start up.
* @return {@code CompletionStage} (possibly asynchronously) producing handle of the running application
* {@link SeBootstrap.Instance instance}.
* @see Configuration
* @since 3.1
*/
static CompletionStage start(final Application application) {
Configuration configuration = Configuration.builder().build();
return start(application, configuration);
}
/**
* Starts the provided application using the specified configuration. Creates application instance
* from class using default constructor. Injection is not supported.
*
*
* This method is intended to be used in Java SE environments only. The outcome of invocations in Jakarta EE container
* environments is undefined.
*
*
* @param clazz The application class.
* @param configuration Provides information needed for bootstrapping the application.
* @return {@code CompletionStage} (possibly asynchronously) producing handle of the running application
* {@link SeBootstrap.Instance instance}.
* @see Configuration
* @since 3.1
*/
static CompletionStage start(final Class extends Application> clazz, final Configuration configuration) {
return RuntimeDelegate.getInstance().bootstrap(clazz, configuration);
}
/**
* Starts the provided application using a default configuration. Creates application instance
* from class using default constructor. Injection is not supported.
*
*
* This method is intended to be used in Java SE environments only. The outcome of invocations in Jakarta EE container
* environments is undefined.
*
*
* @param clazz The application class.
* @return {@code CompletionStage} (possibly asynchronously) producing handle of the running application
* {@link SeBootstrap.Instance instance}.
* @see Configuration
* @since 3.1
*/
static CompletionStage start(final Class extends Application> clazz) {
Configuration configuration = Configuration.builder().build();
return start(clazz, configuration);
}
/**
* Provides information needed by the JAX-RS implementation for bootstrapping an application.
*
* The configuration essentially consists of a set of parameters. While the set of actually effective keys is product
* specific, the key constants defined by the {@link SeBootstrap.Configuration} interface MUST be effective on all compliant
* products. Any unknown key MUST be silently ignored.
*
*
* @author Markus KARG ([email protected])
* @since 3.1
*/
interface Configuration {
/**
* Configuration key for the protocol an application is bound to.
*
* A compliant implementation at least MUST accept the strings {@code "HTTP"} and {@code "HTTPS"} if these protocols are
* supported.
*
*
* The default value is {@code "HTTP"}.
*
*
* @since 3.1
*/
String PROTOCOL = "jakarta.ws.rs.SeBootstrap.Protocol";
/**
* Configuration key for the hostname or IP address an application is bound to.
*
* A compliant implementation at least MUST accept string values bearing hostnames, IP4 address text representations,
* and IP6 address text representations. If a hostname string, the special IP4 address string {@code "0.0.0.0"} or
* {@code "::"} for IP6 is provided, the application MUST be bound to all IP addresses assigned to that
* hostname. If the hostname string is {@code "localhost"} the application MUST be bound to the local host's loopback
* adapter only.
*
*
* The default value is {@code "localhost"}.
*
*
* @since 3.1
*/
String HOST = "jakarta.ws.rs.SeBootstrap.Host";
/**
* Configuration key for the TCP port an application is bound to.
*
*
* A compliant implementation MUST accept {@code java.lang.Integer} values.
*
*
* There is no default port mandated by this specification, but the default value of this property is
* {@link #DEFAULT_PORT} (i. e. -1
). A compliant implementation MUST use its own default port when
* the value -1
is provided, and MAY apply (but is not obligated to) auto-selection and
* range-scanning algorithms.
*
*
* @since 3.1
*/
String PORT = "jakarta.ws.rs.SeBootstrap.Port";
/**
* Configuration key for the root path an application is bound to.
*
* The default value is {@code "/"}.
*
*
* @since 3.1
*/
String ROOT_PATH = "jakarta.ws.rs.SeBootstrap.RootPath";
/**
* Configuration key for the secure socket configuration to be used.
*
* The default value is {@link SSLContext#getDefault()}.
*
*
* @since 3.1
*/
String SSL_CONTEXT = "jakarta.ws.rs.SeBootstrap.SSLContext";
/**
* Configuration key for the secure socket client authentication policy.
*
*
* A compliant implementation MUST accept {@link SSLClientAuthentication} enums.
*
*
* The default value is {@code SSLClientAuthentication#NONE}.
*
*
* @since 3.1
*/
String SSL_CLIENT_AUTHENTICATION = "jakarta.ws.rs.SeBootstrap.SSLClientAuthentication";
/**
* Secure socket client authentication policy
*
*
* This policy is used in secure socket handshake to control whether the server requests client authentication,
* and whether successful client authentication is mandatory (i. e. connection attempt will fail for
* invalid clients).
*
*
* @author Markus KARG ([email protected])
* @since 3.1
*/
enum SSLClientAuthentication {
/**
* Server will not request client authentication.
*
* @since 3.1
*/
NONE,
/**
* Client authentication is performed, but invalid clients are accepted.
*
* @since 3.1
*/
OPTIONAL,
/**
* Client authentication is performed, and invalid clients are rejected.
*
* @since 3.1
*/
MANDATORY
}
/**
* Special value for {@link #PORT} property indicating that the implementation MUST scan for a free port.
*
* @since 3.1
*/
int FREE_PORT = 0;
/**
* Special value for {@link #PORT} property indicating that the implementation MUST use its default port.
*
* @since 3.1
*/
int DEFAULT_PORT = -1;
/**
* Returns the value of the property with the given name, or {@code null} if there is no property of that name.
*
* @param name a {@code String} specifying the name of the property.
* @return an {@code Object} containing the value of the property, or {@code null} if no property exists matching the
* given name.
* @since 3.1
*/
Object property(String name);
/**
* Returns whether the property with the given name is configured, either explicitly or by default.
*
* @param name a {@code String} specifying the name of the property.
* @return {@code false} if no property exists matching the given name, {@code true} otherwise.
* @since 3.1
*/
default boolean hasProperty(String name) {
return property(name) != null;
}
/**
* Convenience method to get the {@code protocol} to be used.
*
* Same as if calling {@link #property(String) (String) property(PROTOCOL)}.
*
*
* @return protocol to be used (e. g. {@code "HTTP")}.
* @throws ClassCastException if protocol is not a {@link String}.
* @see SeBootstrap.Configuration#PROTOCOL
* @since 3.1
*/
default String protocol() {
return (String) property(PROTOCOL);
}
/**
* Convenience method to get the {@code host} to be used.
*
* Same as if calling {@link #property(String) (String) property(HOST)}.
*
*
* @return host name or IP address to be used (e. g. {@code "localhost"} or {@code "0.0.0.0"}).
* @throws ClassCastException if host is not a {@link String}.
* @see SeBootstrap.Configuration#HOST
* @since 3.1
*/
default String host() {
return (String) property(HOST);
}
/**
* Convenience method to get the actually used {@code port}.
*
* Same as if calling {@link #property(String) (int) property(PORT)}.
*
*
* If the port was not explicitly given, this will return the port chosen implicitly by the JAX-RS
* implementation.
*
*
* @return port number actually used (e. g. {@code 8080}).
* @throws ClassCastException if port is not an {@code Integer}.
* @see SeBootstrap.Configuration#PORT
* @since 3.1
*/
default int port() {
return (int) property(PORT);
}
/**
* Convenience method to get the {@code rootPath} to be used.
*
* Same as if calling {@link #property(String) (String) property(ROOT_PATH)}.
*
*
* @return root path to be used, e.g. {@code "/"}.
* @throws ClassCastException if root path is not a {@link String}.
* @see SeBootstrap.Configuration#ROOT_PATH
* @since 3.1
*/
default String rootPath() {
return (String) property(ROOT_PATH);
}
/**
* Convenience method to get the {@code sslContext} to be used.
*
* Same as if calling {@link #property(String) (SSLContext) property(SSL_CONTEXT)}.
*
*
* @return root path to be used, e. g. {@code "/"}.
* @throws ClassCastException if sslContext is not a {@link SSLContext}.
* @see SeBootstrap.Configuration#SSL_CONTEXT
* @since 3.1
*/
default SSLContext sslContext() {
return (SSLContext) property(SSL_CONTEXT);
}
/**
* Convenience method to get the secure socket client authentication policy.
*
* Same as if calling {@link #property(String) (SSLClientAuthentication) property(SSL_CLIENT_AUTHENTICATION)}.
*
*
* @return client authentication mode, e. g. {@code NONE}.
* @throws ClassCastException if sslClientAuthentication is not a {@link SSLClientAuthentication}.
* @see SeBootstrap.Configuration#SSL_CLIENT_AUTHENTICATION
* @since 3.1
*/
default SSLClientAuthentication sslClientAuthentication() {
return (SSLClientAuthentication) property(SSL_CLIENT_AUTHENTICATION);
}
/**
* Returns a {@link UriBuilder} that includes the application root path.
*
* @return a {@link UriBuilder} for the application.
* @since 3.1
*/
default UriBuilder baseUriBuilder() {
return UriBuilder.newInstance().scheme(protocol().toLowerCase())
.host(host()).port(port()).path(rootPath());
}
/**
* Convenience method that returns a built the {@link URI} for the application.
*
* @return a built {@link URI} for the application.
* @see Configuration#baseUriBuilder()
* @since 3.1
*/
default URI baseUri() {
return baseUriBuilder().build();
}
/**
* Creates a new bootstrap configuration builder instance.
*
* @return {@link Builder} for bootstrap configuration.
* @since 3.1
*/
static Builder builder() {
return RuntimeDelegate.getInstance().createConfigurationBuilder();
}
/**
* Builder for bootstrap {@link Configuration}.
*
* @author Markus KARG ([email protected])
* @since 3.1
*/
interface Builder {
/**
* Builds a bootstrap configuration instance from the provided property values.
*
* @return {@link Configuration} built from provided property values.
* @since 3.1
*/
Configuration build();
/**
* Sets the property {@code name} to the provided {@code value}.
*
* This method does not check the validity, type or syntax of the provided value.
*
*
* @param name name of the parameter to set.
* @param value value to set, or {@code null} to use the default value.
* @return the updated builder.
* @since 3.1
*/
Builder property(String name, Object value);
/**
* Convenience method to set the {@code protocol} to be used.
*
* Same as if calling {@link #property(String, Object) property(PROTOCOL, value)}.
*
*
* @param protocol protocol parameter of this configuration, or {@code null} to use the default value.
* @return the updated builder.
* @see SeBootstrap.Configuration#PROTOCOL
* @since 3.1
*/
default Builder protocol(String protocol) {
return property(PROTOCOL, protocol);
}
/**
* Convenience method to set the {@code host} to be used.
*
* Same as if calling {@link #property(String, Object) property(HOST, value)}.
*
*
* @param host host parameter (IP address or hostname) of this configuration, or {@code null} to use the default value.
* @return the updated builder.
* @see SeBootstrap.Configuration#HOST
* @since 3.1
*/
default Builder host(String host) {
return property(HOST, host);
}
/**
* Convenience method to set the {@code port} to be used.
*
* Same as if calling {@link #property(String, Object) property(PORT, value)}.
*
*
* @param port port parameter of this configuration, or {@code null} to use the default value.
* @return the updated builder.
* @see SeBootstrap.Configuration#PORT
* @since 3.1
*/
default Builder port(Integer port) {
return property(PORT, port);
}
/**
* Convenience method to set the {@code rootPath} to be used.
*
* Same as if calling {@link #property(String, Object) property(ROOT_PATH, value)}.
*
*
* @param rootPath rootPath parameter of this configuration, or {@code null} to use the default value.
* @return the updated builder.
* @see SeBootstrap.Configuration#ROOT_PATH
* @since 3.1
*/
default Builder rootPath(String rootPath) {
return property(ROOT_PATH, rootPath);
}
/**
* Convenience method to set the {@code sslContext} to be used.
*
* Same as if calling {@link #property(String, Object) property(SSL_CONTEXT, value)}.
*
*
* @param sslContext sslContext parameter of this configuration, or {@code null} to use the default value.
* @return the updated builder.
* @see SeBootstrap.Configuration#SSL_CONTEXT
* @since 3.1
*/
default Builder sslContext(SSLContext sslContext) {
return property(SSL_CONTEXT, sslContext);
}
/**
* Convenience method to set SSL client authentication policy.
*
* Same as if calling {@link #property(String, Object) property(SSL_CLIENT_AUTHENTICATION, value)}.
*
*
* @param sslClientAuthentication SSL client authentication mode of this configuration
* @return the updated builder.
* @see SeBootstrap.Configuration#SSL_CLIENT_AUTHENTICATION
* @since 3.1
*/
default Builder sslClientAuthentication(SSLClientAuthentication sslClientAuthentication) {
return property(SSL_CLIENT_AUTHENTICATION, sslClientAuthentication);
}
/**
* Convenience method for bulk-loading configuration from a property supplier.
*
* Implementations ask the passed provider function for the actual values of all their supported properties, before
* returning from this configuration method. For each single request the implementation provides the name of the
* property and the expected data type of the value. If no such property exists (i. e. either the name is unknown or
* misspelled, or the type does not exactly match), the {@link Optional} is {@link Optional#empty() empty}.
*
*
* @param Type of the requested property value.
* @param propertiesProvider Retrieval function of externally managed properties. MUST NOT return {@code null}.
* @return the updated builder.
* @since 3.1
*/
Builder from(BiFunction, Optional> propertiesProvider);
/**
* Optional convenience method to bulk-load external configuration.
*
* Implementations are free to support any external configuration mechanics, or none at all. It is completely up to the
* implementation what set of properties is effectively loaded from the provided external configuration, possibly none
* at all.
*
*
* If the passed external configuration mechanics is unsupported, this method MUST simply do nothing.
*
*
* Portable applications should not call this method, as the outcome is completely implementation-specific.
*
*
* @param externalConfig source of externally managed properties
* @return the updated builder.
* @since 3.1
*/
default Builder from(Object externalConfig) {
return this;
}
}
}
/**
* Handle of the running application instance.
*
* @author Markus KARG ([email protected])
* @since 3.1
*/
interface Instance {
/**
* Provides access to the configuration actually used by the implementation used to create this instance.
*
* This may, or may not, be the same instance passed to {@link SeBootstrap#start(Application, Configuration)}, not even an
* equal instance, as implementations MAY create a new intance and MUST update at least the {@code PORT} property with
* the actually used value. Portable applications should not make any assumptions but always explicitly read the actual
* values from the configuration returned from this method.
*
*
* @return The configuration actually used to create this instance.
* @since 3.1
*/
Configuration configuration();
/**
* Initiate immediate shutdown of running application instance.
*
* @return {@code CompletionStage} asynchronously shutting down this application instance.
* @since 3.1
*/
CompletionStage stop();
/**
* Result of stopping the application instance.
*
* @author Markus KARG ([email protected])
* @since 3.1
*/
interface StopResult {
/**
* Provides access to the wrapped native shutdown result.
*
* Implementations may, or may not, have native shutdown results. Portable applications should not invoke this method,
* as the outcome is undefined.
*
*
* @param Requested type of the native result to return.
* @param nativeClass Requested type of the native result to return.
* @return Native result of shutting down the running application instance or {@code null} if the implementation has no
* native result.
* @throws ClassCastException if the result is not {@code null} or is not assignable to the type {@code T}.
* @since 3.1
*/
T unwrap(Class nativeClass);
}
/**
* Provides access to the wrapped native handle of the application instance.
*
* Implementations may, or may not, have native handles. Portable applications should not invoke this method, as the
* outcome is undefined.
*
*
* @param Requested type of the native handle to return.
* @param nativeClass Requested type of the native handle to return.
* @return Native handle of the running application instance or {@code null} if the implementation has no native handle.
* @throws ClassCastException if the handle is not {@code null} and is not assignable to the type {@code T}.
* @since 3.1
*/
T unwrap(Class nativeClass);
/**
* Registers a consumer for a {@link StopResult} which will be executed in a new thread
* during the JVM shutdown phase.
*
* @param consumer The consumer.
* @since 3.1
*/
default void stopOnShutdown(Consumer consumer) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> stop().thenAccept(consumer)));
}
}
}