Skip to content

Commit

Permalink
CARROT-1276: Allow multiple classloader SPI resolution in language co…
Browse files Browse the repository at this point in the history
…mponents loader
  • Loading branch information
dweiss committed Oct 1, 2020
1 parent 8ebb140 commit d66d8d9
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 13 deletions.
Expand Up @@ -19,9 +19,11 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand All @@ -30,19 +32,16 @@
import org.carrot2.util.ResourceLookup;

public final class LanguageComponentsLoader {
private ClassLoader spiClassLoader;
private Set<String> languageRestrictions;
private Function<LanguageComponentsProvider, ResourceLookup> resourceLookupModifier;
private ClusteringAlgorithm[] algorithmRestriction;

public LoadedLanguages load() throws IOException {
if (spiClassLoader == null) {
spiClassLoader = defaultSpiClassloader();
}

Map<String, List<LanguageComponentsProvider>> languageProviders =
loadProvidersFromSpi(spiClassLoader);
return load(loadProvidersFromSpi(defaultSpiClassloader()));
}

public LoadedLanguages load(Map<String, List<LanguageComponentsProvider>> languageProviders)
throws IOException {
sanityCheck(languageProviders);

// Apply restrictions.
Expand Down Expand Up @@ -143,14 +142,30 @@ private ClassLoader defaultSpiClassloader() {
return getClass().getClassLoader();
}

private Map<String, List<LanguageComponentsProvider>> loadProvidersFromSpi(
ClassLoader spiClassLoader) {
public static Map<String, List<LanguageComponentsProvider>> loadProvidersFromSpi(
ClassLoader... classloaders) {
Map<String, List<LanguageComponentsProvider>> providers = new TreeMap<>();

for (LanguageComponentsProvider provider :
ServiceLoader.load(LanguageComponentsProvider.class, spiClassLoader)) {
for (String language : provider.languages()) {
providers.computeIfAbsent(language, key -> new ArrayList<>()).add(provider);
BiPredicate<LanguageComponentsProvider, LanguageComponentsProvider> sameProvider =
(p1, p2) -> {
return Objects.equals(p1.name(), p2.name())
&& Objects.equals(p1.getClass(), p2.getClass())
&& Objects.equals(p1.componentTypes(), p2.componentTypes())
&& Objects.equals(p1.languages(), p2.languages());
};

for (ClassLoader classLoader : classloaders) {
for (LanguageComponentsProvider candidate :
ServiceLoader.load(LanguageComponentsProvider.class, classLoader)) {
for (String language : candidate.languages()) {
List<LanguageComponentsProvider> existingProviders =
providers.computeIfAbsent(language, key -> new ArrayList<>());

if (existingProviders.stream()
.noneMatch(existing -> sameProvider.test(existing, candidate))) {
existingProviders.add(candidate);
}
}
}
}

Expand Down
@@ -0,0 +1,37 @@
/*
* Carrot2 project.
*
* Copyright (C) 2002-2020, Dawid Weiss, Stanisław Osiński.
* All rights reserved.
*
* Refer to the full license file "carrot2.LICENSE"
* in the root folder of the repository checkout or at:
* https://www.carrot2.org/carrot2.LICENSE
*/
package org.carrot2.language;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.assertj.core.api.Assertions;
import org.carrot2.TestBase;
import org.junit.Test;

public class LanguageComponentsLoaderTest extends TestBase {
@Test
public void testMultipleSpiDeduplicatesProviders() {
Map<String, List<LanguageComponentsProvider>> langs =
LanguageComponentsLoader.loadProvidersFromSpi(
getClass().getClassLoader(), getClass().getClassLoader());

for (List<LanguageComponentsProvider> perLangProviders : langs.values()) {
perLangProviders.stream()
.collect(Collectors.groupingBy(LanguageComponentsProvider::getClass))
.values()
.forEach(
providersByClass -> {
Assertions.assertThat(providersByClass).hasSize(1);
});
}
}
}

0 comments on commit d66d8d9

Please sign in to comment.