org.glassfish.jersey.server.ApplicationHandler Maven / Gradle / Ivy
Show all versions of jaxrs-ri Show documentation
/*
* Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Payara Foundation and/or its affiliates.
*
* 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 org.glassfish.jersey.server;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.security.Principal;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.RuntimeType;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.MessageBodyReader;
import jakarta.ws.rs.ext.MessageBodyWriter;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.AutoDiscoverableConfigurator;
import org.glassfish.jersey.internal.BootstrapBag;
import org.glassfish.jersey.internal.BootstrapConfigurator;
import org.glassfish.jersey.internal.ContextResolverFactory;
import org.glassfish.jersey.internal.DynamicFeatureConfigurator;
import org.glassfish.jersey.internal.Errors;
import org.glassfish.jersey.internal.ExceptionMapperFactory;
import org.glassfish.jersey.internal.FeatureConfigurator;
import org.glassfish.jersey.internal.JaxrsProviders;
import org.glassfish.jersey.internal.Version;
import org.glassfish.jersey.internal.inject.Binder;
import org.glassfish.jersey.internal.inject.Bindings;
import org.glassfish.jersey.internal.inject.CompositeBinder;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.Injections;
import org.glassfish.jersey.internal.inject.InstanceBinding;
import org.glassfish.jersey.internal.inject.Providers;
import org.glassfish.jersey.message.MessageBodyWorkers;
import org.glassfish.jersey.message.internal.MessageBodyFactory;
import org.glassfish.jersey.message.internal.MessagingBinders;
import org.glassfish.jersey.message.internal.NullOutputStream;
import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
import org.glassfish.jersey.model.internal.RankedComparator;
import org.glassfish.jersey.model.internal.RankedProvider;
import org.glassfish.jersey.process.internal.ChainableStage;
import org.glassfish.jersey.process.internal.RequestScope;
import org.glassfish.jersey.process.internal.Stage;
import org.glassfish.jersey.process.internal.Stages;
import org.glassfish.jersey.server.internal.JerseyRequestTimeoutHandler;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.jersey.server.internal.ProcessingProviders;
import org.glassfish.jersey.internal.inject.ParamConverterConfigurator;
import org.glassfish.jersey.server.internal.inject.ParamExtractorConfigurator;
import org.glassfish.jersey.server.internal.inject.ValueParamProviderConfigurator;
import org.glassfish.jersey.server.internal.monitoring.ApplicationEventImpl;
import org.glassfish.jersey.server.internal.monitoring.CompositeApplicationEventListener;
import org.glassfish.jersey.server.internal.monitoring.MonitoringContainerListener;
import org.glassfish.jersey.server.internal.process.ReferencesInitializer;
import org.glassfish.jersey.server.internal.process.RequestProcessingConfigurator;
import org.glassfish.jersey.server.internal.process.RequestProcessingContext;
import org.glassfish.jersey.server.internal.process.RequestProcessingContextReference;
import org.glassfish.jersey.server.internal.routing.Routing;
import org.glassfish.jersey.server.model.ComponentModelValidator;
import org.glassfish.jersey.server.model.ModelProcessor;
import org.glassfish.jersey.server.model.ModelValidationException;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.internal.ModelErrors;
import org.glassfish.jersey.server.model.internal.ResourceMethodInvokerConfigurator;
import org.glassfish.jersey.server.monitoring.ApplicationEvent;
import org.glassfish.jersey.server.monitoring.ApplicationEventListener;
import org.glassfish.jersey.server.spi.Container;
import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
import org.glassfish.jersey.server.spi.ContainerResponseWriter;
/**
* Jersey server-side application handler.
*
* Container implementations use the {@code ApplicationHandler} API to process requests
* by invoking the {@link #handle(ContainerRequest) handle(request)}
* method on a configured application handler instance.
*
*
* {@code ApplicationHandler} provides two implementations of {@link jakarta.ws.rs.core.Configuration config} that can be injected
* into the application classes. The first is {@link ResourceConfig resource config} which implements {@code Configuration}
* itself and is configured by the user. The resource config is not modified by this application handler so the future reloads of
* the application is not disrupted by providers found on a classpath. This config can
* be injected only as {@code ResourceConfig} or {@code Application}. The second one can be injected into the
* {@code Configuration} parameters / fields and contains info about all the properties / provider classes / provider instances
* from the resource config and also about all the providers found during processing classes registered under
* {@link ServerProperties server properties}. After the application handler is initialized both configurations are marked as
* read-only.
*
*
* Application handler instance also acts as an aggregate {@link ContainerLifecycleListener} instance
* for the associated application. It aggregates all the registered container lifecycle listeners
* under a single, umbrella listener, represented by this application handler instance, that delegates all container lifecycle
* listener method calls to all the registered listeners. Jersey {@link Container containers} are expected to invoke
* the container lifecycle methods directly on the active {@code ApplicationHandler} instance. The application handler will then
* make sure to delegate the lifecycle listener calls further to all the container lifecycle listeners registered within the
* application. Additionally, invoking the {@link ContainerLifecycleListener#onShutdown(Container)} method on this application
* handler instance will release all the resources associated with the underlying application instance as well as close the
* application-specific {@link InjectionManager injection manager}.
*
*
* @author Pavel Bucek
* @author Jakub Podlesak
* @author Marek Potociar
* @author Libor Kramolis
* @see ResourceConfig
* @see jakarta.ws.rs.core.Configuration
* @see org.glassfish.jersey.server.spi.ContainerProvider
*/
public final class ApplicationHandler implements ContainerLifecycleListener {
private static final Logger LOGGER = Logger.getLogger(ApplicationHandler.class.getName());
/**
* Default dummy security context.
*/
private static final SecurityContext DEFAULT_SECURITY_CONTEXT = new SecurityContext() {
@Override
public boolean isUserInRole(final String role) {
return false;
}
@Override
public boolean isSecure() {
return false;
}
@Override
public Principal getUserPrincipal() {
return null;
}
@Override
public String getAuthenticationScheme() {
return null;
}
};
/**
* Configurator which initializes and register {@link ApplicationHandler} and {@link Configuration} instances into
* {@link InjectionManager} and {@link BootstrapBag}.
*
* @author Petr Bouda
*/
private class RuntimeConfigConfigurator implements BootstrapConfigurator {
@Override
public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag;
serverBag.setApplicationHandler(ApplicationHandler.this);
serverBag.setConfiguration(ResourceConfig.createRuntimeConfig(serverBag.getApplication()));
// TODO: Do we really need these three bindings in DI provider? What JAX-RS specification says?
InstanceBinding handlerBinding =
Bindings.service(ApplicationHandler.this)
.to(ApplicationHandler.class);
InstanceBinding configBinding =
Bindings.service(serverBag.getRuntimeConfig())
.to(Configuration.class)
.to(ServerConfig.class);
injectionManager.register(handlerBinding);
injectionManager.register(configBinding);
}
}
private Application application;
private ResourceConfig runtimeConfig;
private ServerRuntime runtime;
private Iterable containerLifecycleListeners;
private InjectionManager injectionManager;
private MessageBodyWorkers msgBodyWorkers;
private ManagedObjectsFinalizer managedObjectsFinalizer;
/**
* Create a new Jersey application handler using a default configuration.
*/
public ApplicationHandler() {
this(new Application());
}
/**
* Create a new Jersey server-side application handler configured by a
* {@link Application JAX-RS Application (sub-)class}.
*
* @param jaxrsApplicationClass JAX-RS {@code Application} (sub-)class that will be
* instantiated and used to configure the new Jersey
* application handler.
*/
public ApplicationHandler(final Class extends Application> jaxrsApplicationClass) {
initialize(new ApplicationConfigurator(jaxrsApplicationClass), Injections.createInjectionManager(), null);
}
/**
* Create a new Jersey server-side application handler configured by an instance
* of a {@link Application JAX-RS Application sub-class}.
*
* @param application an instance of a JAX-RS {@code Application} (sub-)class that
* will be used to configure the new Jersey application handler.
*/
public ApplicationHandler(final Application application) {
this(application, null, null);
}
/**
* Create a new Jersey server-side application handler configured by an instance
* of a {@link ResourceConfig} and a custom {@link Binder}.
*
* @param application an instance of a JAX-RS {@code Application} (sub-)class that
* will be used to configure the new Jersey application handler.
* @param customBinder additional custom bindings used to configure the application's.
*/
public ApplicationHandler(final Application application, final Binder customBinder) {
this(application, customBinder, null);
}
/**
* Create a new Jersey server-side application handler configured by an instance
* of a {@link ResourceConfig}, custom {@link Binder} and a parent used by {@link InjectionManager}.
*
* @param application an instance of a JAX-RS {@code Application} (sub-)class that
* will be used to configure the new Jersey application handler.
* @param customBinder additional custom bindings used during {@link InjectionManager} creation.
* @param parentManager parent used in {@link InjectionManager} for a specific DI provider.
*/
public ApplicationHandler(final Application application, final Binder customBinder, final Object parentManager) {
initialize(new ApplicationConfigurator(application), Injections.createInjectionManager(parentManager), customBinder);
}
private void initialize(ApplicationConfigurator applicationConfigurator, InjectionManager injectionManager,
Binder customBinder) {
LOGGER.config(LocalizationMessages.INIT_MSG(Version.getBuildId()));
this.injectionManager = injectionManager;
this.injectionManager.register(CompositeBinder.wrap(new ServerBinder(), customBinder));
this.managedObjectsFinalizer = new ManagedObjectsFinalizer(injectionManager);
ServerBootstrapBag bootstrapBag = new ServerBootstrapBag();
bootstrapBag.setManagedObjectsFinalizer(managedObjectsFinalizer);
List bootstrapConfigurators = Arrays.asList(
new RequestProcessingConfigurator(),
new RequestScope.RequestScopeConfigurator(),
new ParamConverterConfigurator(),
new ParamExtractorConfigurator(),
new ValueParamProviderConfigurator(),
new JerseyResourceContextConfigurator(),
new ComponentProviderConfigurator(),
new JaxrsProviders.ProvidersConfigurator(),
applicationConfigurator,
new RuntimeConfigConfigurator(),
new ContextResolverFactory.ContextResolversConfigurator(),
new MessageBodyFactory.MessageBodyWorkersConfigurator(),
new ExceptionMapperFactory.ExceptionMappersConfigurator(),
new ResourceMethodInvokerConfigurator(),
new ProcessingProvidersConfigurator(),
new ContainerProviderConfigurator(RuntimeType.SERVER),
new AutoDiscoverableConfigurator(RuntimeType.SERVER),
new DynamicFeatureConfigurator(),
new FeatureConfigurator(RuntimeType.SERVER));
bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));
this.runtime = Errors.processWithException(
() -> initialize(injectionManager, bootstrapConfigurators, bootstrapBag));
this.containerLifecycleListeners = Providers.getAllProviders(injectionManager, ContainerLifecycleListener.class);
}
/**
* Assumes the configuration field is initialized with a valid ResourceConfig.
*/
private ServerRuntime initialize(InjectionManager injectionManager, List bootstrapConfigurators,
ServerBootstrapBag bootstrapBag) {
this.application = bootstrapBag.getApplication();
this.runtimeConfig = bootstrapBag.getRuntimeConfig();
// Register the binders which are dependent on "Application.properties()"
injectionManager.register(new MessagingBinders.MessageBodyProviders(application.getProperties(), RuntimeType.SERVER));
// Lock original ResourceConfig.
if (application instanceof ResourceConfig) {
((ResourceConfig) application).lock();
}
CompositeApplicationEventListener compositeListener = null;
Errors.mark(); // mark begin of validation phase
try {
// TODO: Create as a configurator? / The same code in ClientConfig.
// AutoDiscoverable.
if (!CommonProperties.getValue(runtimeConfig.getProperties(), RuntimeType.SERVER,
CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, Boolean.FALSE, Boolean.class)) {
runtimeConfig.configureAutoDiscoverableProviders(injectionManager, bootstrapBag.getAutoDiscoverables());
} else {
runtimeConfig.configureForcedAutoDiscoverableProviders(injectionManager);
}
// Configure binders and features.
runtimeConfig.configureMetaProviders(injectionManager, bootstrapBag.getManagedObjectsFinalizer());
ResourceBagConfigurator resourceBagConfigurator = new ResourceBagConfigurator();
resourceBagConfigurator.init(injectionManager, bootstrapBag);
runtimeConfig.lock();
ExternalRequestScopeConfigurator externalRequestScopeConfigurator = new ExternalRequestScopeConfigurator();
externalRequestScopeConfigurator.init(injectionManager, bootstrapBag);
ModelProcessorConfigurator modelProcessorConfigurator = new ModelProcessorConfigurator();
modelProcessorConfigurator.init(injectionManager, bootstrapBag);
ResourceModelConfigurator resourceModelConfigurator = new ResourceModelConfigurator();
resourceModelConfigurator.init(injectionManager, bootstrapBag);
ServerExecutorProvidersConfigurator executorProvidersConfigurator = new ServerExecutorProvidersConfigurator();
executorProvidersConfigurator.init(injectionManager, bootstrapBag);
injectionManager.completeRegistration();
bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, bootstrapBag));
resourceModelConfigurator.postInit(injectionManager, bootstrapBag);
Iterable appEventListeners =
Providers.getAllProviders(injectionManager, ApplicationEventListener.class, new RankedComparator<>());
if (appEventListeners.iterator().hasNext()) {
ResourceBag resourceBag = bootstrapBag.getResourceBag();
compositeListener = new CompositeApplicationEventListener(appEventListeners);
compositeListener.onEvent(new ApplicationEventImpl(ApplicationEvent.Type.INITIALIZATION_START,
this.runtimeConfig, runtimeConfig.getComponentBag().getRegistrations(),
resourceBag.classes, resourceBag.instances, null));
}
if (!disableValidation()) {
ComponentModelValidator validator = new ComponentModelValidator(
bootstrapBag.getValueParamProviders(), bootstrapBag.getMessageBodyWorkers());
validator.validate(bootstrapBag.getResourceModel());
}
if (Errors.fatalIssuesFound() && !ignoreValidationError()) {
throw new ModelValidationException(LocalizationMessages.RESOURCE_MODEL_VALIDATION_FAILED_AT_INIT(),
ModelErrors.getErrorsAsResourceModelIssues(true));
}
} finally {
if (ignoreValidationError()) {
Errors.logErrors(true);
Errors.reset(); // reset errors to the state before validation phase
} else {
Errors.unmark();
}
}
this.msgBodyWorkers = bootstrapBag.getMessageBodyWorkers();
// assembly request processing chain
ProcessingProviders processingProviders = bootstrapBag.getProcessingProviders();
final ContainerFilteringStage preMatchRequestFilteringStage = new ContainerFilteringStage(
processingProviders.getPreMatchFilters(),
processingProviders.getGlobalResponseFilters());
final ChainableStage routingStage =
Routing.forModel(bootstrapBag.getResourceModel().getRuntimeResourceModel())
.resourceContext(bootstrapBag.getResourceContext())
.configuration(runtimeConfig)
.entityProviders(msgBodyWorkers)
.valueSupplierProviders(bootstrapBag.getValueParamProviders())
.modelProcessors(Providers.getAllRankedSortedProviders(injectionManager, ModelProcessor.class))
.createService(serviceType -> Injections.getOrCreate(injectionManager, serviceType))
.processingProviders(processingProviders)
.resourceMethodInvokerBuilder(bootstrapBag.getResourceMethodInvokerBuilder())
.buildStage();
/*
* Root linear request acceptor. This is the main entry point for the whole request processing.
*/
final ContainerFilteringStage resourceFilteringStage =
new ContainerFilteringStage(processingProviders.getGlobalRequestFilters(), null);
final ReferencesInitializer referencesInitializer = new ReferencesInitializer(injectionManager,
() -> injectionManager.getInstance(RequestProcessingContextReference.class));
final Stage rootStage = Stages
.chain(referencesInitializer)
.to(preMatchRequestFilteringStage)
.to(routingStage)
.to(resourceFilteringStage)
.build(Routing.matchedEndpointExtractor());
ServerRuntime serverRuntime = ServerRuntime.createServerRuntime(
injectionManager, bootstrapBag, rootStage, compositeListener, processingProviders);
// Inject instances.
ComponentBag componentBag = runtimeConfig.getComponentBag();
ResourceBag resourceBag = bootstrapBag.getResourceBag();
for (final Object instance : componentBag.getInstances(ComponentBag.excludeMetaProviders(injectionManager))) {
injectionManager.inject(instance);
}
for (final Object instance : resourceBag.instances) {
injectionManager.inject(instance);
}
logApplicationInitConfiguration(injectionManager, resourceBag, processingProviders);
if (compositeListener != null) {
ApplicationEvent initFinishedEvent = new ApplicationEventImpl(
ApplicationEvent.Type.INITIALIZATION_APP_FINISHED, runtimeConfig,
componentBag.getRegistrations(), resourceBag.classes, resourceBag.instances,
bootstrapBag.getResourceModel());
compositeListener.onEvent(initFinishedEvent);
MonitoringContainerListener containerListener = injectionManager.getInstance(MonitoringContainerListener.class);
containerListener.init(compositeListener, initFinishedEvent);
}
return serverRuntime;
}
private boolean ignoreValidationError() {
return ServerProperties.getValue(runtimeConfig.getProperties(),
ServerProperties.RESOURCE_VALIDATION_IGNORE_ERRORS,
Boolean.FALSE,
Boolean.class);
}
private boolean disableValidation() {
return ServerProperties.getValue(runtimeConfig.getProperties(),
ServerProperties.RESOURCE_VALIDATION_DISABLE,
Boolean.FALSE,
Boolean.class);
}
private static void logApplicationInitConfiguration(final InjectionManager injectionManager,
final ResourceBag resourceBag,
final ProcessingProviders processingProviders) {
if (!LOGGER.isLoggable(Level.CONFIG)) {
return;
}
final StringBuilder sb = new StringBuilder(LocalizationMessages.LOGGING_APPLICATION_INITIALIZED()).append('\n');
final List rootResourceClasses = resourceBag.getRootResources();
if (!rootResourceClasses.isEmpty()) {
sb.append(LocalizationMessages.LOGGING_ROOT_RESOURCE_CLASSES()).append(":");
for (final Resource r : rootResourceClasses) {
for (final Class clazz : r.getHandlerClasses()) {
sb.append('\n').append(" ").append(clazz.getName());
}
}
}
sb.append('\n');
final Set messageBodyReaders;
final Set messageBodyWriters;
if (LOGGER.isLoggable(Level.FINE)) {
Spliterator mbrSpliterator =
Providers.getAllProviders(injectionManager, MessageBodyReader.class).spliterator();
messageBodyReaders = StreamSupport.stream(mbrSpliterator, false).collect(Collectors.toSet());
Spliterator mbwSpliterator =
Providers.getAllProviders(injectionManager, MessageBodyWriter.class).spliterator();
messageBodyWriters = StreamSupport.stream(mbwSpliterator, false).collect(Collectors.toSet());
} else {
messageBodyReaders = Providers.getCustomProviders(injectionManager, MessageBodyReader.class);
messageBodyWriters = Providers.getCustomProviders(injectionManager, MessageBodyWriter.class);
}
printProviders(LocalizationMessages.LOGGING_PRE_MATCH_FILTERS(),
processingProviders.getPreMatchFilters(), sb);
printProviders(LocalizationMessages.LOGGING_GLOBAL_REQUEST_FILTERS(),
processingProviders.getGlobalRequestFilters(), sb);
printProviders(LocalizationMessages.LOGGING_GLOBAL_RESPONSE_FILTERS(),
processingProviders.getGlobalResponseFilters(), sb);
printProviders(LocalizationMessages.LOGGING_GLOBAL_READER_INTERCEPTORS(),
processingProviders.getGlobalReaderInterceptors(), sb);
printProviders(LocalizationMessages.LOGGING_GLOBAL_WRITER_INTERCEPTORS(),
processingProviders.getGlobalWriterInterceptors(), sb);
printNameBoundProviders(LocalizationMessages.LOGGING_NAME_BOUND_REQUEST_FILTERS(),
processingProviders.getNameBoundRequestFilters(), sb);
printNameBoundProviders(LocalizationMessages.LOGGING_NAME_BOUND_RESPONSE_FILTERS(),
processingProviders.getNameBoundResponseFilters(), sb);
printNameBoundProviders(LocalizationMessages.LOGGING_NAME_BOUND_READER_INTERCEPTORS(),
processingProviders.getNameBoundReaderInterceptors(), sb);
printNameBoundProviders(LocalizationMessages.LOGGING_NAME_BOUND_WRITER_INTERCEPTORS(),
processingProviders.getNameBoundWriterInterceptors(), sb);
printProviders(LocalizationMessages.LOGGING_DYNAMIC_FEATURES(),
processingProviders.getDynamicFeatures(), sb);
printProviders(LocalizationMessages.LOGGING_MESSAGE_BODY_READERS(),
messageBodyReaders.stream().map(new WorkersToStringTransform<>()).collect(Collectors.toList()), sb);
printProviders(LocalizationMessages.LOGGING_MESSAGE_BODY_WRITERS(),
messageBodyWriters.stream().map(new WorkersToStringTransform<>()).collect(Collectors.toList()), sb);
LOGGER.log(Level.CONFIG, sb.toString());
}
private static class WorkersToStringTransform implements Function {
@Override
public String apply(final T t) {
if (t != null) {
return t.getClass().getName();
}
return null;
}
}
private static void printNameBoundProviders(final String title,
final Map, List>> providers,
final StringBuilder sb) {
if (!providers.isEmpty()) {
sb.append(title).append(":").append('\n');
for (final Map.Entry, List>> entry : providers.entrySet()) {
for (final RankedProvider rankedProvider : entry.getValue()) {
sb.append(" ")
.append(LocalizationMessages.LOGGING_PROVIDER_BOUND(rankedProvider, entry.getKey()))
.append('\n');
}
}
}
}
private static void printProviders(final String title, final Iterable providers, final StringBuilder sb) {
final Iterator iterator = providers.iterator();
boolean first = true;
while (iterator.hasNext()) {
if (first) {
sb.append(title).append(":").append('\n');
first = false;
}
final T provider = iterator.next();
sb.append(" ").append(provider).append('\n');
}
}
/**
* Invokes a request and returns the {@link Future response future}.
*
* @param requestContext request data.
* @return response future.
*/
public Future apply(final ContainerRequest requestContext) {
return apply(requestContext, new NullOutputStream());
}
/**
* Invokes a request and returns the {@link Future response future}.
*
* @param request request data.
* @param outputStream response output stream.
* @return response future.
*/
public Future apply(final ContainerRequest request,
final OutputStream outputStream) {
final FutureResponseWriter responseFuture =
new FutureResponseWriter(request.getMethod(), outputStream, runtime.getBackgroundScheduler());
if (request.getSecurityContext() == null) {
request.setSecurityContext(DEFAULT_SECURITY_CONTEXT);
}
request.setWriter(responseFuture);
handle(request);
return responseFuture;
}
private static class FutureResponseWriter extends CompletableFuture implements ContainerResponseWriter {
private ContainerResponse response = null;
private final String requestMethodName;
private final OutputStream outputStream;
private final JerseyRequestTimeoutHandler requestTimeoutHandler;
private FutureResponseWriter(final String requestMethodName,
final OutputStream outputStream,
final ScheduledExecutorService backgroundScheduler) {
this.requestMethodName = requestMethodName;
this.outputStream = outputStream;
this.requestTimeoutHandler = new JerseyRequestTimeoutHandler(this, backgroundScheduler);
}
@Override
public OutputStream writeResponseStatusAndHeaders(final long contentLength, final ContainerResponse response) {
this.response = response;
if (contentLength >= 0) {
response.getHeaders().putSingle(HttpHeaders.CONTENT_LENGTH, Long.toString(contentLength));
}
return outputStream;
}
@Override
public boolean suspend(final long time, final TimeUnit unit, final TimeoutHandler handler) {
return requestTimeoutHandler.suspend(time, unit, handler);
}
@Override
public void setSuspendTimeout(final long time, final TimeUnit unit) {
requestTimeoutHandler.setSuspendTimeout(time, unit);
}
@Override
public void commit() {
final ContainerResponse current = response;
if (current != null) {
if (HttpMethod.HEAD.equals(requestMethodName) && current.hasEntity()) {
// for testing purposes:
// need to also strip the object entity as it was stripped when writing to output
current.setEntity(null);
}
requestTimeoutHandler.close();
super.complete(current);
}
}
@Override
public void failure(final Throwable error) {
requestTimeoutHandler.close();
super.completeExceptionally(error);
}
@Override
public boolean enableResponseBuffering() {
return true;
}
}
/**
* The main request/response processing entry point for Jersey container implementations.
*
* The method invokes the request processing of the provided
* {@link ContainerRequest container request context} and uses the
* {@link ContainerResponseWriter container response writer} to suspend & resume the processing
* as well as write the response back to the container.
*
*
* The the {@link SecurityContext security context} stored in the container request context
* is bound as an injectable instance in the scope of the processed request context.
* Also, any {@link org.glassfish.jersey.server.spi.RequestScopedInitializer custom scope injections}
* are initialized in the current request scope.
*
*
* @param request container request context of the current request.
*/
public void handle(final ContainerRequest request) {
request.setWorkers(msgBodyWorkers);
runtime.process(request);
}
/**
* Returns {@link InjectionManager} relevant to current application.
*
* @return {@link InjectionManager} instance.
* @since 2.26
*/
public InjectionManager getInjectionManager() {
return injectionManager;
}
/**
* Get the application configuration.
*
* @return application configuration.
*/
public ResourceConfig getConfiguration() {
return runtimeConfig;
}
// Aggregate container lifecycle listener implementation
@Override
public void onStartup(final Container container) {
for (final ContainerLifecycleListener listener : containerLifecycleListeners) {
listener.onStartup(container);
}
}
@Override
public void onReload(final Container container) {
for (final ContainerLifecycleListener listener : containerLifecycleListeners) {
listener.onReload(container);
}
}
@Override
public void onShutdown(final Container container) {
try {
for (final ContainerLifecycleListener listener : containerLifecycleListeners) {
listener.onShutdown(container);
}
} finally {
try {
// Call @PreDestroy method on Application.
injectionManager.preDestroy(ResourceConfig.unwrapApplication(application));
} finally {
// Shutdown ServiceLocator.
// Takes care of the injected executors & schedulers shut-down too.
managedObjectsFinalizer.preDestroy();
injectionManager.shutdown();
}
}
}
}