Move ConstraintTo check into ServiceFinder, to a single spot. Filters ServiceFinder.toClassArray by ConstrainedTo ServiceFinder has a builder Signed-off-by: jansupol <jan.supol@oracle.com>
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 61b7eb5..d975814 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 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 b528eaf..2303bb1 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 37b6d93..a3665ad 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 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..78fc695 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 javax.ws.rs.ConstrainedTo; +import javax.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 48b8ebb..6c7d297 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, 2019 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 f944637..911aa01 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 javax.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 72e2f4b..4448075 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 c53fb81..d56c428 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 @@ -594,8 +594,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); @@ -606,15 +606,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/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/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 87fc2f6..73c2fdc 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 javax.ws.rs.ConstrainedTo; import javax.ws.rs.POST; @@ -31,13 +34,20 @@ import javax.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; } }