Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'configuration.service.imp' to define which 'ConfigurationService… #695

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
};
}
}
}
Loading