blob: df26b22587727251482a5385e29311ee9e96b99f [file] [log] [blame]
/*
* Copyright (c) 2017, 2021 Oracle and/or its affiliates. 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 org.glassfish.jersey.client;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.jersey.internal.BootstrapBag;
import org.glassfish.jersey.internal.inject.Bindings;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.InstanceBinding;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
import org.glassfish.jersey.process.internal.AbstractExecutorProvidersConfigurator;
import org.glassfish.jersey.spi.ExecutorServiceProvider;
import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider;
/**
* Configurator which initializes and register {@link ExecutorServiceProvider} and
* {@link ScheduledExecutorServiceProvider}.
*
* @author Petr Bouda
*/
class ClientExecutorProvidersConfigurator extends AbstractExecutorProvidersConfigurator {
private static final Logger LOGGER = Logger.getLogger(ClientExecutorProvidersConfigurator.class.getName());
private static final ExecutorService MANAGED_EXECUTOR_SERVICE = lookupManagedExecutorService();
private final ComponentBag componentBag;
private final JerseyClient client;
private final ExecutorService customExecutorService;
private final ScheduledExecutorService customScheduledExecutorService;
ClientExecutorProvidersConfigurator(ComponentBag componentBag, JerseyClient client,
ExecutorService customExecutorService,
ScheduledExecutorService customScheduledExecutorService) {
this.componentBag = componentBag;
this.client = client;
this.customExecutorService = customExecutorService;
this.customScheduledExecutorService = customScheduledExecutorService;
}
@Override
public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
Map<String, Object> runtimeProperties = bootstrapBag.getConfiguration().getProperties();
ExecutorServiceProvider defaultAsyncExecutorProvider;
ScheduledExecutorServiceProvider defaultScheduledExecutorProvider;
final ExecutorService clientExecutorService = client.getExecutorService() == null
// custom executor service can be also set via managed client config class, in that case, it ends up in the
// customExecutorService field (similar for scheduled version)
? customExecutorService
: client.getExecutorService();
// if there is a users provided executor service, use it
if (clientExecutorService != null) {
defaultAsyncExecutorProvider = new ClientExecutorServiceProvider(clientExecutorService);
// otherwise, check for ClientProperties.ASYNC_THREADPOOL_SIZE - if that is set, Jersey will create the
// ExecutorService to be used. If not and running on Java EE container, ManagedExecutorService will be used.
// Final fallback is DefaultClientAsyncExecutorProvider with defined default.
} else {
// Default async request executors support
Integer asyncThreadPoolSize = ClientProperties
.getValue(runtimeProperties, ClientProperties.ASYNC_THREADPOOL_SIZE, Integer.class);
if (asyncThreadPoolSize != null) {
// TODO: Do we need to register DEFAULT Executor and ScheduledExecutor to InjectionManager?
asyncThreadPoolSize = (asyncThreadPoolSize < 0) ? 0 : asyncThreadPoolSize;
InstanceBinding<Integer> asyncThreadPoolSizeBinding = Bindings
.service(asyncThreadPoolSize)
.named("ClientAsyncThreadPoolSize");
injectionManager.register(asyncThreadPoolSizeBinding);
defaultAsyncExecutorProvider = new DefaultClientAsyncExecutorProvider(asyncThreadPoolSize);
} else {
if (MANAGED_EXECUTOR_SERVICE != null) {
defaultAsyncExecutorProvider = new ClientExecutorServiceProvider(MANAGED_EXECUTOR_SERVICE);
} else {
defaultAsyncExecutorProvider = new DefaultClientAsyncExecutorProvider(0);
}
}
}
InstanceBinding<ExecutorServiceProvider> executorBinding = Bindings
.service(defaultAsyncExecutorProvider)
.to(ExecutorServiceProvider.class);
injectionManager.register(executorBinding);
final ScheduledExecutorService clientScheduledExecutorService = client.getScheduledExecutorService() == null
// scheduled executor service set from {@link ClientConfig}.
? customScheduledExecutorService
: client.getScheduledExecutorService();
if (clientScheduledExecutorService != null) {
defaultScheduledExecutorProvider =
new ClientScheduledExecutorServiceProvider(Values.of(clientScheduledExecutorService));
} else {
ScheduledExecutorService scheduledExecutorService = lookupManagedScheduledExecutorService();
defaultScheduledExecutorProvider =
scheduledExecutorService == null
// default client background scheduler disposes the executor service when client is closed.
// we don't need to do that for user provided (via ClientBuilder) or managed executor service.
? new DefaultClientBackgroundSchedulerProvider()
: new ClientScheduledExecutorServiceProvider(Values.of(scheduledExecutorService));
}
InstanceBinding<ScheduledExecutorServiceProvider> schedulerBinding = Bindings
.service(defaultScheduledExecutorProvider)
.to(ScheduledExecutorServiceProvider.class);
injectionManager.register(schedulerBinding);
registerExecutors(injectionManager, componentBag, defaultAsyncExecutorProvider,
defaultScheduledExecutorProvider, bootstrapBag.getManagedObjectsFinalizer());
}
private static ExecutorService lookupManagedExecutorService() {
// Get the default ManagedExecutorService, if available
try {
// Android and some other environments don't have InitialContext class available.
final Class<?> aClass =
AccessController.doPrivileged(ReflectionHelper.classForNamePA("javax.naming.InitialContext"));
final Object initialContext = aClass.newInstance();
final Method lookupMethod = aClass.getMethod("lookup", String.class);
return (ExecutorService) lookupMethod.invoke(initialContext, "java:comp/DefaultManagedExecutorService");
} catch (Exception e) {
// ignore
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, e.getMessage(), e);
}
} catch (LinkageError error) {
// ignore - JDK8 compact2 profile - http://openjdk.java.net/jeps/161
}
return null;
}
private ScheduledExecutorService lookupManagedScheduledExecutorService() {
try {
// Android and some other environments don't have InitialContext class available.
final Class<?> aClass =
AccessController.doPrivileged(ReflectionHelper.classForNamePA("javax.naming.InitialContext"));
final Object initialContext = aClass.newInstance();
final Method lookupMethod = aClass.getMethod("lookup", String.class);
return (ScheduledExecutorService) lookupMethod
.invoke(initialContext, "java:comp/DefaultManagedScheduledExecutorService");
} catch (Exception e) {
// ignore
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, e.getMessage(), e);
}
} catch (LinkageError error) {
// ignore - JDK8 compact2 profile - http://openjdk.java.net/jeps/161
}
return null;
}
@ClientAsyncExecutor
public static class ClientExecutorServiceProvider implements ExecutorServiceProvider {
private final ExecutorService executorService;
ClientExecutorServiceProvider(ExecutorService executorService) {
this.executorService = executorService;
}
@Override
public ExecutorService getExecutorService() {
return executorService;
}
@Override
public void dispose(ExecutorService executorService) {
}
}
@ClientBackgroundScheduler
public static class ClientScheduledExecutorServiceProvider implements ScheduledExecutorServiceProvider {
private final Value<ScheduledExecutorService> executorService;
ClientScheduledExecutorServiceProvider(Value<ScheduledExecutorService> executorService) {
this.executorService = executorService;
}
@Override
public ScheduledExecutorService getExecutorService() {
return executorService.get();
}
@Override
public void dispose(ExecutorService executorService) {
}
}
}