Skip to content

Commit

Permalink
ServiceFinder defaults to ServiceLookupIteratorProvider
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Bescos Gascon <[email protected]>
  • Loading branch information
jbescos committed Mar 9, 2022
1 parent 2220c83 commit 25654bb
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022 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
Expand Down Expand Up @@ -106,7 +106,7 @@ public static synchronized OsgiRegistry getInstance() {

private final class OsgiServiceFinder extends ServiceFinder.ServiceIteratorProvider {

final ServiceFinder.ServiceIteratorProvider defaultIterator = new ServiceFinder.DefaultServiceIteratorProvider();
final ServiceFinder.ServiceIteratorProvider defaultIterator = new ServiceFinder.ServiceReflectionIteratorProvider();

@Override
public <T> Iterator<T> createIterator(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2022 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
Expand Down Expand Up @@ -31,10 +31,12 @@
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.glassfish.jersey.internal.util.ReflectionHelper;

Expand Down Expand Up @@ -229,7 +231,9 @@ public static <T> ServiceFinder<T> find(final Class<T> service, final ClassLoade
* class loader (or, failing that the bootstrap class loader) is to
* be used
* @param ignoreOnClassNotFound If a provider cannot be loaded by the class loader
* then move on to the next available provider.
* then move on to the next available provider. This value does
* not apply when the {@link ServiceIteratorProvider} is set to
* {@link ServiceLookupIteratorProvider}.
* @throws ServiceConfigurationError If a provider-configuration file violates the specified format
* or names a provider class that cannot be found and instantiated
* @see #find(Class)
Expand Down Expand Up @@ -279,7 +283,10 @@ public static <T> ServiceFinder<T> find(final Class<T> service)
* </pre>
* @param service The service's abstract service class
* @param ignoreOnClassNotFound If a provider cannot be loaded by the class loader
* then move on to the next available provider.
* then move on to the next available provider. This value does
* not apply when the {@link ServiceIteratorProvider} is set to
* {@link ServiceLookupIteratorProvider}.
*
* @throws ServiceConfigurationError If a provider-configuration file violates the specified format
* or names a provider class that cannot be found and instantiated
* @see #find(Class, ClassLoader)
Expand Down Expand Up @@ -312,7 +319,7 @@ public static ServiceFinder<?> find(final String serviceName) throws ServiceConf
* Register the service iterator provider to iterate on provider instances
* or classes.
* <p>
* The default implementation registered, {@link DefaultServiceIteratorProvider},
* The default implementation registered, {@link ServiceLookupIteratorProvider},
* looks up provider classes in META-INF/service files.
* <p>
* This method must be called prior to any attempts to obtain provider
Expand Down Expand Up @@ -790,7 +797,7 @@ private void handleClassNotFoundException() throws ServiceConfigurationError {
* Supports iteration of provider instances or classes.
* <p>
* The default implementation looks up provider classes from META-INF/services
* files, see {@link DefaultServiceIteratorProvider}.
* files, see {@link ServiceLookupIteratorProvider}.
* This implementation may be overridden by invoking
* {@link ServiceFinder#setIteratorProvider(org.glassfish.jersey.internal.ServiceFinder.ServiceIteratorProvider)}.
*/
Expand All @@ -806,7 +813,7 @@ private static ServiceIteratorProvider getInstance() {
synchronized (sipLock) {
result = sip;
if (result == null) { // Second check (with locking)
sip = result = new DefaultServiceIteratorProvider();
sip = result = new ServiceLookupIteratorProvider();
}
}
}
Expand Down Expand Up @@ -834,7 +841,9 @@ private static void setInstance(final ServiceIteratorProvider sip) throws Securi
* classes.
* @param ignoreOnClassNotFound if true ignore an instance if the
* corresponding provider class if cannot be found,
* otherwise throw a {@link ClassNotFoundException}.
* otherwise throw a {@link ClassNotFoundException}. This value does
* not apply when the {@link ServiceIteratorProvider} is set to
* {@link ServiceLookupIteratorProvider}.
* @return the provider instance iterator.
*/
public abstract <T> Iterator<T> createIterator(Class<T> service,
Expand All @@ -851,6 +860,8 @@ public abstract <T> Iterator<T> createIterator(Class<T> service,
* @param ignoreOnClassNotFound if true ignore the provider class if
* cannot be found,
* otherwise throw a {@link ClassNotFoundException}.
* This value does not apply when the {@link ServiceIteratorProvider}
* is set to {@link ServiceLookupIteratorProvider}.
* @return the provider class iterator.
*/
public abstract <T> Iterator<Class<T>> createClassIterator(Class<T> service,
Expand All @@ -860,13 +871,10 @@ public abstract <T> Iterator<Class<T>> createClassIterator(Class<T> service,
}

/**
* The default service iterator provider that looks up provider classes in
* The service iterator provider that looks up provider classes in
* META-INF/services files.
* <p>
* This class may utilized if a {@link ServiceIteratorProvider} needs to
* reuse the default implementation.
*/
public static final class DefaultServiceIteratorProvider extends ServiceIteratorProvider {
public static final class ServiceReflectionIteratorProvider extends ServiceIteratorProvider {

@Override
public <T> Iterator<T> createIterator(final Class<T> service, final String serviceName,
Expand All @@ -880,4 +888,41 @@ public <T> Iterator<Class<T>> createClassIterator(final Class<T> service, final
return new LazyClassIterator<T>(service, serviceName, loader, ignoreOnClassNotFound);
}
}

/**
* The service iterator provider that looks up provider classes in
* META-INF/services files using {@link ServiceLoader}.
*/
public static final class ServiceLookupIteratorProvider extends ServiceIteratorProvider {

@Override
public <T> Iterator<T> createIterator(final Class<T> service, final String serviceName,
final ClassLoader loader, final boolean ignoreOnClassNotFound) {
Class<T> clazz = fixGenericService(service, serviceName, loader, ignoreOnClassNotFound);
return ServiceLoader.load(clazz, loader).iterator();
}

@Override
public <T> Iterator<Class<T>> createClassIterator(final Class<T> service, final String serviceName,
final ClassLoader loader, final boolean ignoreOnClassNotFound) {
Class<T> clazz = fixGenericService(service, serviceName, loader, ignoreOnClassNotFound);
List<Class<T>> classes = ServiceLoader.load(clazz, loader).stream()
.map(provider -> (Class<T>) provider.type())
.collect(Collectors.toList());
return classes.iterator();
}

private <T> Class<T> fixGenericService(final Class<T> service, final String serviceName,
final ClassLoader loader, final boolean ignoreOnClassNotFound) {
Class<T> clazz = service;
if (Object.class == service) {
try {
clazz = (Class<T>) ReflectionHelper.classForNameWithExceptionPEA(serviceName, loader).run();
} catch (Exception e) {
// Ignore it. Later, the service implementation will not be loaded.
}
}
return clazz;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2022 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.server.internal;

public interface ServiceExample {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2022 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
Expand All @@ -16,29 +16,128 @@

package org.glassfish.jersey.server.internal;

import static org.glassfish.jersey.server.JarUtils.createJarFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import jakarta.ws.rs.container.DynamicFeature;
import jakarta.ws.rs.core.Configurable;

import org.glassfish.jersey.internal.ServiceConfigurationError;
import org.glassfish.jersey.internal.ServiceFinder;
import org.glassfish.jersey.internal.ServiceFinder.ServiceIteratorProvider;
import org.glassfish.jersey.internal.ServiceFinder.ServiceLookupIteratorProvider;
import org.glassfish.jersey.internal.ServiceFinder.ServiceReflectionIteratorProvider;
import org.glassfish.jersey.server.JarUtils;

import org.glassfish.jersey.server.JaxRsFeatureRegistrationTest.DynamicFeatureImpl;
import org.junit.AfterClass;
import org.junit.Test;
import static org.glassfish.jersey.server.JarUtils.createJarFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
* @author Michal Gajdos
*/
public class ServiceFinderTest {

@AfterClass
public static void afterClass() {
// Restore the default
ServiceFinder.setIteratorProvider(null);
}

@Test
public void testExistingClass() {
ServiceIteratorProvider[] providers = new ServiceIteratorProvider[] {
new ServiceReflectionIteratorProvider(), new ServiceLookupIteratorProvider()};
for (ServiceIteratorProvider provider : providers) {
ServiceFinder.setIteratorProvider(provider);
ServiceFinder<?> serviceFinder = ServiceFinder.find(DynamicFeature.class);
checks(provider, serviceFinder);
serviceFinder = ServiceFinder.find("jakarta.ws.rs.container.DynamicFeature");
checks(provider, serviceFinder);
}
}

@Test
public void testMissingService() {
ServiceIteratorProvider[] providers = new ServiceIteratorProvider[] {
new ServiceReflectionIteratorProvider(), new ServiceLookupIteratorProvider()};
for (ServiceIteratorProvider provider : providers) {
ServiceFinder.setIteratorProvider(provider);
ServiceFinder<?> serviceFinder = ServiceFinder.find(Configurable.class);
assertFalse(serviceFinder.iterator().hasNext());
serviceFinder = ServiceFinder.find("jakarta.ws.rs.core.Configurable");
assertFalse(serviceFinder.iterator().hasNext());
}
}

@Test
public void testClassNotFound() {
ServiceIteratorProvider[] providers = new ServiceIteratorProvider[] {
new ServiceReflectionIteratorProvider(), new ServiceLookupIteratorProvider()};
for (ServiceIteratorProvider provider : providers) {
ServiceFinder.setIteratorProvider(provider);
ServiceFinder<?> serviceFinder = ServiceFinder.find("doesNotExist");
assertFalse(serviceFinder.iterator().hasNext());
}
}

@Test
public void testServiceReflectionIteratorProviderImplementationNotFound() {
ServiceFinder.setIteratorProvider(new ServiceReflectionIteratorProvider());
ServiceFinder<?> serviceFinder = ServiceFinder.find(ServiceExample.class, true);
assertFalse(serviceFinder.iterator().hasNext());
serviceFinder = ServiceFinder.find(ServiceExample.class, false);
try {
serviceFinder.iterator().hasNext();
fail("It is expected to fail");
} catch (ServiceConfigurationError e) {
// Expected
}
}

@Test
public void testServiceLookupIteratorProviderImplementationNotFound() {
ServiceFinder.setIteratorProvider(new ServiceLookupIteratorProvider());
ServiceFinder<?> serviceFinder = ServiceFinder.find(ServiceExample.class, true);
Iterator<?> iterator = serviceFinder.iterator();
try {
iterator.hasNext();
iterator.next();
fail("It is expected to fail");
} catch (java.util.ServiceConfigurationError e) {
// Expected
}
serviceFinder = ServiceFinder.find(ServiceExample.class, false);
iterator = serviceFinder.iterator();
try {
iterator.hasNext();
iterator.next();
fail("It is expected to fail");
} catch (java.util.ServiceConfigurationError e) {
// Expected
}
}

private void checks(ServiceIteratorProvider provider, ServiceFinder<?> serviceFinder) {
Iterator<?> iterator = serviceFinder.iterator();
assertTrue("No instance found with " + provider, iterator.hasNext());
Object dynamicFeature = iterator.next();
assertEquals(DynamicFeatureImpl.class, dynamicFeature.getClass());
}

@Test
public void testJarTopLevel() throws Exception {
final Map<String, String> map = new HashMap<>();
Expand All @@ -49,6 +148,7 @@ public void testJarTopLevel() throws Exception {
final String path = ServiceFinderTest.class.getResource("").getPath();
final ClassLoader classLoader = createClassLoader(path.substring(0, path.indexOf("org")), map);

ServiceFinder.setIteratorProvider(new ServiceReflectionIteratorProvider());
final ServiceFinder<?> finder = createServiceFinder(classLoader, "jaxrs-components");

final Set<Class<?>> s = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unknown
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@
-Xmx${surefire.maxmem.argline}m -Dfile.encoding=UTF8 ${surefire.security.argline} ${surefire.coverage.argline}
</argLine>
<skipTests>${skip.tests}</skipTests>
<trimStackTrace>false</trimStackTrace>
</configuration>
<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2022 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
Expand Down Expand Up @@ -48,6 +48,8 @@ public ConstructorInjectedProvider(@Context final Providers providers) {
this.providers = providers;
}

public ConstructorInjectedProvider() {}

@Override
public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
final MediaType mediaType) {
Expand Down
Loading

0 comments on commit 25654bb

Please sign in to comment.