ConnectorConfiguration.java
/*
* Copyright (c) 2025 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.innate;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.ClientRequest;
import org.glassfish.jersey.client.RequestEntityProcessing;
import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
import org.glassfish.jersey.internal.PropertiesResolver;
import javax.net.ssl.SSLContext;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.client.Client;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Feature;
import java.net.Proxy;
import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Configuration object to use for configuring the client connectors and HTTP request processing.
* This configuration provides settings to be handled by the connectors, mainly declared by {@link ClientProperties}.
*
* @param <E> the connector configuration subtype.
*/
public class ConnectorConfiguration<E extends ConnectorConfiguration<E>> {
protected final NullableRef<Integer> connectTimeout = NullableRef.empty();
protected final NullableRef<Boolean> expect100Continue = NullableRef.empty();
protected final NullableRef<Long> expect100continueThreshold = NullableRef.empty();
protected final NullableRef<Boolean> followRedirects = NullableRef.empty();
protected final NullableRef<String> prefix = NullableRef.empty();
protected final NullableRef<Object> proxyUri = NullableRef.empty();
protected final NullableRef<String> proxyUserName = NullableRef.empty();
protected final NullableRef<String> proxyPassword = NullableRef.empty();
protected final NullableRef<Integer> readTimeout = NullableRef.empty();
protected final NullableRef<RequestEntityProcessing> requestEntityProcessing = NullableRef.empty();
protected final NullableRef<String> sniHostname = NullableRef.empty();
protected final NullableRef<Supplier<SSLContext>> sslContextSupplier = NullableRef.empty();
protected final NullableRef<Integer> threadPoolSize = NullableRef.empty();
/**
* Use factory methods provided by each connector supporting this configuration object and its subclass instead.
*/
protected ConnectorConfiguration() {
}
/**
* Set the asynchronous thread-pool size. The property {@link ClientProperties#ASYNC_THREADPOOL_SIZE}
* has precedence over this setting.
*
* @param threadPoolSize the size of the asynchronous thread-pool.
* @return updated configuration.
*/
public E asyncThreadPoolSize(int threadPoolSize) {
this.threadPoolSize.set(threadPoolSize);
return self();
}
/**
* Set connect timeout. The property {@link ClientProperties#CONNECT_TIMEOUT}
* has precedence over this setting.
*
* @param millis timeout in milliseconds.
* @return updated configuration.
*/
public E connectTimeout(int millis) {
connectTimeout.set(millis);
return self();
}
/**
* Allows for HTTP Expect:100-Continue.
* The property {@link ClientProperties#EXPECT_100_CONTINUE} has precedence over this setting.
*
* @param enable allows for HTTP Expect:100-Continue or not.
* @return updated configuration.
*/
public E expect100Continue(boolean enable) {
expect100Continue.set(enable);
return self();
}
/**
* Set the Expect:100-Continue content-length threshold size.
* The {@link ClientProperties#EXPECT_100_CONTINUE_THRESHOLD_SIZE} property has precedence over this setting.
*
* @param size the content-length threshold.
* @return updated configuration.
*/
public E expect100ContinueThreshold(long size) {
expect100continueThreshold.set(size);
return self();
}
/**
* Set to follow redirects. The property {@link ClientProperties#FOLLOW_REDIRECTS} has precedence over this setting.
*
* @param follow to follow or not to follow.
* @return updated configuration.
*/
public E followRedirects(boolean follow) {
followRedirects.set(follow);
return self();
}
/**
* <p>
* Set the prefix for the configuration properties used by Client/Request to configure and override the settings.
* For instance, if the prefix would be {@code com.example.MyProject.}, the property {@link #connectTimeout(int)}
* is overridden only by properties with key starting by the prefix,
* i.e. for {@link ClientProperties#CONNECT_TIMEOUT},
* the property key {@code com.example.MyProject.jersey.config.client.connectTimeout} would override the setting.
* </p>
* <p>
* The prefix can be used to override the settings by the System property set specifically for the
* prefixed connector. See {@link org.glassfish.jersey.CommonProperties#ALLOW_SYSTEM_PROPERTIES_PROVIDER}
* for enabling System properties usage.
* </p>
* <p>
* The default configuration prefix is empty.
* </p>
*
* @param prefix the non-null prefix.
* @throws NullPointerException if the prefix is null.
* @return updated configuration.
*/
public E prefix(String prefix) {
this.prefix.set(Objects.requireNonNull(prefix));
return self();
}
/**
* Set proxy password. The property {@link ClientProperties#PROXY_PASSWORD}
* has precedence over this setting.
*
* @param proxyPassword the proxy password.
* @return updated configuration.
*/
public E proxyPassword(String proxyPassword) {
this.proxyPassword.set(proxyPassword);
return self();
}
/**
* Set proxy username. The property {@link ClientProperties#PROXY_USERNAME}
* has precedence over this setting.
*
* @param userName the proxy username.
* @return updated configuration.
*/
public E proxyUserName(String userName) {
proxyUserName.set(userName);
return self();
}
/**
* Set proxy URI. The property {@link ClientProperties#PROXY_URI}
* has precedence over this setting.
*
* @param proxyUri the proxy URI.
* @return updated configuration.
*/
public E proxyUri(String proxyUri) {
this.proxyUri.set(proxyUri);
return self();
}
/**
* Set proxy URI. The property {@link ClientProperties#PROXY_URI}
* has precedence over this setting.
*
* @param proxyUri the proxy URI.
* @return updated configuration.
*/
public E proxyUri(URI proxyUri) {
this.proxyUri.set(proxyUri);
return self();
}
/**
* Set HTTP proxy. The property {@link ClientProperties#PROXY_URI}
* has precedence over this setting.
*
* @param proxy the HTTP proxy.
* @return updated configuration.
*/
public E proxy(Proxy proxy) {
this.proxyUri.set(proxy);
return self();
}
/**
* Set read timeout. The property {@link ClientProperties#READ_TIMEOUT}
* has precedence over this setting.
*
* @param millis timeout in milliseconds.
* @return updated configuration.
*/
public E readTimeout(int millis) {
readTimeout.set(millis);
return self();
}
/**
* Set the request entity processing type.
*
* @param requestEntityProcessing the request entity processing type.
* @return the updated configuration.
*/
public E requestEntityProcessing(RequestEntityProcessing requestEntityProcessing) {
this.requestEntityProcessing.set(requestEntityProcessing);
return self();
}
public E sniHostName(String sniHostname) {
this.sniHostname.set(sniHostname);
return self();
}
/**
* Set the {@link SSLContext} supplier. The property {@link ClientProperties#SSL_CONTEXT_SUPPLIER} has precedence over
* this setting.
*
* @param sslContextSupplier the {@link SSLContext} supplier.
* @return the updated configuration.
*/
public E sslContextSupplier(Supplier<SSLContext> sslContextSupplier) {
this.sslContextSupplier.set(sslContextSupplier);
return self();
}
/**
* Return type-cast self.
* @return self.
*/
@SuppressWarnings("unchecked")
protected E self() {
return (E) this;
}
/**
* <p>
* A reference to a value. The reference can be empty, but unlike the {@code Optional}, once a value is set,
* it never can be empty again. The {@code null} value is treated as a non-empty value of null.
* </p><p>
* This {@code null}
* can be used to override some previous configuration value, to distinguish the intentional {@code null} override
* from an empty (non-set) configuration value.
* </p>
* @param <T> type of the value.
*/
protected static class NullableRef<T> implements org.glassfish.jersey.internal.util.collection.Ref<T> {
private NullableRef() {
// use factory methods;
}
/**
* Return a new empty reference.
*
* @return an empty reference.
* @param <T> The type of the empty value.
*/
public static <T> NullableRef<T> empty() {
return new NullableRef<>();
}
/**
* Return a reference of a given value.
*
* @param value the value this reference refers to.*
* @return a new reference to a given value.
* @param <T> type of the value.
*/
public static <T> NullableRef<T> of(T value) {
NullableRef<T> ref = new NullableRef<>();
ref.set(value);
return ref;
}
private boolean empty = true;
private T ref = null;
@Override
public void set(T value) {
empty = false;
ref = value;
}
/**
* Set or replace the value if other value is set.
* @param other a reference to another value.
*/
public void setNonEmpty(NullableRef<T> other) {
other.ifPresent(this::set);
}
@Override
public T get() {
return ref;
}
/**
* Run action if and only if the condition applies.
*
* @param predicate the condition to be met.
* @param action the action to run if condition is met.
*/
public void iff(Predicate<T> predicate, Runnable action) {
if (predicate.test(ref)) {
action.run();
}
}
/**
* If it is empty, sets the {@code value} value. Keeps the original value, otherwise.
*
* @param value the value to be set if empty.
*/
public void ifEmptySet(T value) {
if (empty) {
set(value);
}
}
/**
* If a value is present, performs the given action with the value,
* otherwise does nothing.
*
* @param action the action to be performed, if a value is present
* @throws NullPointerException if value is present and the given action is
* {@code null}
*/
public void ifPresent(Consumer<? super T> action) {
if (!empty) {
action.accept(ref);
}
}
/**
* If a value is present, performs the given action with the value,
* otherwise performs the given empty-based action.
*
* @param action the action to be performed, if a value is present
* @param emptyAction the empty-based action to be performed, if no value is
* present
* @throws NullPointerException if a value is present and the given action
* is {@code null}, or no value is present and the given empty-based
* action is {@code null}.
*/
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (!empty) {
action.accept(ref);
} else {
emptyAction.run();
}
}
/**
* Return the value if present, the {@code other} otherwise.
*
* @param other the value if not present
* @return inner value if present or the other otherwise.
*/
public T ifPresentOrElse(T other) {
return empty ? other : ref;
}
/**
* If a value is not present, returns {@code true}, otherwise
* {@code false}.
*
* @return {@code true} if a value is not present, otherwise {@code false}
*/
public boolean isEmpty() {
return empty;
}
/**
* If a value is present, returns {@code true}, otherwise {@code false}.
*
* @return {@code true} if a value is present, otherwise {@code false}
*/
public boolean isPresent() {
return !empty;
}
@Override
public int hashCode() {
return Objects.hash(ref, empty);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof NullableRef)) {
return false;
}
NullableRef<?> that = (NullableRef<?>) o;
return Objects.equals(empty, that.empty) && Objects.equals(ref, that.ref);
}
@Override
public String toString() {
return empty ? "<empty>" : ref == null ? "<null>" : ref.toString();
}
}
protected interface Read<CC extends ConnectorConfiguration<CC> & Read<CC>>
extends SSLParamConfigurator.SSLParamConfiguratorConfiguration {
/**
* Set and replace the values of current configuration by values of other configuration
* if and only if the values of other configuration are set.
*
* @param other another configuration instance.
*/
public default <X extends ConnectorConfiguration<?>> void setNonEmpty(X other) {
me().connectTimeout.setNonEmpty(other.connectTimeout);
me().expect100Continue.setNonEmpty(other.expect100Continue);
me().expect100continueThreshold.setNonEmpty(other.expect100continueThreshold);
me().followRedirects.setNonEmpty(other.followRedirects);
me().prefix.setNonEmpty(other.prefix);
me().proxyUri.setNonEmpty(other.proxyUri);
me().proxyUserName.setNonEmpty(other.proxyUserName);
me().proxyPassword.setNonEmpty(other.proxyPassword);
me().readTimeout.setNonEmpty(other.readTimeout);
me().requestEntityProcessing.setNonEmpty(other.requestEntityProcessing);
me().sniHostname.setNonEmpty(other.sniHostname);
me().sslContextSupplier.setNonEmpty(other.sslContextSupplier);
me().threadPoolSize.setNonEmpty(other.threadPoolSize);
}
/**
* Return the thread-pool size setting.
*
* @return the thread pool size setting.
*/
public default Integer asyncThreadPoolSize() {
return me().threadPoolSize.get();
}
/**
* Update connect timeout value based on request properties settings.
*
* @param request the current HTTP client request.
* @return the updated configuration.
*/
public default int connectTimeout(ClientRequest request) {
me().connectTimeout.set(
request.resolveProperty(prefixed(ClientProperties.CONNECT_TIMEOUT), me().connectTimeout.get())
);
return me().connectTimeout.get();
}
/**
* Get the value of connect timeout setting.
*
* @return connect timeout value.
*/
public default int connectTimeout() {
return me().connectTimeout.get();
}
/**
* Sets the default value. The default methods cannot be set on instances passed by the customers using
* {@link ClientProperties#CONNECTOR_CONFIGURATION} since they would override the values previously set
* by the connector configuration object.
* @return the initialized configuration object.
*/
public default CC init() {
me().connectTimeout(0)
.expect100ContinueThreshold(ClientProperties.DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE)
.followRedirects(Boolean.TRUE)
.prefix("")
.readTimeout(0);
return me();
}
/**
* Utility method to create a new instance of configuration to preserve the settings of previous configuration.
*
* @return a new instance of the configuration.
*/
public default CC copy() {
CC config = instance();
config.init();
config.setNonEmpty(me());
return config;
}
public default CC copyFromClient(Configuration configuration) {
CC clientConfiguration = copy();
final Map<String, Object> properties = configuration.getProperties();
Object configProp = properties.get(clientConfiguration.prefixed(ClientProperties.CONNECTOR_CONFIGURATION));
if (configProp != null) {
ConnectorConfiguration<?> clientCfg = (ConnectorConfiguration<?>) configProp;
if (me().prefix.equals(clientCfg.prefix) || clientCfg.prefix.get() == null) {
clientConfiguration.setNonEmpty(clientCfg);
}
} else {
configProp = properties.get(ClientProperties.CONNECTOR_CONFIGURATION);
if (configProp != null && me().prefix.equals(((ConnectorConfiguration<?>) configProp).prefix)) {
clientConfiguration.setNonEmpty((ConnectorConfiguration<?>) configProp);
}
}
return clientConfiguration;
}
public default CC copyFromRequest(ClientRequest request) {
CC requestConfiguration = copy();
Object configProp = request.getProperty(prefixed(ClientProperties.CONNECTOR_CONFIGURATION));
if (configProp != null) {
ConnectorConfiguration<?> requestCfg = (ConnectorConfiguration<?>) configProp;
if (me().prefix.equals(requestCfg.prefix) || requestCfg.prefix.get() == null) {
requestConfiguration.setNonEmpty(requestCfg);
}
} else {
configProp = request.getProperty(ClientProperties.CONNECTOR_CONFIGURATION);
if (configProp != null && me().prefix.equals(((ConnectorConfiguration<?>) configProp).prefix)) {
requestConfiguration.setNonEmpty((ConnectorConfiguration<?>) configProp);
}
}
return requestConfiguration;
}
@Override
default String getSniHostNameProperty(Configuration configuration) {
Object property = configuration.getProperty(prefixed(ClientProperties.SNI_HOST_NAME));
if (property == null) {
property = configuration.getProperty(prefixed(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT)));
}
return property == null ? me().sniHostname.get() : (String) property;
}
/**
* Update the {@link #expect100Continue(boolean)} from the HTTP client request.
*
* @param request the HTTP client request.
* @return the Expect: 100-Continue support value.
*/
public default Boolean expect100Continue(ClientRequest request) {
final Boolean expectContinueActivated =
request.resolveProperty(prefixed(ClientProperties.EXPECT_100_CONTINUE), Boolean.class);
if (expectContinueActivated != null) {
me().expect100Continue.set(expectContinueActivated);
}
return me().expect100Continue.get();
}
/**
* Update the {@link #expect100ContinueThreshold(long)} from the HTTP client request.
*
* @param request the HTTP client request.
* @return the content length threshold size.
*/
public default long expect100ContinueThreshold(ClientRequest request) {
me().expect100continueThreshold.set(
request.resolveProperty(prefixed(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE),
me().expect100continueThreshold.get())
);
return me().expect100continueThreshold.get();
}
/**
* Update the {@link #followRedirects(boolean)} setting from the HTTP client request. The default is {@code true}.
*
* @param request the HTTP client request.
* @return follow redirects setting.
*/
public default boolean followRedirects(ClientRequest request) {
me().followRedirects.set(
request.resolveProperty(prefixed(ClientProperties.FOLLOW_REDIRECTS), me().followRedirects.get())
);
return me().followRedirects.get();
}
/**
* Get the value of the follow redirects setting. The default is {@code true}.
*
* @return whether to follow redirects or not.
*/
public default boolean followRedirects() {
return me().followRedirects.get();
}
public default Configuration prefixedConfiguration(Configuration configuration) {
return me().prefix.get().isEmpty() ? configuration : new PrefixedConfiguration(me().prefix.get(), configuration);
}
/**
* Create optional client proxy information based on the proxy information set in the configuration
* or the HTTP client request. The used settings are {@link #proxy(Proxy)},
* {@link #proxyUri(URI)}, {@link #proxyUri(String)}, {@link #proxyUserName(String)},
* and {@link #proxyPassword(String)}.
*
* @param request the HTTP client request,
* @param requestUri the HTTP request URI. It can differ from the URI used in the request, based on other
* information set by the HTTP client request.
* @return the optional client proxy.
*/
public default Optional<ClientProxy> proxy(ClientRequest request, URI requestUri) {
Optional<ClientProxy> proxy = ClientProxy.proxyFromRequest(
me().prefix.get().isEmpty()
? request
: new PrefixedPropertiesResolver(me().prefix.get(), request)
);
if (!proxy.isPresent() && me().proxyUri.isPresent()) {
final Map<String, Object> properties = me().prefix.get().isEmpty()
? new HashMap<>()
: new PrefixedMap<>(me().prefix.get(), new HashMap<>());
properties.put(me().prefix.get() + ClientProperties.PROXY_URI, me().proxyUri.get());
properties.put(me().prefix.get() + ClientProperties.PROXY_USERNAME, me().proxyUserName.get());
properties.put(me().prefix.get() + ClientProperties.PROXY_PASSWORD, me().proxyPassword.get());
request.getPropertyNames().forEach(k -> properties.put(k, request.getProperty(k)));
proxy = ClientProxy.proxyFromProperties(properties);
}
if (!proxy.isPresent()) {
proxy = ClientProxy.proxyFromUri(requestUri);
}
return proxy;
}
/**
* Update {@link #readTimeout(int) read timeout} based on the HTTP request properties.
*
* @param request the current HTTP client request.
* @return updated configuration.
*/
public default CC readTimeout(ClientRequest request) {
me().readTimeout.set(request.resolveProperty(prefixed(ClientProperties.READ_TIMEOUT), me().readTimeout.get()));
return me();
}
/**
* Get the value of preset {@link #readTimeout(int)}.
*
* @return the read timeout milliseconds.
*/
public default int readTimeout() {
return me().readTimeout.get();
}
/**
* Get the {@link RequestEntityProcessing} updated by the HTTP client request.
*
* @param request the HTTP client request.
* @return the RequestEntityProcessing type.
*/
public default RequestEntityProcessing requestEntityProcessing(ClientRequest request) {
RequestEntityProcessing entityProcessing =
request.resolveProperty(prefixed(ClientProperties.REQUEST_ENTITY_PROCESSING), RequestEntityProcessing.class);
if (entityProcessing == null) {
entityProcessing = me().requestEntityProcessing.get();
}
return entityProcessing;
}
@Override
default String resolveSniHostNameProperty(PropertiesResolver resolver) {
String property = resolver.resolveProperty(prefixed(ClientProperties.SNI_HOST_NAME), String.class);
if (property == null) {
property = resolver.resolveProperty(
prefixed(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT)), String.class);
}
return property == null ? me().sniHostname.get() : property;
}
/**
* Get {@link SSLContext} either from the {@link ClientProperties#SSL_CONTEXT_SUPPLIER}, or from this configuration,
* or from the {@link Client#getSslContext()} in this order.
*
* @param client the client used to get the {@link SSLContext}.
* @param request the request used to get the {@link SSLContext}.
* @return the {@link SSLContext}.
*/
public default SSLContext sslContext(Client client, ClientRequest request) {
@SuppressWarnings("unchecked")
Supplier<SSLContext> supplier =
request.resolveProperty(prefixed(ClientProperties.SSL_CONTEXT_SUPPLIER), Supplier.class);
if (supplier == null) {
supplier = me().sslContextSupplier.get();
}
return supplier == null ? client.getSslContext() : supplier.get();
}
public default String prefixed(String propertyName) {
return me().prefix.get() + propertyName;
}
/**
* Return a new instance of configuration.
* @return a new instance of configuration.
*/
public CC instance();
/**
* Return typed-cast self.
* @return self.
*/
public CC me();
}
/**
* A properties map that works with prefixed properties.
*
* @param <V> Object type.
*/
private static class PrefixedMap<V> implements Map<String, V> {
private final Map<String, V> inner;
private final String prefix;
private PrefixedMap(String prefix, Map<String, V> inner) {
this.inner = inner;
this.prefix = prefix;
}
@Override
public int size() {
return inner.size();
}
@Override
public boolean isEmpty() {
return inner.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return inner.containsKey(prefix + key);
}
@Override
public boolean containsValue(Object value) {
return inner.containsValue(value);
}
@Override
public V get(Object key) {
return inner.get(prefix + key);
}
@Override
public V put(String key, V value) {
return inner.put(key, value);
}
@Override
public V remove(Object key) {
return inner.remove(prefix + key);
}
@Override
public void putAll(Map<? extends String, ? extends V> m) {
inner.putAll(m);
}
@Override
public void clear() {
inner.clear();
}
@Override
public Set<String> keySet() {
return inner.keySet();
}
@Override
public Collection<V> values() {
return inner.values();
}
@Override
public Set<Entry<String, V>> entrySet() {
return inner.entrySet();
}
}
/**
* Properties resolver that resolves prefixed properties.
*/
private static class PrefixedPropertiesResolver implements PropertiesResolver {
private final String prefix;
private final PropertiesResolver resolver;
private PrefixedPropertiesResolver(String prefix, PropertiesResolver resolver) {
this.prefix = prefix;
this.resolver = resolver;
}
@Override
public <T> T resolveProperty(String name, Class<T> type) {
return resolver.resolveProperty(prefix + name, type);
}
@Override
public <T> T resolveProperty(String name, T defaultValue) {
return resolver.resolveProperty(prefix + name, defaultValue);
}
}
protected static class PrefixedConfiguration implements Configuration {
private final String prefix;
private final Configuration inner;
private PrefixedConfiguration(String prefix, Configuration inner) {
this.prefix = prefix;
this.inner = inner;
}
@Override
public RuntimeType getRuntimeType() {
return inner.getRuntimeType();
}
@Override
public Map<String, Object> getProperties() {
return new PrefixedMap<>(prefix, inner.getProperties());
}
@Override
public Object getProperty(String name) {
return inner.getProperty(prefix + name);
}
@Override
public Collection<String> getPropertyNames() {
return inner.getPropertyNames();
}
@Override
public boolean isEnabled(Feature feature) {
return inner.isEnabled(feature);
}
@Override
public boolean isEnabled(Class<? extends Feature> featureClass) {
return inner.isEnabled(featureClass);
}
@Override
public boolean isRegistered(Object component) {
return inner.isRegistered(component);
}
@Override
public boolean isRegistered(Class<?> componentClass) {
return inner.isRegistered(componentClass);
}
@Override
public Map<Class<?>, Integer> getContracts(Class<?> componentClass) {
return inner.getContracts(componentClass);
}
@Override
public Set<Class<?>> getClasses() {
return inner.getClasses();
}
@Override
public Set<Object> getInstances() {
return inner.getInstances();
}
}
}