forked from langchain4j/langchain4j
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor ServiceHelper and add tests and docs. (langchain4j#452)
- Loading branch information
Showing
6 changed files
with
107 additions
and
19 deletions.
There are no files selected for viewing
77 changes: 58 additions & 19 deletions
77
langchain4j-core/src/main/java/dev/langchain4j/spi/ServiceHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,76 @@ | ||
package dev.langchain4j.spi; | ||
|
||
import java.util.*; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.ServiceLoader; | ||
|
||
/** | ||
* Utility wrapper around {@code ServiceLoader.load()}. | ||
*/ | ||
public class ServiceHelper { | ||
/** | ||
* Utility class, no public constructor. | ||
*/ | ||
private ServiceHelper() { | ||
} | ||
|
||
/** | ||
* Load all the services of a given type. | ||
* | ||
* @param clazz the type of service | ||
* @param <T> the type of service | ||
* @return the list of services, empty if none | ||
*/ | ||
public static <T> Collection<T> loadFactories(Class<T> clazz) { | ||
return loadFactories(clazz, null); | ||
} | ||
|
||
public static <T> Collection<T> loadFactories(Class<T> clazz, ClassLoader classLoader) { | ||
List<T> list = new ArrayList<>(); | ||
ServiceLoader<T> factories; | ||
/** | ||
* Load all the services of a given type. | ||
* | ||
* <p>Utility mechanism around {@code ServiceLoader.load()}</p> | ||
* | ||
* <ul> | ||
* <li>If classloader is {@code null}, will try {@code ServiceLoader.load(clazz)}</li> | ||
* <li>If classloader is not {@code null}, will try {@code ServiceLoader.load(clazz, classloader)}</li> | ||
* </ul> | ||
* | ||
* <p>If the above return nothing, will fall back to {@code ServiceLoader.load(clazz, $this class loader$)}</p> | ||
* | ||
* @param clazz the type of service | ||
* @param classLoader the classloader to use, may be null | ||
* @param <T> the type of service | ||
* @return the list of services, empty if none | ||
*/ | ||
public static <T> Collection<T> loadFactories(Class<T> clazz, /* @Nullable */ ClassLoader classLoader) { | ||
List<T> result; | ||
if (classLoader != null) { | ||
factories = ServiceLoader.load(clazz, classLoader); | ||
result = loadAll(ServiceLoader.load(clazz, classLoader)); | ||
} else { | ||
// this is equivalent to: | ||
// ServiceLoader.load(clazz, TCCL); | ||
factories = ServiceLoader.load(clazz); | ||
result = loadAll(ServiceLoader.load(clazz)); | ||
} | ||
if (factories.iterator().hasNext()) { | ||
factories.iterator().forEachRemaining(list::add); | ||
return list; | ||
} else { | ||
// By default ServiceLoader.load uses the TCCL, this may not be enough in environment dealing with | ||
// classloaders differently such as OSGi. So we should try to use the classloader having loaded this | ||
if (result.isEmpty()) { | ||
// By default, ServiceLoader.load uses the TCCL, this may not be enough in environment dealing with | ||
// classloaders differently such as OSGi. So we should try to use the classloader having loaded this | ||
// class. In OSGi it would be the bundle exposing vert.x and so have access to all its classes. | ||
factories = ServiceLoader.load(clazz, ServiceHelper.class.getClassLoader()); | ||
if (factories.iterator().hasNext()) { | ||
factories.iterator().forEachRemaining(list::add); | ||
return list; | ||
} else { | ||
return Collections.emptyList(); | ||
} | ||
result = loadAll(ServiceLoader.load(clazz, ServiceHelper.class.getClassLoader())); | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* Load all the services from a ServiceLoader. | ||
* | ||
* @param loader the loader | ||
* @param <T> the type of service | ||
* @return the list of services, empty if none | ||
*/ | ||
private static <T> List<T> loadAll(ServiceLoader<T> loader) { | ||
List<T> list = new ArrayList<>(); | ||
loader.iterator().forEachRemaining(list::add); | ||
return list; | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
langchain4j-core/src/test/java/dev/langchain4j/spi/ExampleService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package dev.langchain4j.spi; | ||
|
||
public interface ExampleService { | ||
String getGreeting(); | ||
} |
8 changes: 8 additions & 0 deletions
8
langchain4j-core/src/test/java/dev/langchain4j/spi/ExampleServiceGoodbye.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package dev.langchain4j.spi; | ||
|
||
public class ExampleServiceGoodbye implements ExampleService{ | ||
@Override | ||
public String getGreeting() { | ||
return "Goodbye"; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
langchain4j-core/src/test/java/dev/langchain4j/spi/ExampleServiceHello.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package dev.langchain4j.spi; | ||
|
||
public class ExampleServiceHello implements ExampleService{ | ||
@Override | ||
public String getGreeting() { | ||
return "Hello"; | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
langchain4j-core/src/test/java/dev/langchain4j/spi/ServiceHelperTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package dev.langchain4j.spi; | ||
|
||
import org.assertj.core.api.WithAssertions; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.Collection; | ||
|
||
class ServiceHelperTest implements WithAssertions { | ||
public void assertServices(Collection<ExampleService> services) { | ||
assertThat(services).extracting(ExampleService::getGreeting) | ||
.containsExactlyInAnyOrder("Hello", "Goodbye"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
interface NotAService { | ||
int unused(); | ||
} | ||
|
||
@Test | ||
public void test_loadFactories() { | ||
assertServices(ServiceHelper.loadFactories(ExampleService.class)); | ||
assertServices(ServiceHelper.loadFactories(ExampleService.class, ServiceHelperTest.class.getClassLoader())); | ||
|
||
assertThat(ServiceHelper.loadFactories(NotAService.class)).isEmpty(); | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
langchain4j-core/src/test/resources/META-INF/services/dev.langchain4j.spi.ExampleService
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dev.langchain4j.spi.ExampleServiceHello | ||
dev.langchain4j.spi.ExampleServiceGoodbye |