-
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #311 from focus-shift/310-service-loader
Introduce service loader to retrieve different configuration services
- Loading branch information
Showing
11 changed files
with
169 additions
and
76 deletions.
There are no files selected for viewing
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
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
39 changes: 21 additions & 18 deletions
39
...re/src/main/java/de/focus_shift/jollyday/core/datasource/ConfigurationServiceManager.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,35 +1,38 @@ | ||
package de.focus_shift.jollyday.core.datasource; | ||
|
||
import de.focus_shift.jollyday.core.ManagerParameter; | ||
import de.focus_shift.jollyday.core.spi.ConfigurationService; | ||
import de.focus_shift.jollyday.core.util.ClassLoadingUtil; | ||
import de.focus_shift.jollyday.core.support.LazyServiceLoaderCache; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* This manager is responsible for instantiating the configured configuration datasource | ||
* This manager is responsible for instantiating | ||
* the provided implementation of the {@link ConfigurationService} | ||
* which is used to access the holiday data. | ||
*/ | ||
public class ConfigurationServiceManager { | ||
|
||
private final ClassLoadingUtil classLoadingUtil = new ClassLoadingUtil(); | ||
private final LazyServiceLoaderCache<ConfigurationService> configurationServiceCache; | ||
|
||
public ConfigurationService getConfigurationService(ManagerParameter parameter) { | ||
validateConfiguration(parameter); | ||
final String dataSourceClassName = parameter.getProperty(ManagerParameter.CONFIGURATION_DATASOURCE_IMPL_CLASS); | ||
return instantiateDataSource(dataSourceClassName); | ||
public ConfigurationServiceManager(LazyServiceLoaderCache<ConfigurationService> configurationServiceCache) { | ||
this.configurationServiceCache = configurationServiceCache; | ||
} | ||
|
||
private ConfigurationService instantiateDataSource(String dataSourceClassName) { | ||
try { | ||
final Class<?> dataSourceClass = classLoadingUtil.loadClass(dataSourceClassName); | ||
return (ConfigurationService) dataSourceClass.getDeclaredConstructor().newInstance(); | ||
} catch (Exception e) { | ||
throw new IllegalStateException("Cannot instantiate datasource instance of " + dataSourceClassName, e); | ||
} | ||
public ConfigurationService getConfigurationService() { | ||
return instantiateDataSource(); | ||
} | ||
|
||
private void validateConfiguration(ManagerParameter parameter) { | ||
if (parameter.getProperty(ManagerParameter.CONFIGURATION_DATASOURCE_IMPL_CLASS) == null) { | ||
throw new IllegalStateException("Missing holiday configuration datasource implementation class under config key " + ManagerParameter.CONFIGURATION_DATASOURCE_IMPL_CLASS); | ||
private ConfigurationService instantiateDataSource() { | ||
|
||
final List<ConfigurationService> services = configurationServiceCache.getServices(); | ||
|
||
if (services.size() > 1) { | ||
throw new IllegalStateException("Cannot instantiate datasource instance because there are two or more implementations available " + services); | ||
} | ||
if (services.isEmpty()) { | ||
throw new IllegalStateException("Cannot instantiate datasource instance because there is no implementations"); | ||
} | ||
|
||
return services.get(0); | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
jollyday-core/src/main/java/de/focus_shift/jollyday/core/support/LazyServiceLoaderCache.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,44 @@ | ||
package de.focus_shift.jollyday.core.support; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.List; | ||
import java.util.ServiceConfigurationError; | ||
import java.util.ServiceLoader; | ||
import java.util.concurrent.CopyOnWriteArrayList; | ||
|
||
import static java.lang.String.format; | ||
|
||
public class LazyServiceLoaderCache<S> { | ||
private static final Logger LOG = LoggerFactory.getLogger(LazyServiceLoaderCache.class.getName()); | ||
|
||
private final Class<S> clz; | ||
private List<S> services; | ||
|
||
public LazyServiceLoaderCache(Class<S> clz) { | ||
this.clz = clz; | ||
} | ||
|
||
public List<S> getServices() { | ||
if (services == null) { | ||
loadServices(); | ||
} | ||
return services; | ||
} | ||
|
||
private synchronized void loadServices() { | ||
services = new CopyOnWriteArrayList<>(); | ||
try { | ||
for (S s : ServiceLoader.load(clz)) { | ||
services.add(s); | ||
} | ||
} catch (ServiceConfigurationError serviceConfigurationError) { | ||
final String message = format("Cannot load services of type [%s].%n %s", | ||
clz.getName(), | ||
serviceConfigurationError.getMessage() | ||
); | ||
LOG.warn(message); | ||
} | ||
} | ||
} |
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
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
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
87 changes: 87 additions & 0 deletions
87
...rc/test/java/de/focus_shift/jollyday/core/datasource/ConfigurationServiceManagerTest.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,87 @@ | ||
package de.focus_shift.jollyday.core.datasource; | ||
|
||
import de.focus_shift.jollyday.core.ManagerParameter; | ||
import de.focus_shift.jollyday.core.spi.Configuration; | ||
import de.focus_shift.jollyday.core.spi.ConfigurationService; | ||
import de.focus_shift.jollyday.core.spi.Holidays; | ||
import de.focus_shift.jollyday.core.support.LazyServiceLoaderCache; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
import java.util.List; | ||
import java.util.stream.Stream; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.Mockito.when; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class ConfigurationServiceManagerTest { | ||
|
||
@Mock | ||
private LazyServiceLoaderCache<ConfigurationService> configurationServiceCache; | ||
|
||
private ConfigurationServiceManager sut; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
sut = new ConfigurationServiceManager(configurationServiceCache); | ||
} | ||
|
||
@Test | ||
void ensuresToThrowExceptionIfNoImplementationIsAvailable() { | ||
|
||
when(configurationServiceCache.getServices()).thenReturn(List.of()); | ||
|
||
final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> sut.getConfigurationService()); | ||
assertThat(exception.getMessage()).contains("Cannot instantiate datasource instance because there is no implementations"); | ||
} | ||
|
||
@Test | ||
void ensuresToProvideConfigurationServiceIfExactlyOneIsAvailable() { | ||
when(configurationServiceCache.getServices()).thenReturn(List.of(new MockConfigurationService())); | ||
|
||
final ConfigurationService configurationService = sut.getConfigurationService(); | ||
assertThat(configurationService).isInstanceOf(MockConfigurationService.class); | ||
} | ||
|
||
@Test | ||
void ensuresToThrowExceptionIfMultipleImplementationsAreAvailable() { | ||
|
||
when(configurationServiceCache.getServices()).thenReturn(List.of(new MockConfigurationService(), new MockConfigurationService())); | ||
|
||
final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> sut.getConfigurationService()); | ||
assertThat(exception.getMessage()).contains("Cannot instantiate datasource instance because there are two or more implementations available"); | ||
} | ||
|
||
private static class MockConfigurationService implements ConfigurationService { | ||
|
||
@Override | ||
public Configuration getConfiguration(ManagerParameter parameter) { | ||
return new Configuration() { | ||
@Override | ||
public Holidays holidays() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Stream<Configuration> subConfigurations() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String hierarchy() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String description() { | ||
return null; | ||
} | ||
}; | ||
} | ||
} | ||
} |
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,8 +1,17 @@ | ||
import de.focus_shift.jollyday.core.spi.ConfigurationService; | ||
import de.focus_shift.jollyday.jaxb.JaxbConfigurationService; | ||
|
||
module de.focus_shift.jollyday.jaxb { | ||
|
||
opens de.focus_shift.jollyday.jaxb.mapping to jakarta.xml.bind; | ||
|
||
requires java.xml; | ||
requires jakarta.xml.bind; | ||
requires org.slf4j; | ||
requires org.threeten.extra; | ||
|
||
requires de.focus_shift.jollyday.core; | ||
|
||
exports de.focus_shift.jollyday.jaxb to de.focus_shift.jollyday.core; | ||
provides ConfigurationService | ||
with JaxbConfigurationService; | ||
} |
1 change: 1 addition & 0 deletions
1
...rc/main/resources/META-INF/services/de.focus_shift.jollyday.core.spi.ConfigurationService
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 @@ | ||
de.focus_shift.jollyday.jaxb.JaxbConfigurationService |
49 changes: 0 additions & 49 deletions
49
...c/test/java/de/focus_shift/jollyday/tests/datasource/ConfigurationServiceManagerTest.java
This file was deleted.
Oops, something went wrong.