Skip to content

Commit

Permalink
Merge pull request #695 from focus-shift/694-configure-configuration-…
Browse files Browse the repository at this point in the history
…service-implementation

Add 'configuration.service.imp' to define which 'ConfigurationService…
  • Loading branch information
derTobsch authored Dec 19, 2024
2 parents e3382d1 + d837c1c commit 788e162
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 19 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@ If you already use one of these libraries in your project than just use the spec
The configuration property name starts with `parser.impl` and finishes with the XML class name.
The value is the parser implementation class name which implements the `HolidayParser` interface.


**Configuration Service implementation**
A configuration service implementation is used to define which xml unmarshalling implementation should be used

```properties
configuration.service.impl = de.focus_shift.jollyday.jackson.JacksonConfigurationService
```

The configuration property `configuration.service.impl` contains the class name as string.
The value is the configuration service implementation class name which implements the `ConfigurationService` interface.

Values are:
* `de.focus_shift.jollyday.jackson.JacksonConfigurationService` (default)
* `de.focus_shift.jollyday.jaxb.JaxbConfigurationService`

</details>

### Examples
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ private static HolidayManager createManager(final ManagerParameter parameter) {
CONFIGURATION_MANAGER_PROVIDER.mergeConfigurationProperties(parameter);

final String managerImplClassName = readManagerImplClassName(parameter);
final HolidayManagerValueHandler holidayManagerValueHandler = new HolidayManagerValueHandler(parameter, managerImplClassName, configurationServiceManager);
final String configurationServiceImplClassName = readConfigurationServiceImplClassName(parameter);
final HolidayManagerValueHandler holidayManagerValueHandler = new HolidayManagerValueHandler(parameter, managerImplClassName, configurationServiceImplClassName, configurationServiceManager);
if (isManagerCachingEnabled()) {
return HOLIDAY_MANAGER_CACHE.get(holidayManagerValueHandler);
} else {
Expand All @@ -136,6 +137,20 @@ private static String readManagerImplClassName(final ManagerParameter parameter)
return className;
}

/**
* Reads the managers implementation class from the properties config file.
*
* @param parameter the parameter to read the manager implementation class from
* @return the manager implementation class name
*/
private static String readConfigurationServiceImplClassName(final ManagerParameter parameter) {
final String className = parameter.getConfigurationServiceImplClassName();
if (className == null) {
throw new IllegalStateException("Missing configuration '" + ManagerParameter.CONFIGURATION_SERVICE_IMPL + "'. Cannot create configuration service.");
}
return className;
}

/**
* If true, instantiated managers will be cached. If false every call to
* getInstance will create new manager. True by default.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ class HolidayManagerValueHandler implements Cache.ValueHandler<HolidayManager> {

private final ManagerParameter parameter;
private final String managerImplClassName;
private final String configurationServiceImplClassName;
private final ConfigurationServiceManager configurationServiceManager;

HolidayManagerValueHandler(final ManagerParameter parameter, final String managerImplClassName, final ConfigurationServiceManager configurationServiceManager) {
HolidayManagerValueHandler(final ManagerParameter parameter,
final String managerImplClassName,
final String configurationServiceImplClassName,
final ConfigurationServiceManager configurationServiceManager) {
this.parameter = parameter;
this.managerImplClassName = managerImplClassName;
this.configurationServiceImplClassName = configurationServiceImplClassName;
this.configurationServiceManager = configurationServiceManager;
}

Expand All @@ -29,7 +34,7 @@ public String getKey() {
public HolidayManager createValue() {
final HolidayManager manager = instantiateManagerImpl(managerImplClassName);

final ConfigurationService configurationService = configurationServiceManager.getConfigurationService();
final ConfigurationService configurationService = configurationServiceManager.getConfigurationService(configurationServiceImplClassName);
manager.setConfigurationService(configurationService);

manager.init(parameter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public interface ManagerParameter {

String MANAGER_IMPL_CLASS_PREFIX = "manager.impl";

String CONFIGURATION_SERVICE_IMPL = "configuration.service.impl";

void mergeProperties(final Properties properties);

String getProperty(final String key);
Expand All @@ -15,6 +17,8 @@ public interface ManagerParameter {

String getManagerImplClassName();

String getConfigurationServiceImplClassName();

String createCacheKey();

String getDisplayName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,25 @@ public ConfigurationServiceManager(LazyServiceLoaderCache<ConfigurationService>
this.configurationServiceCache = configurationServiceCache;
}

public ConfigurationService getConfigurationService() {
return instantiateDataSource();
public ConfigurationService getConfigurationService(final String configurationServiceImplClassName) {
return instantiateDataSource(configurationServiceImplClassName);
}

private ConfigurationService instantiateDataSource() {
private ConfigurationService instantiateDataSource(final String configurationServiceImplClassName) {

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.size() == 1) {
return services.get(0);
}
if (services.isEmpty()) {
throw new IllegalStateException("Cannot instantiate datasource instance because there is no implementations");

if (services.size() > 1) {
return services.stream()
.filter(configurationService -> configurationServiceImplClassName.equals(configurationService.getClass().getName()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Cannot instantiate datasource instance because there are two or more implementations available " + configurationServiceImplClassName));
}

return services.get(0);
throw new IllegalStateException("Cannot instantiate datasource instance because there is no implementations");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ public void setProperty(String key, String value) {
public String getManagerImplClassName() {
return getProperty(MANAGER_IMPL_CLASS_PREFIX);
}

@Override
public String getConfigurationServiceImplClassName() {
return getProperty(CONFIGURATION_SERVICE_IMPL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
* Service to provide the serialised configuration from XML files.
*/
public interface ConfigurationService {

Configuration getConfiguration(ManagerParameter parameter);
}
2 changes: 2 additions & 0 deletions jollyday-core/src/main/resources/jollyday.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ parser.impl.de.focus_shift.jollyday.core.spi.FixedWeekdayBetweenFixed = de.fo
parser.impl.de.focus_shift.jollyday.core.spi.FixedWeekdayRelativeToFixed = de.focus_shift.jollyday.core.parser.impl.FixedWeekdayRelativeToFixedParser
parser.impl.de.focus_shift.jollyday.core.spi.EthiopianOrthodoxHoliday = de.focus_shift.jollyday.core.parser.impl.EthiopianOrthodoxHolidayParser
parser.impl.de.focus_shift.jollyday.core.spi.RelativeToEasterSunday = de.focus_shift.jollyday.core.parser.impl.RelativeToEasterSundayParser

configuration.service.impl = de.focus_shift.jollyday.jackson.JacksonConfigurationService
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ class HolidayManagerValueHandlerTest {
@Test
void ensureToGetCorrectKey() {
final String managerImplClassName = "de.focus_shift.jollyday.core.impl.DefaultHolidayManager";
final String configurationServiceImplClassName = "de.focus_shift.jollyday.jackson.JacksonConfigurationService";
final ConfigurationServiceManager configurationServiceManager = mock(ConfigurationServiceManager.class);
final HolidayManagerValueHandler sut = new HolidayManagerValueHandler(create("de"), managerImplClassName, configurationServiceManager);
final HolidayManagerValueHandler sut = new HolidayManagerValueHandler(create("de"), managerImplClassName, configurationServiceImplClassName, configurationServiceManager);

final String key = sut.getKey();
assertThat(key).isEqualTo("de");
Expand All @@ -49,10 +50,10 @@ void ensureToCreateCorrectHolidayManagerAndReturnIt(final String managerImplClas
final ConfigurationService configurationService = mock(ConfigurationService.class);
final Configuration configuration = mock(Configuration.class);

when(configurationServiceManager.getConfigurationService()).thenReturn(configurationService);
when(configurationServiceManager.getConfigurationService(any(String.class))).thenReturn(configurationService);
when(configurationService.getConfiguration(any(ManagerParameter.class))).thenReturn(configuration);

final HolidayManagerValueHandler sut = new HolidayManagerValueHandler(create("de"), managerImplClass, configurationServiceManager);
final HolidayManagerValueHandler sut = new HolidayManagerValueHandler(create("de"), managerImplClass, "configurationServiceImplClassName", configurationServiceManager);

final HolidayManager holidayManager = sut.createValue();
assertThat(holidayManager).isInstanceOf(clazz);
Expand All @@ -62,7 +63,7 @@ void ensureToCreateCorrectHolidayManagerAndReturnIt(final String managerImplClas
void ensureToThrowIllegalStateExceptionIfHolidayManagerIsWrong() {

final String wrongManagerImplClassName = "de.focus_shift.jollyday.core.impl.WrongHolidayManager";
final HolidayManagerValueHandler sut = new HolidayManagerValueHandler(create("de"), wrongManagerImplClassName, mock(ConfigurationServiceManager.class));
final HolidayManagerValueHandler sut = new HolidayManagerValueHandler(create("de"), wrongManagerImplClassName, "configurationServiceImplClassName", mock(ConfigurationServiceManager.class));

assertThatThrownBy(sut::createValue)
.isInstanceOf(IllegalStateException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,37 @@ void ensuresToThrowExceptionIfNoImplementationIsAvailable() {

when(configurationServiceCache.getServices()).thenReturn(List.of());

final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> sut.getConfigurationService());
final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> sut.getConfigurationService("configurationServiceImplClassName"));
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();
final ConfigurationService configurationService = sut.getConfigurationService("configurationServiceImplClassName");
assertThat(configurationService).isInstanceOf(MockConfigurationService.class);
}

@Test
void ensuresToThrowExceptionIfMultipleImplementationsAreAvailable() {
void ensuresToThrowExceptionIfMultipleImplementationsAreAvailableButNoneOfThatAreConfigured() {

when(configurationServiceCache.getServices()).thenReturn(List.of(new MockConfigurationService(), new MockConfigurationService()));

final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> sut.getConfigurationService());
final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> sut.getConfigurationService("configurationServiceImplClassName"));
assertThat(exception.getMessage()).contains("Cannot instantiate datasource instance because there are two or more implementations available");
}

@Test
void ensuresToUseTheConfiguredConfigurationServiceIfMultipleAreOnTheClasspath() {

final MockConfigurationServiceOther mockConfigurationServiceOther = new MockConfigurationServiceOther();
when(configurationServiceCache.getServices()).thenReturn(List.of(new MockConfigurationService(), mockConfigurationServiceOther));

final ConfigurationService configurationService = sut.getConfigurationService(mockConfigurationServiceOther.getClass().getName());
assertThat(configurationService).isInstanceOf(MockConfigurationServiceOther.class);
}

private static class MockConfigurationService implements ConfigurationService {

@Override
Expand Down Expand Up @@ -84,4 +94,32 @@ public String description() {
};
}
}

private static class MockConfigurationServiceOther 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;
}
};
}
}
}

0 comments on commit 788e162

Please sign in to comment.