Skip to content

Commit 0a7e6ec

Browse files
committed
extract ClassIndexLoaderUtils out of ServiceLoaderUtils and add tests
1 parent b98f220 commit 0a7e6ec

9 files changed

+238
-60
lines changed

modules/typed-ids/src/main/java/org/framefork/typedIds/TypedIdsRegistry.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.framefork.typedIds;
22

33
import org.framefork.typedIds.bigint.ObjectBigIntId;
4+
import org.framefork.typedIds.common.ClassIndexLoaderUtils;
45
import org.framefork.typedIds.common.LazyValue;
5-
import org.framefork.typedIds.common.ServiceLoaderUtils;
66
import org.framefork.typedIds.uuid.ObjectUuid;
77

88
import java.util.List;
@@ -21,10 +21,10 @@ private TypedIdsRegistry()
2121
}
2222

2323
private final static LazyValue<List<Class<? extends ObjectBigIntId>>> objectBigIntIdClasses = new LazyValue<>(() ->
24-
ServiceLoaderUtils.getIndexedSubclassesFor(ObjectBigIntId.class));
24+
ClassIndexLoaderUtils.getIndexedSubclassesFor(ObjectBigIntId.class));
2525

2626
private final static LazyValue<List<Class<? extends ObjectUuid>>> objectUuidClasses = new LazyValue<>(() ->
27-
ServiceLoaderUtils.getIndexedSubclassesFor(ObjectUuid.class));
27+
ClassIndexLoaderUtils.getIndexedSubclassesFor(ObjectUuid.class));
2828

2929
public static List<Class<? extends ObjectBigIntId>> getObjectBigIntIdClasses()
3030
{
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package org.framefork.typedIds.common;
2+
3+
import org.jetbrains.annotations.ApiStatus;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
7+
import java.io.BufferedReader;
8+
import java.io.FileNotFoundException;
9+
import java.io.IOException;
10+
import java.io.InputStreamReader;
11+
import java.net.URL;
12+
import java.nio.charset.StandardCharsets;
13+
import java.util.Collection;
14+
import java.util.Comparator;
15+
import java.util.Enumeration;
16+
import java.util.HashSet;
17+
import java.util.List;
18+
import java.util.Objects;
19+
import java.util.Set;
20+
21+
/**
22+
* The <code>typed-ids-index-java-classes-processor</code> names index files without the dollar sign for inner classes.
23+
*/
24+
@ApiStatus.Internal
25+
public final class ClassIndexLoaderUtils
26+
{
27+
28+
private static final String SUBCLASS_INDEX_PREFIX = "META-INF/services/";
29+
30+
private static final Logger log = LoggerFactory.getLogger(ClassIndexLoaderUtils.class);
31+
32+
private ClassIndexLoaderUtils()
33+
{
34+
}
35+
36+
@SuppressWarnings("unchecked")
37+
public static <T> List<Class<? extends T>> getIndexedSubclassesFor(final Class<T> superClass)
38+
{
39+
Collection<String> classNames = readIndexFile(SUBCLASS_INDEX_PREFIX + superClass.getCanonicalName());
40+
41+
var classes = classNames.stream()
42+
.map(ReflectionHacks::classForName)
43+
.filter(Objects::nonNull)
44+
.map(klass -> superClass.isAssignableFrom(klass) ? (Class<? extends T>) klass : null)
45+
.filter(Objects::nonNull)
46+
.sorted(Comparator.comparing(Class::getName))
47+
.toList();
48+
49+
return (List<Class<? extends T>>) (List<?>) classes;
50+
}
51+
52+
private static Collection<String> readIndexFile(final String resourceFile)
53+
{
54+
try {
55+
Set<String> entries = new HashSet<>();
56+
57+
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(resourceFile);
58+
while (resources.hasMoreElements()) {
59+
URL resource = resources.nextElement();
60+
61+
try (var reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) {
62+
reader.lines()
63+
.filter(line -> line != null && !line.isBlank())
64+
.forEach(entries::add);
65+
66+
} catch (FileNotFoundException e) {
67+
log.warn("Failed to read resource '{}': {}", resource, e.getMessage(), e);
68+
}
69+
}
70+
71+
return entries;
72+
73+
} catch (IOException e) {
74+
throw new IllegalStateException("ClassIndex: Cannot read class index", e);
75+
}
76+
}
77+
78+
}

modules/typed-ids/src/main/java/org/framefork/typedIds/common/ServiceLoaderUtils.java

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,13 @@
55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
77

8-
import java.io.BufferedReader;
9-
import java.io.FileNotFoundException;
10-
import java.io.IOException;
11-
import java.io.InputStreamReader;
12-
import java.net.URL;
13-
import java.nio.charset.StandardCharsets;
14-
import java.util.Collection;
15-
import java.util.Comparator;
16-
import java.util.Enumeration;
17-
import java.util.HashSet;
188
import java.util.Iterator;
19-
import java.util.List;
20-
import java.util.Objects;
219
import java.util.ServiceLoader;
22-
import java.util.Set;
2310

2411
@ApiStatus.Internal
2512
public final class ServiceLoaderUtils
2613
{
2714

28-
private static final String SUBCLASS_INDEX_PREFIX = "META-INF/services/";
29-
3015
private static final Logger log = LoggerFactory.getLogger(ServiceLoaderUtils.class);
3116

3217
private ServiceLoaderUtils()
@@ -48,46 +33,4 @@ public static <T> T getSingleOrNull(final Class<T> type)
4833
return first;
4934
}
5035

51-
@SuppressWarnings("unchecked")
52-
public static <T> List<Class<? extends T>> getIndexedSubclassesFor(final Class<T> superClass)
53-
{
54-
Collection<String> classNames = readIndexFile(SUBCLASS_INDEX_PREFIX + superClass.getCanonicalName());
55-
56-
var classes = classNames.stream()
57-
.map(ReflectionHacks::classForName)
58-
.filter(Objects::nonNull)
59-
.map(klass -> superClass.isAssignableFrom(klass) ? (Class<? extends T>) klass : null)
60-
.filter(Objects::nonNull)
61-
.sorted(Comparator.comparing(Class::getName))
62-
.toList();
63-
64-
return (List<Class<? extends T>>) (List<?>) classes;
65-
}
66-
67-
private static Collection<String> readIndexFile(final String resourceFile)
68-
{
69-
try {
70-
Set<String> entries = new HashSet<>();
71-
72-
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(resourceFile);
73-
while (resources.hasMoreElements()) {
74-
URL resource = resources.nextElement();
75-
76-
try (var reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) {
77-
reader.lines()
78-
.filter(line -> line != null && !line.isBlank())
79-
.forEach(entries::add);
80-
81-
} catch (FileNotFoundException e) {
82-
log.warn("Failed to read resource '{}': {}", resource, e.getMessage(), e);
83-
}
84-
}
85-
86-
return entries;
87-
88-
} catch (IOException e) {
89-
throw new IllegalStateException("ClassIndex: Cannot read class index", e);
90-
}
91-
}
92-
9336
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.framefork.typedIds.common;
2+
3+
import org.junit.jupiter.api.Nested;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.util.List;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
final class ClassIndexLoaderUtilsTest
11+
{
12+
13+
@Nested
14+
final class GetIndexedSubclassesFor
15+
{
16+
17+
@Test
18+
public void shouldReturnEmptyWhenNoIndexFound()
19+
{
20+
List<Class<? extends TestNotIndexedService>> result = ClassIndexLoaderUtils.getIndexedSubclassesFor(TestNotIndexedService.class);
21+
22+
assertThat(result).hasSize(0);
23+
}
24+
25+
@Test
26+
public void shouldReturnSingleWhenSingleServiceFound()
27+
{
28+
List<Class<? extends TestIndexedSingleImplClass>> result = ClassIndexLoaderUtils.getIndexedSubclassesFor(TestIndexedSingleImplClass.class);
29+
30+
assertThat(result)
31+
.hasSize(1)
32+
.containsExactly(TestIndexedSingleImplSubclass1.class);
33+
}
34+
35+
@Test
36+
public void shouldReturnSortedListByClassName()
37+
{
38+
List<Class<? extends TestIndexedMultipleImplsClass>> result = ClassIndexLoaderUtils.getIndexedSubclassesFor(TestIndexedMultipleImplsClass.class);
39+
40+
assertThat(result)
41+
.hasSize(2)
42+
.containsExactly(
43+
TestIndexedMultipleImplsSubclass1.class,
44+
TestIndexedMultipleImplsSubclass2.class
45+
);
46+
}
47+
48+
}
49+
50+
public interface TestNotIndexedService
51+
{
52+
53+
}
54+
55+
public static class TestIndexedSingleImplClass
56+
{
57+
58+
}
59+
60+
public static class TestIndexedSingleImplSubclass1 extends TestIndexedSingleImplClass
61+
{
62+
63+
}
64+
65+
public static class TestIndexedMultipleImplsClass
66+
{
67+
68+
}
69+
70+
public static class TestIndexedMultipleImplsSubclass1 extends TestIndexedMultipleImplsClass
71+
{
72+
73+
}
74+
75+
public static class TestIndexedMultipleImplsSubclass2 extends TestIndexedMultipleImplsClass
76+
{
77+
78+
}
79+
80+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.framefork.typedIds.common;
2+
3+
import org.junit.jupiter.api.Nested;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.assertj.core.api.Assertions.assertThat;
7+
8+
final class ServiceLoaderUtilsTest
9+
{
10+
11+
@Nested
12+
final class GetSingleOrNull
13+
{
14+
15+
@Test
16+
public void shouldReturnNullWhenNoServicesFound()
17+
{
18+
TestNotIndexedService result = ServiceLoaderUtils.getSingleOrNull(TestNotIndexedService.class);
19+
20+
assertThat(result).isNull();
21+
}
22+
23+
@Test
24+
public void shouldReturnNullWhenMultipleServicesFound()
25+
{
26+
var result = ServiceLoaderUtils.getSingleOrNull(TestIndexedMultipleImplsClass.class);
27+
28+
assertThat(result).isNull();
29+
}
30+
31+
@Test
32+
public void shouldReturnWhenSingleServiceFound()
33+
{
34+
var result = ServiceLoaderUtils.getSingleOrNull(TestIndexedSingleImplClass.class);
35+
36+
assertThat(result).isInstanceOf(TestIndexedSingleImplSubclass1.class);
37+
}
38+
39+
}
40+
41+
public interface TestNotIndexedService
42+
{
43+
44+
}
45+
46+
public static class TestIndexedSingleImplClass
47+
{
48+
49+
}
50+
51+
public static class TestIndexedSingleImplSubclass1 extends TestIndexedSingleImplClass
52+
{
53+
54+
}
55+
56+
public static class TestIndexedMultipleImplsClass
57+
{
58+
59+
}
60+
61+
public static class TestIndexedMultipleImplsSubclass1 extends TestIndexedMultipleImplsClass
62+
{
63+
64+
}
65+
66+
public static class TestIndexedMultipleImplsSubclass2 extends TestIndexedMultipleImplsClass
67+
{
68+
69+
}
70+
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.framefork.typedIds.common.ClassIndexLoaderUtilsTest$TestIndexedMultipleImplsSubclass1
2+
org.framefork.typedIds.common.ClassIndexLoaderUtilsTest$TestIndexedMultipleImplsSubclass2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.framefork.typedIds.common.ClassIndexLoaderUtilsTest$TestIndexedSingleImplSubclass1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.framefork.typedIds.common.ServiceLoaderUtilsTest$TestIndexedMultipleImplsSubclass1
2+
org.framefork.typedIds.common.ServiceLoaderUtilsTest$TestIndexedMultipleImplsSubclass2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.framefork.typedIds.common.ServiceLoaderUtilsTest$TestIndexedSingleImplSubclass1

0 commit comments

Comments
 (0)