Skip to content

Commit

Permalink
Add 'configuration.service.imp' to define which 'ConfigurationService…
Browse files Browse the repository at this point in the history
…s' implementation should be used if multiple on classpath...

... the default if both are on the classpath is the one from jackson
  • Loading branch information
derTobsch committed Dec 10, 2024
1 parent fc66a1f commit d837c1c
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 d837c1c

Please sign in to comment.