@@ -66,26 +66,26 @@ public abstract class ServiceProvider {
6666 * Class name of JSR-330 annotation for naming a service provider.
6767 * We use reflection for keeping JSR-330 an optional dependency.
6868 */
69- private static final String NAMED_ANNOTATION = "javax.inject.Named" ;
69+ private static final String LEGACY_NAMED_ANNOTATION = "javax.inject.Named" ;
7070
7171 /**
7272 * Class name of JSR-250 annotation for assigning a priority level to a service provider.
7373 * We use reflection for keeping JSR-250 an optional dependency.
7474 */
75- private static final String PRIORITY_ANNOTATION = "javax.annotation.Priority" ;
75+ private static final String LEGACY_PRIORITY_ANNOTATION = "javax.annotation.Priority" ;
7676
7777 /**
7878 * Class name of Jakarta Dependency Injection annotation for naming a service provider.
7979 * We use reflection for keeping Jakata Injection an optional dependency.
8080 */
81- private static final String JAKARTA_NAMED_ANNOTATION = "jakarta.inject.Named" ;
81+ private static final String NAMED_ANNOTATION = "jakarta.inject.Named" ;
8282
8383 /**
8484 * Class name of Jakarta Common Annotation for assigning a priority level to a service provider.
8585 * We use reflection for keeping Jakarta Annotations an optional dependency.
8686 */
87- private static final String JAKARTA_PRIORITY_ANNOTATION = "jakarta.annotation.Priority" ;
88-
87+ private static final String PRIORITY_ANNOTATION = "jakarta.annotation.Priority" ;
88+
8989 /**
9090 * The current service provider, or {@code null} if not yet determined.
9191 *
@@ -106,7 +106,8 @@ protected ServiceProvider() {
106106 * Allows to define a priority for a registered {@code ServiceProvider} instance.
107107 * When multiple providers are registered in the system, the provider with the highest priority value is taken.
108108 *
109- * <p>If the {@value #PRIORITY_ANNOTATION} annotation (from JSR-250) or {@value #JAKARTA_PRIORITY_ANNOTATION} annotation (from Jakarta Annotations) is present on the {@code ServiceProvider}
109+ * <p>If the {@value #PRIORITY_ANNOTATION} annotation (from Jakarta Annotations)
110+ * or {@value #LEGACY_PRIORITY_ANNOTATION} annotation (from JSR-250) is present on the {@code ServiceProvider}
110111 * implementation class, then that annotation (first if both were present) is taken and this {@code getPriority()} method is ignored.
111112 * Otherwise – if a {@code Priority} annotation is absent – this method is used as a fallback.</p>
112113 *
@@ -155,22 +156,28 @@ private static final class Selector implements Predicate<ServiceProvider>, Compa
155156 private final String toSearch ;
156157
157158 /**
158- * Class of the {@value #NAMED_ANNOTATION} and {@value #PRIORITY_ANNOTATION} annotations to search ,
159- * or {@code null} if those classes are not on the classpath.
159+ * The {@code value()} method in the {@value #NAMED_ANNOTATION} annotation ,
160+ * or {@code null} if that class is not on the classpath.
160161 */
161- private Class <? extends Annotation > namedAnnotation , priorityAnnotation ;
162-
162+ private final Method nameGetter ;
163+
164+ /**
165+ * The {@code value()} method in the {@value #PRIORITY_ANNOTATION} annotation,
166+ * or {@code null} if that class is not on the classpath.
167+ */
168+ private final Method priorityGetter ;
169+
163170 /**
164- * Class of the {@value #JAKARTA_NAMED_ANNOTATION} and {@value #JAKARTA_PRIORITY_ANNOTATION} annotations to search ,
165- * or {@code null} if those classes are not on the classpath.
171+ * The {@code value()} method in the {@value #LEGACY_NAMED_ANNOTATION} annotation ,
172+ * or {@code null} if that class is not on the classpath.
166173 */
167- private Class <? extends Annotation > jakartaNamedAnnotation , jakartaPriorityAnnotation ;
174+ private final Method legacyNameGetter ;
168175
169176 /**
170- * The {@code value()} method in the {@code *Annotation} class ,
171- * or {@code null} if those classes are not on the classpath.
177+ * The {@code value()} method in the {@value #LEGACY_PRIORITY_ANNOTATION} annotation ,
178+ * or {@code null} if that class is not on the classpath.
172179 */
173- private Method nameGetter , priorityGetter ;
180+ private final Method legacyPriorityGetter ;
174181
175182 /**
176183 * Creates a new filter and comparator for a stream of service providers.
@@ -180,60 +187,73 @@ private static final class Selector implements Predicate<ServiceProvider>, Compa
180187 Selector (String name ) {
181188 toSearch = name ;
182189 try {
183- if (name != null ) try {
184- namedAnnotation = Class .forName (NAMED_ANNOTATION ).asSubclass (Annotation .class );
185- nameGetter = namedAnnotation .getMethod ("value" , (Class []) null );
186- } catch (ClassNotFoundException e ) {
187- // Ignore since JSR-330 is an optional dependency.
188- }
189- if (nameGetter == null ) try { // if nameGetter has not been set already try Jakarta Injection
190- jakartaNamedAnnotation = Class .forName (JAKARTA_NAMED_ANNOTATION ).asSubclass (Annotation .class );
191- nameGetter = jakartaNamedAnnotation .getMethod ("value" , (Class []) null );
192- } catch (ClassNotFoundException e ) {
193- // Ignore since Jakarta Injection is an optional dependency.
194- }
195-
196- try {
197- priorityAnnotation = Class .forName (PRIORITY_ANNOTATION ).asSubclass (Annotation .class );
198- priorityGetter = priorityAnnotation .getMethod ("value" , (Class []) null );
199- } catch (ClassNotFoundException e ) {
200- // Ignore since JSR-250 is an optional dependency.
201- }
202- if (priorityGetter == null ) try { // if priorityGetter has not been set already try Jakarta Annotations
203- jakartaPriorityAnnotation = Class .forName (JAKARTA_PRIORITY_ANNOTATION ).asSubclass (Annotation .class );
204- priorityGetter = jakartaPriorityAnnotation .getMethod ("value" , (Class []) null );
205- } catch (ClassNotFoundException e ) {
206- // Ignore since Jakarta Annotations is an optional dependency.
190+ if (name != null ) {
191+ nameGetter = getValueMethod (NAMED_ANNOTATION );
192+ legacyNameGetter = getValueMethod (LEGACY_NAMED_ANNOTATION );
193+ } else {
194+ nameGetter = null ;
195+ legacyNameGetter = null ;
207196 }
197+ priorityGetter = getValueMethod (PRIORITY_ANNOTATION );
198+ legacyPriorityGetter = getValueMethod (LEGACY_PRIORITY_ANNOTATION );
208199 } catch (NoSuchMethodException e ) {
209200 // Should never happen since value() is a standard public method of those annotations.
210201 throw new ServiceConfigurationError ("Cannot get annotation value" , e );
211202 }
212203 }
213204
214205 /**
215- * Returns {@code true} if the given service provider has the name we are looking for .
216- * This method shall be invoked only if a non-null name has been specified to the constructor.
217- * This method looks for the {@value #NAMED_ANNOTATION} annotation or {@value #JAKARTA_NAMED_ANNOTATION} annotation, and if none are found fallbacks on
218- * {@link ServiceProvider#toString ()}.
206+ * Returns the {@code value()} method in the given annotation class .
207+ *
208+ * @param classname name of the class from which to get the {@code value()} method.
209+ * @return the {@code value ()} method, or {@code null} if the annotation class was not found .
219210 */
220- @ Override
221- public boolean test (ServiceProvider provider ) {
222- Object value = null ;
223- if (nameGetter != null ) {
224- Annotation a = null ;
225- if (namedAnnotation != null ) {
226- a = provider .getClass ().getAnnotation (namedAnnotation );
227- } else if (jakartaNamedAnnotation != null ) {
228- a = provider .getClass ().getAnnotation (jakartaNamedAnnotation );
229- }
211+ private static Method getValueMethod (final String classname ) throws NoSuchMethodException {
212+ try {
213+ return Class .forName (classname ).getMethod ("value" , (Class []) null );
214+ } catch (ClassNotFoundException e ) {
215+ // Ignore because JSR-330, JSR-250 and Jakarta are optional dependencies.
216+ return null ;
217+ }
218+ }
219+
220+ /**
221+ * Invokes the {@code value()} method on the annotation of the given class.
222+ * The annotation on which to invoke the method is given by {@link Method#getDeclaringClass()}.
223+ *
224+ * @param provider class of the provider on which to invoke annotation {@code value()}.
225+ * @param getter the preferred {@code value()} method to invoke, or {@code null}.
226+ * @param fallback an alternative {@code value()} method to invoke, or {@code null}.
227+ * @return the value, or {@code null} if none.
228+ */
229+ private static Object getValue (final Class <?> provider , Method getter , Method fallback ) {
230+ if (getter == null ) {
231+ getter = fallback ;
232+ fallback = null ;
233+ }
234+ while (getter != null ) {
235+ final Annotation a = provider .getAnnotation (getter .getDeclaringClass ().asSubclass (Annotation .class ));
230236 if (a != null ) try {
231- value = nameGetter .invoke (a , (Object []) null );
237+ return getter .invoke (a , (Object []) null );
232238 } catch (IllegalAccessException | InvocationTargetException e ) {
233239 // Should never happen since value() is a public method and should not throw exception.
234240 throw new ServiceConfigurationError ("Cannot get annotation value" , e );
235241 }
242+ getter = fallback ;
243+ fallback = null ;
236244 }
245+ return null ;
246+ }
247+
248+ /**
249+ * Returns {@code true} if the given service provider has the name we are looking for.
250+ * This method shall be invoked only if a non-null name has been specified to the constructor.
251+ * This method looks for the {@value #NAMED_ANNOTATION} and {@value #LEGACY_NAMED_ANNOTATION}
252+ * annotations in that order, and if none are found fallbacks on {@link ServiceProvider#toString()}.
253+ */
254+ @ Override
255+ public boolean test (final ServiceProvider provider ) {
256+ Object value = getValue (provider .getClass (), nameGetter , legacyNameGetter );
237257 if (value == null ) {
238258 value = provider .toString ();
239259 }
@@ -242,23 +262,13 @@ public boolean test(ServiceProvider provider) {
242262
243263 /**
244264 * Returns the priority of the given service provider.
245- * This method looks for the {@value #PRIORITY_ANNOTATION} annotation or {@value #JAKARTA_PRIORITY_ANNOTATION},
246- * and if none are found falls back on {@link ServiceProvider#getPriority()}.
265+ * This method looks for the {@value #PRIORITY_ANNOTATION} and {@value #LEGACY_PRIORITY_ANNOTATION}
266+ * annotations in that order, and if none are found falls back on {@link ServiceProvider#getPriority()}.
247267 */
248- private int priority (ServiceProvider provider ) {
249- if (priorityGetter != null ) {
250- Annotation a = null ;
251- if (priorityAnnotation != null ) {
252- a = provider .getClass ().getAnnotation (priorityAnnotation );
253- } else if (jakartaPriorityAnnotation != null ) {
254- a = provider .getClass ().getAnnotation (jakartaPriorityAnnotation );
255- }
256- if (a != null ) try {
257- return (Integer ) priorityGetter .invoke (a , (Object []) null );
258- } catch (IllegalAccessException | InvocationTargetException e ) {
259- // Should never happen since value() is a public method and should not throw exception.
260- throw new ServiceConfigurationError ("Cannot get annotation value" , e );
261- }
268+ private int priority (final ServiceProvider provider ) {
269+ Object value = getValue (provider .getClass (), priorityGetter , legacyPriorityGetter );
270+ if (value != null ) {
271+ return (Integer ) value ;
262272 }
263273 return provider .getPriority ();
264274 }
@@ -328,8 +338,10 @@ public static final List<ServiceProvider> available() {
328338 /**
329339 * Returns the {@link ServiceProvider} with the specified name.
330340 * The given name must match the name of at least one service provider available in the current thread's
331- * context class loader. The service provider names are the values of {@value #NAMED_ANNOTATION} annotations
332- * when present, or the value of {@link #toString()} method for providers without {@code Named} annotation.
341+ * context class loader.
342+ * The service provider names are the values of {@value #NAMED_ANNOTATION} (from Jakarta Annotations) or
343+ * {@value #LEGACY_NAMED_ANNOTATION} (from JSR-330) annotations when present (first if both were present),
344+ * or the value of {@link #toString()} method for providers without {@code Named} annotation.
333345 *
334346 * <p>Implementors are encouraged to provide an {@code Named} annotation or to override {@link #toString()}
335347 * and use a unique enough name, e.g. the class name or other distinct attributes.
0 commit comments