merge of the current 2.x into the 3.0
diff --git a/core-common/src/main/java/org/glassfish/jersey/AbstractFeatureConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/AbstractFeatureConfigurator.java
index a73b185..36a3712 100644
--- a/core-common/src/main/java/org/glassfish/jersey/AbstractFeatureConfigurator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/AbstractFeatureConfigurator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -49,9 +49,11 @@
* @param loader specific classloader (must not be NULL)
* @return list of found classes
*/
+
protected List<Class<T>> loadImplementations(Map<String, Object> applicationProperties, ClassLoader loader) {
if (PropertiesHelper.isMetaInfServicesEnabled(applicationProperties, getRuntimeType())) {
- return Stream.of(ServiceFinder.find(getContract(), loader, true).toClassArray())
+ return Stream.of(ServiceFinder.service(getContract()).loader(loader).ignoreNotFound(true)
+ .runtimeType(getRuntimeType()).find().toClassArray())
.collect(Collectors.toList());
}
return Collections.emptyList();
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java
index ea7c62c..4a4972b 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractServiceFinderConfigurator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -60,8 +60,8 @@
*/
protected List<Class<T>> loadImplementations(Map<String, Object> applicationProperties) {
if (PropertiesHelper.isMetaInfServicesEnabled(applicationProperties, runtimeType)) {
- return Stream.of(ServiceFinder.find(contract, true).toClassArray())
- .collect(Collectors.toList());
+ return Stream.of(ServiceFinder.service(contract).ignoreNotFound(true).runtimeType(runtimeType)
+ .find().toClassArray()).collect(Collectors.toList());
}
return Collections.emptyList();
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java
index 5f954ba..3b7bd24 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -114,7 +114,8 @@
static {
Set<HeaderDelegateProvider> hps = new HashSet<HeaderDelegateProvider>();
- for (HeaderDelegateProvider provider : ServiceFinder.find(HeaderDelegateProvider.class, true)) {
+ for (HeaderDelegateProvider provider : ServiceFinder
+ .service(HeaderDelegateProvider.class).ignoreNotFound(true).find()) {
hps.add(provider);
}
headerDelegateProviders = Collections.unmodifiableSet(hps);
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
index 46184d8..5aa82a3 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -24,6 +24,7 @@
import java.lang.reflect.ReflectPermission;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
@@ -38,6 +39,9 @@
import org.glassfish.jersey.internal.util.ReflectionHelper;
+import jakarta.ws.rs.ConstrainedTo;
+import jakarta.ws.rs.RuntimeType;
+
/**
* A simple service-provider lookup mechanism. A <i>service</i> is a
* well-known set of interfaces and (usually abstract) classes. A <i>service
@@ -106,7 +110,7 @@
* sun.io.StandardCodec # Standard codecs for the platform
* </pre>
* <p/>
- * To locate an codec for a given encoding name, the internal I/O code would
+ * To locate a codec for a given encoding name, the internal I/O code would
* do something like this:
* <p/>
* <pre>
@@ -133,10 +137,98 @@
private static final Logger LOGGER = Logger.getLogger(ServiceFinder.class.getName());
private static final String PREFIX = "META-INF/services/";
- private final Class<T> serviceClass;
- private final String serviceName;
- private final ClassLoader classLoader;
- private final boolean ignoreOnClassNotFound;
+
+ public static final class Builder<T> {
+ final Class<T> service;
+ String serviceName;
+ private ClassLoader loader;
+ private Boolean ignoreOnClassNotFound;
+ RuntimeType runtimeType = null;
+
+ private Builder(Class<T> serviceClass) {
+ this.service = serviceClass;
+ }
+
+ private Builder(Builder<T> builder) {
+ this.service = builder.service;
+ this.serviceName = builder.serviceName;
+ this.loader = builder.loader;
+ this.ignoreOnClassNotFound = builder.ignoreOnClassNotFound;
+ this.runtimeType = builder.runtimeType;
+ }
+
+ /**
+ * Create the service finder capable of locating the services with information specified by the builder.
+ * @return the service finder instance.
+ */
+ public ServiceFinder<T> find() {
+ if (serviceName == null) {
+ serviceName = service.getName();
+ }
+ if (loader == null) {
+ loader = _getContextClassLoader();
+ }
+ if (ignoreOnClassNotFound == null) {
+ ignoreOnClassNotFound = false;
+ }
+
+ return new ServiceFinder<T>(this);
+ }
+
+ /**
+ * Set the service name the service finder use to locate the services.
+ * @param serviceName the service name correspond to a file in
+ * META-INF/services that contains a list of fully qualified class
+ * names.
+ * @return the updated builder.
+ */
+ public Builder<T> serviceName(String serviceName) {
+ this.serviceName = serviceName;
+ return this;
+ }
+
+ /**
+ * Set the service finder to use the given {@code Classloader}. By default, the context classloader is used.
+ * @param loader the given classloader for the service finder to use when searching for the service.
+ * @return the updated builder.
+ */
+ public Builder<T> loader(ClassLoader loader) {
+ this.loader = loader;
+ return this;
+ }
+
+ /**
+ * Set the service finder to ignore the service if not found. The default is {@code false}.
+ * @param ignoreOnClassNotFound whether to ignore the service not found or not.
+ * @return the updated builder.
+ */
+ public Builder<T> ignoreNotFound(boolean ignoreOnClassNotFound) {
+ this.ignoreOnClassNotFound = ignoreOnClassNotFound;
+ return this;
+ }
+
+ /**
+ * Update the builder with a specified runtime type the searched services are constrained to it.
+ * @param runtimeType the specified runtime type.
+ * @return the updated builder.
+ */
+ public Builder<T> runtimeType(RuntimeType runtimeType) {
+ this.runtimeType = runtimeType;
+ return this;
+ }
+ }
+
+ /**
+ * Start configuring {@link Builder} with a specific service class to find.
+ * @param serviceClass the service class to find.
+ * @return a new instance of service finder builder.
+ * @param <T> type of the service class the service finder builder is created for.
+ */
+ public static <T> ServiceFinder.Builder<T> service(Class<T> serviceClass) {
+ return new Builder<>(serviceClass);
+ }
+
+ private final Builder<T> builder;
static {
final OsgiRegistry osgiRegistry = ReflectionHelper.getOsgiRegistryInstance();
@@ -201,11 +293,10 @@
* @param <T> the type of the service instance.
* @return the service finder
*/
+ @Deprecated
public static <T> ServiceFinder<T> find(final Class<T> service, final ClassLoader loader)
throws ServiceConfigurationError {
- return find(service,
- loader,
- false);
+ return service(service).loader(loader).find();
}
/**
@@ -236,12 +327,11 @@
* @param <T> the type of the service instance.
* @return the service finder
*/
+ @Deprecated
public static <T> ServiceFinder<T> find(final Class<T> service,
final ClassLoader loader,
final boolean ignoreOnClassNotFound) throws ServiceConfigurationError {
- return new ServiceFinder<T>(service,
- loader,
- ignoreOnClassNotFound);
+ return service(service).loader(loader).ignoreNotFound(ignoreOnClassNotFound).find();
}
/**
@@ -251,7 +341,7 @@
* <p/>
* <pre>
* ClassLoader cl = Thread.currentThread().getContextClassLoader();
- * return Service.providers(service, cl, false);
+ * return ServiceFinder.service(service).loader(cl).ignoreNotFound(false).find();
* </pre>
* @param service The service's abstract service class
* @throws ServiceConfigurationError If a provider-configuration file violates the specified format
@@ -262,9 +352,7 @@
*/
public static <T> ServiceFinder<T> find(final Class<T> service)
throws ServiceConfigurationError {
- return find(service,
- _getContextClassLoader(),
- false);
+ return ServiceFinder.service(service).find();
}
/**
@@ -275,7 +363,7 @@
* <pre>
* ClassLoader cl = Thread.currentThread().getContextClassLoader();
* boolean ingore = ...
- * return Service.providers(service, cl, ignore);
+ * return ServiceFinder.service(service).loader(cl).ignoreNotFound(ignore).find();
* </pre>
* @param service The service's abstract service class
* @param ignoreOnClassNotFound If a provider cannot be loaded by the class loader
@@ -286,11 +374,10 @@
* @param <T> the type of the service instance.
* @return the service finder
*/
+ @Deprecated
public static <T> ServiceFinder<T> find(final Class<T> service,
final boolean ignoreOnClassNotFound) throws ServiceConfigurationError {
- return find(service,
- _getContextClassLoader(),
- ignoreOnClassNotFound);
+ return ServiceFinder.service(service).ignoreNotFound(ignoreOnClassNotFound).find();
}
/**
@@ -305,7 +392,7 @@
* @return the service finder
*/
public static ServiceFinder<?> find(final String serviceName) throws ServiceConfigurationError {
- return new ServiceFinder<Object>(Object.class, serviceName, _getContextClassLoader(), false);
+ return service(Object.class).serviceName(serviceName).find();
}
/**
@@ -325,22 +412,8 @@
ServiceIteratorProvider.setInstance(sip);
}
- private ServiceFinder(
- final Class<T> service,
- final ClassLoader loader,
- final boolean ignoreOnClassNotFound) {
- this(service, service.getName(), loader, ignoreOnClassNotFound);
- }
-
- private ServiceFinder(
- final Class<T> service,
- final String serviceName,
- final ClassLoader loader,
- final boolean ignoreOnClassNotFound) {
- this.serviceClass = service;
- this.serviceName = serviceName;
- this.classLoader = loader;
- this.ignoreOnClassNotFound = ignoreOnClassNotFound;
+ private ServiceFinder(Builder<T> builder) {
+ this.builder = new Builder<>(builder);
}
/**
@@ -354,8 +427,7 @@
*/
@Override
public Iterator<T> iterator() {
- return ServiceIteratorProvider.getInstance()
- .createIterator(serviceClass, serviceName, classLoader, ignoreOnClassNotFound);
+ return createIterator();
}
/**
@@ -373,7 +445,7 @@
for (final T t : this) {
result.add(t);
}
- return result.toArray((T[]) Array.newInstance(serviceClass, result.size()));
+ return result.toArray((T[]) Array.newInstance(builder.service, result.size()));
}
/**
@@ -388,16 +460,68 @@
@SuppressWarnings("unchecked")
public Class<T>[] toClassArray() throws ServiceConfigurationError {
final List<Class<T>> result = new ArrayList<Class<T>>();
-
- final ServiceIteratorProvider iteratorProvider = ServiceIteratorProvider.getInstance();
- final Iterator<Class<T>> i = iteratorProvider
- .createClassIterator(serviceClass, serviceName, classLoader, ignoreOnClassNotFound);
+ final Iterator<Class<T>> i = createClassIterator();
while (i.hasNext()) {
result.add(i.next());
}
return result.toArray((Class<T>[]) Array.newInstance(Class.class, result.size()));
}
+ /**
+ * Return true iff the service class is not constrained to other runtime type.
+ * @param clazz the service class.
+ * @param runtimeType the expected constraint runtime type.
+ * @return {@code true} when the service class is constrained to configurator's runtime type or {@code false} otherwise.
+ */
+ private static boolean isConstrained(Class<?> clazz, RuntimeType runtimeType) {
+ final ConstrainedTo annotation = clazz.getAnnotation(ConstrainedTo.class);
+ return annotation == null || annotation.value() == runtimeType;
+ }
+
+ private Iterator<Class<T>> createClassIterator() {
+ final Iterator<Class<T>> it = ServiceIteratorProvider.getInstance().createClassIterator(
+ builder.service, builder.serviceName, builder.loader, builder.ignoreOnClassNotFound);
+ return builder.runtimeType == null ? it : new ConstrainedIterator<Class<T>>(it, builder.runtimeType);
+ }
+
+ private Iterator<T> createIterator() {
+ final Iterator<T> it = ServiceIteratorProvider.getInstance().createIterator(
+ builder.service, builder.serviceName, builder.loader, builder.ignoreOnClassNotFound);
+ return builder.runtimeType == null ? it : new ConstrainedIterator<T>(it, builder.runtimeType);
+ }
+
+ private static final class ConstrainedIterator<IT> implements Iterator<IT> {
+ private final Iterator<IT> i;
+ private final RuntimeType runtimeType;
+ private IT next;
+
+ private ConstrainedIterator(Iterator<IT> i, RuntimeType runtimeType) {
+ this.i = i;
+ this.runtimeType = runtimeType;
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (next == null && i.hasNext()) {
+ next = i.next();
+ if (!isConstrained(next.getClass() == Class.class ? (Class<?>) next : next.getClass(), runtimeType)) {
+ next = null;
+ }
+ }
+ return next != null;
+ }
+
+ @Override
+ public IT next() {
+ if (next == null && !hasNext()) {
+ throw new NoSuchElementException();
+ }
+ final IT n = next;
+ next = null;
+ return n;
+ }
+ }
+
private static void fail(final String serviceName, final String msg, final Throwable cause)
throws ServiceConfigurationError {
final ServiceConfigurationError sce = new ServiceConfigurationError(serviceName + ": " + msg);
@@ -480,7 +604,7 @@
final URLConnection uConn = u.openConnection();
uConn.setUseCaches(false);
in = uConn.getInputStream();
- r = new BufferedReader(new InputStreamReader(in, "utf-8"));
+ r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
int lc = 1;
while ((lc = parseLine(serviceName, u, r, lc, names, returned)) >= 0) {
// continue
@@ -863,7 +987,7 @@
* The default service iterator provider that looks up provider classes in
* META-INF/services files.
* <p>
- * This class may utilized if a {@link ServiceIteratorProvider} needs to
+ * This class may be utilized if a {@link ServiceIteratorProvider} needs to
* reuse the default implementation.
*/
public static final class DefaultServiceIteratorProvider extends ServiceIteratorProvider {
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinderBinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinderBinder.java
index 223d32b..670562d 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinderBinder.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinderBinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -58,7 +58,8 @@
@Override
protected void configure() {
if (PropertiesHelper.isMetaInfServicesEnabled(applicationProperties, runtimeType)) {
- for (Class<T> t : ServiceFinder.find(contract, true).toClassArray()) {
+ for (Class<T> t : ServiceFinder.service(contract).ignoreNotFound(true).runtimeType(runtimeType).find()
+ .toClassArray()) {
bind(t).to(contract);
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java
index 1750dd8..9444e9c 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -93,16 +93,12 @@
* @param clazz type of service to look for.
* @param <T> type of service to look for.
* @param type {@link RuntimeType} the {@link InjectionManagerFactory} must be {@link ConstrainedTo} if annotated.
- * @return instance of service with highest priority or {@code null} if service of given type cannot be found.
+ * @return instance of service with the highest priority or {@code null} if service of given type cannot be found.
* @see jakarta.annotation.Priority
*/
private static <T> Optional<T> lookupService(final Class<T> clazz, RuntimeType type) {
List<RankedProvider<T>> providers = new LinkedList<>();
- for (T provider : ServiceFinder.find(clazz)) {
- ConstrainedTo constrain = provider.getClass().getAnnotation(ConstrainedTo.class);
- if (constrain != null && type != constrain.value()) {
- continue;
- }
+ for (T provider : ServiceFinder.service(clazz).runtimeType(type).find()) {
providers.add(new RankedProvider<>(provider));
}
providers.sort(new RankedComparator<>(RankedComparator.Order.DESCENDING));
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
index f5c467a..e0aa5bf 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -107,7 +107,8 @@
interfaces.put(Binder.class, ProviderRuntime.BOTH);
try {
- ServiceFinder<ExternalRegistrables> registerables = ServiceFinder.find(ExternalRegistrables.class, true);
+ ServiceFinder<ExternalRegistrables> registerables =
+ ServiceFinder.service(ExternalRegistrables.class).ignoreNotFound(true).find();
registerables.forEach(regs -> regs.registrableContracts()
.forEach(pair -> interfaces.put(pair.getContract(), ProviderRuntime.fromRuntimeType(pair.getRuntimeType()))));
} catch (Throwable t) {
diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java
index 1afa8c1..41d0868 100644
--- a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java
+++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -596,8 +596,8 @@
// Forced (always invoked).
final List<ForcedAutoDiscoverable> forcedAutoDiscroverables = new LinkedList<>();
- for (Class<ForcedAutoDiscoverable> forcedADType : ServiceFinder.find(ForcedAutoDiscoverable.class, true)
- .toClassArray()) {
+ for (Class<ForcedAutoDiscoverable> forcedADType : ServiceFinder.service(ForcedAutoDiscoverable.class)
+ .ignoreNotFound(true).runtimeType(getRuntimeType()).find().toClassArray()) {
forcedAutoDiscroverables.add(injectionManager.createAndInitialize(forcedADType));
}
providers.addAll(forcedAutoDiscroverables);
@@ -608,15 +608,11 @@
}
for (final AutoDiscoverable autoDiscoverable : providers) {
- final ConstrainedTo constrainedTo = autoDiscoverable.getClass().getAnnotation(ConstrainedTo.class);
-
- if (constrainedTo == null || type.equals(constrainedTo.value())) {
- try {
- autoDiscoverable.configure(this);
- } catch (final Exception e) {
- LOGGER.log(Level.FINE,
- LocalizationMessages.AUTODISCOVERABLE_CONFIGURATION_FAILED(autoDiscoverable.getClass()), e);
- }
+ try {
+ autoDiscoverable.configure(this);
+ } catch (final Exception e) {
+ LOGGER.log(Level.FINE,
+ LocalizationMessages.AUTODISCOVERABLE_CONFIGURATION_FAILED(autoDiscoverable.getClass()), e);
}
}
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
index 42d11dd..10e0d81 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2025 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
@@ -366,7 +366,7 @@
if (!disableValidation()) {
ComponentModelValidator validator = new ComponentModelValidator(
- bootstrapBag.getValueParamProviders(), bootstrapBag.getMessageBodyWorkers());
+ bootstrapBag.getValueParamProviders(), bootstrapBag.getMessageBodyWorkers(), runtimeConfig);
validator.validate(bootstrapBag.getResourceModel());
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java
index d3d67d2..7619fcf 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -43,7 +43,8 @@
public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag;
- Class<ExternalRequestScope>[] extScopes = ServiceFinder.find(ExternalRequestScope.class, true).toClassArray();
+ Class<ExternalRequestScope>[] extScopes =
+ ServiceFinder.service(ExternalRequestScope.class).ignoreNotFound(true).find().toClassArray();
boolean extScopeBound = false;
if (extScopes.length == 1) {
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
index 6414204..47c784e 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -462,6 +462,23 @@
"jersey.config.server.resource.validation.ignoreErrors";
/**
+ * If {@code true} then validation of application resource models will not log a warning when a get resource method consumes an entity.
+ *
+ * This impacts both the validation of root resources during deployment as well as validation of any sub resources
+ * returned from sub-resource locators.
+ * <p>
+ * The default value is {@code false}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ *
+ * @see #RESOURCE_VALIDATION_DISABLE
+ */
+ public static final String RESOURCE_VALIDATION_IGNORE_GET_CONSUMES_ENTITY_WARNINGS =
+ "jersey.config.server.resource.validation.ignoreGetConsumesEntityWarnings";
+
+ /**
* If {@code true} then application monitoring will be enabled.
*
* This will enable the possibility
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java
index aa5c0f3..23b926f 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -228,7 +228,7 @@
Errors.process(new Runnable() {
@Override
public void run() {
- final ComponentModelValidator validator = new ComponentModelValidator(valueSuppliers, messageBodyWorkers);
+ final ComponentModelValidator validator = new ComponentModelValidator(valueSuppliers, messageBodyWorkers, config);
validator.validate(component);
if (Errors.fatalIssuesFound() && !ignoreValidationErrors) {
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java
index b93b172..5cd6878 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -26,6 +26,8 @@
import org.glassfish.jersey.server.model.internal.ModelErrors;
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
+import jakarta.ws.rs.core.Configuration;
+
/**
* A resource model validator that checks the given resource model.
*
@@ -57,10 +59,16 @@
private final List<ResourceModelIssue> issueList = new LinkedList<>();
public ComponentModelValidator(Collection<ValueParamProvider> valueParamProviders, MessageBodyWorkers msgBodyWorkers) {
+ this(valueParamProviders, msgBodyWorkers, null);
+ }
+
+ public ComponentModelValidator(Collection<ValueParamProvider> valueParamProviders,
+ MessageBodyWorkers msgBodyWorkers,
+ Configuration configuration) {
validators = new ArrayList<>();
validators.add(new ResourceValidator());
validators.add(new RuntimeResourceModelValidator(msgBodyWorkers));
- validators.add(new ResourceMethodValidator(valueParamProviders));
+ validators.add(new ResourceMethodValidator(valueParamProviders, configuration));
validators.add(new InvocableValidator());
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java
index ac0c1da..2ab0707 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -37,10 +37,11 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
-import jakarta.ws.rs.sse.SseEventSink;
+import jakarta.ws.rs.core.Configuration;
import org.glassfish.jersey.internal.Errors;
import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.jersey.server.model.internal.SseTypeResolver;
import org.glassfish.jersey.server.spi.internal.ParameterValueHelper;
@@ -55,9 +56,15 @@
class ResourceMethodValidator extends AbstractResourceModelVisitor {
private final Collection<ValueParamProvider> valueParamProviders;
+ private final Configuration configuration;
ResourceMethodValidator(Collection<ValueParamProvider> valueParamProviders) {
+ this(valueParamProviders, null);
+ }
+
+ ResourceMethodValidator(Collection<ValueParamProvider> valueParamProviders, Configuration configuration) {
this.valueParamProviders = valueParamProviders;
+ this.configuration = configuration;
}
@Override
@@ -100,7 +107,7 @@
}
// ensure GET does not consume an entity parameter, if not inflector-based
- if (invocable.requiresEntity() && !invocable.isInflector()) {
+ if (invocable.requiresEntity() && !invocable.isInflector() && !shouldIgnoreGetConsumesEntityWarnings(configuration)) {
Errors.warning(method, LocalizationMessages.GET_CONSUMES_ENTITY(invocable.getHandlingMethod()));
}
// ensure GET does not consume any @FormParam annotated parameter
@@ -154,6 +161,16 @@
}
}
+ private static Boolean shouldIgnoreGetConsumesEntityWarnings(Configuration configuration) {
+ if (configuration == null) {
+ return Boolean.FALSE;
+ }
+ return ServerProperties.getValue(configuration.getProperties(),
+ ServerProperties.RESOURCE_VALIDATION_IGNORE_GET_CONSUMES_ENTITY_WARNINGS,
+ Boolean.FALSE,
+ Boolean.class);
+ }
+
private void checkUnexpectedAnnotations(ResourceMethod resourceMethod) {
Invocable invocable = resourceMethod.getInvocable();
for (Annotation annotation : invocable.getHandlingMethod().getDeclaredAnnotations()) {
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/JerseyClassAnalyzer.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/JerseyClassAnalyzer.java
index bceaeda..d9963b5 100644
--- a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/JerseyClassAnalyzer.java
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/JerseyClassAnalyzer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -25,7 +25,6 @@
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Set;
-import java.util.function.Supplier;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@@ -35,9 +34,6 @@
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.inject.InjectionResolver;
import org.glassfish.jersey.internal.util.collection.ImmutableCollectors;
-import org.glassfish.jersey.internal.util.collection.LazyValue;
-import org.glassfish.jersey.internal.util.collection.Value;
-import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.hk2.api.ClassAnalyzer;
import org.glassfish.hk2.api.MultiException;
@@ -79,33 +75,31 @@
@Override
protected void configure() {
- ClassAnalyzer defaultAnalyzer =
- serviceLocator.getService(ClassAnalyzer.class, ClassAnalyzer.DEFAULT_IMPLEMENTATION_NAME);
-
- Supplier<List<InjectionResolver>> resolvers = () -> serviceLocator.getAllServices(InjectionResolver.class);
-
- bind(new JerseyClassAnalyzer(defaultAnalyzer, resolvers))
+ bind(JerseyClassAnalyzer.class)
.analyzeWith(ClassAnalyzer.DEFAULT_IMPLEMENTATION_NAME)
.named(JerseyClassAnalyzer.NAME)
.to(ClassAnalyzer.class);
}
}
+ private final Set<Class> resolverAnnotations;
private final ClassAnalyzer defaultAnalyzer;
- private final LazyValue<Set<Class>> resolverAnnotations;
+
/**
* Injection constructor.
*
- * @param defaultAnalyzer default HK2 class analyzer.
- * @param supplierResolvers configured injection resolvers.
+ * @param serviceLocator current injection manager.
*/
- private JerseyClassAnalyzer(ClassAnalyzer defaultAnalyzer, Supplier<List<InjectionResolver>> supplierResolvers) {
- this.defaultAnalyzer = defaultAnalyzer;
- Value<Set<Class>> resolvers = () -> supplierResolvers.get().stream()
+ @Inject
+ public JerseyClassAnalyzer(ServiceLocator serviceLocator) {
+ defaultAnalyzer = serviceLocator.getService(ClassAnalyzer.class, ClassAnalyzer.DEFAULT_IMPLEMENTATION_NAME);
+ // Load the resolver annotations once to avoid potential deadlock later
+ // See https://github.com/eclipse-ee4j/jersey/issues/5996
+ List<InjectionResolver> resolvers = serviceLocator.getAllServices(InjectionResolver.class);
+ this.resolverAnnotations = resolvers.stream()
.filter(InjectionResolver::isConstructorParameterIndicator)
.map(InjectionResolver::getAnnotation)
.collect(ImmutableCollectors.toImmutableSet());
- this.resolverAnnotations = Values.lazy(resolvers);
}
@SuppressWarnings("unchecked")
@@ -186,7 +180,7 @@
final int paramSize = constructor.getParameterTypes().length;
- if (paramSize != 0 && resolverAnnotations.get().isEmpty()) {
+ if (paramSize != 0 && resolverAnnotations.isEmpty()) {
return false;
}
@@ -200,7 +194,7 @@
for (final Annotation[] paramAnnotations : constructor.getParameterAnnotations()) {
boolean found = false;
for (final Annotation paramAnnotation : paramAnnotations) {
- if (resolverAnnotations.get().contains(paramAnnotation.annotationType())) {
+ if (resolverAnnotations.contains(paramAnnotation.annotationType())) {
found = true;
break;
}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/AutoDiscoverableTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/AutoDiscoverableTest.java
index 3dea887..476c255 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/AutoDiscoverableTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/AutoDiscoverableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -17,6 +17,9 @@
package org.glassfish.jersey.tests.e2e.common;
import java.io.IOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
import jakarta.ws.rs.ConstrainedTo;
import jakarta.ws.rs.POST;
@@ -31,13 +34,20 @@
import jakarta.ws.rs.ext.WriterInterceptorContext;
import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.AutoDiscoverableConfigurator;
+import org.glassfish.jersey.internal.BootstrapBag;
+import org.glassfish.jersey.internal.ServiceFinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
import org.glassfish.jersey.internal.spi.AutoDiscoverable;
import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Note: Auto-discoverables from this test "affects" all other tests in suit.
@@ -133,6 +143,84 @@
public void testAutoDiscoverableConstrainedTo() throws Exception {
final Response response = target().request().post(Entity.text("value"));
- assertEquals("value-common-client-common-server", response.readEntity(String.class));
+ Assertions.assertEquals("value-common-client-common-server", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testServiceFinderIterator() {
+ Class<AutoDiscoverable>[] array =
+ ServiceFinder.service(AutoDiscoverable.class).runtimeType(RuntimeType.SERVER).find().toClassArray();
+ int size = array.length;
+
+ Assertions.assertTrue(size > 3);
+
+ ServiceFinder<AutoDiscoverable> finder =
+ ServiceFinder.service(AutoDiscoverable.class).runtimeType(RuntimeType.SERVER).find();
+ AutoDiscoverable next = null;
+ // check next()
+ final Iterator<AutoDiscoverable> it = finder.iterator();
+ for (int i = 0; i != size; i++) {
+ AutoDiscoverable n = it.next();
+ Assertions.assertNotSame(next, n);
+ next = n;
+ }
+ Assertions.assertThrows(NoSuchElementException.class, it::next);
+
+ // check hasNext();
+ final Iterator<AutoDiscoverable> it2 = finder.iterator();
+ next = null;
+ for (int i = 0; i != size; i++) {
+ for (int j = 0; j != size + 1; j++) {
+ Assertions.assertTrue(it2.hasNext());
+ }
+ AutoDiscoverable n = it2.next();
+ Assertions.assertNotSame(next, n);
+ next = n;
+ }
+ Assertions.assertFalse(it2.hasNext());
+ Assertions.assertThrows(NoSuchElementException.class, it2::next);
+ }
+
+ @Test
+ public void testAutoDiscoverableConstrainedConfigurator() {
+ Class<?>[] array = ServiceFinder.find(AutoDiscoverable.class).toClassArray();
+ Assertions.assertTrue(contains(ClientAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(CommonAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(ServerAutoDiscoverable.class, array));
+
+ array = ServiceFinder.service(AutoDiscoverable.class).find().toClassArray();
+ Assertions.assertTrue(contains(ClientAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(CommonAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(ServerAutoDiscoverable.class, array));
+
+ array = ServiceFinder.service(AutoDiscoverable.class).runtimeType(RuntimeType.SERVER).find().toClassArray();
+ Assertions.assertFalse(contains(ClientAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(CommonAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(ServerAutoDiscoverable.class, array));
+
+ array = ServiceFinder.service(AutoDiscoverable.class).runtimeType(RuntimeType.CLIENT).find().toClassArray();
+ Assertions.assertTrue(contains(ClientAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(CommonAutoDiscoverable.class, array));
+ Assertions.assertFalse(contains(ServerAutoDiscoverable.class, array));
+
+ AutoDiscoverableConfigurator configurator = new AutoDiscoverableConfigurator(RuntimeType.SERVER);
+ InjectionManager injectionManager = Injections.createInjectionManager();
+ BootstrapBag bb = new BootstrapBag();
+ bb.setConfiguration(new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL));
+ configurator.init(injectionManager, bb);
+ array = bb.getAutoDiscoverables().stream().map(ad -> ad.getClass()).collect(Collectors.toList())
+ .toArray(new Class[0]);
+ Assertions.assertFalse(contains(ClientAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(CommonAutoDiscoverable.class, array));
+ Assertions.assertTrue(contains(ServerAutoDiscoverable.class, array));
+ }
+
+ private static boolean contains(Class<?> clazz, Class<?>... list) {
+ for (Class<?> listClass : list) {
+ if (listClass.equals(clazz)) {
+ return true;
+ }
+ }
+ return false;
}
}