Fix EphemerisManager crashing on invalid configuration (#2949)

It has been reported in the past (and in the forum) that the EphemerisManagerImpl can't handle illegal configurations. Due to dependencies in other bundles this results in the whole automation component to be unavailable.

Signed-off-by: Jan N. Klug <github@klug.nrw>
pull/2955/head
J-N-K 2022-05-05 21:25:30 +02:00 committed by GitHub
parent 53a072d685
commit de1f850e10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 47 deletions

View File

@ -27,7 +27,6 @@ import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -124,8 +123,7 @@ public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvi
} }
private void sortByLabel(List<ParameterOption> parameterOptions) { private void sortByLabel(List<ParameterOption> parameterOptions) {
Collections.sort(parameterOptions, parameterOptions.sort(Comparator.comparing(ParameterOption::getLabel));
(ParameterOption po1, ParameterOption po2) -> po1.getLabel().compareTo(po2.getLabel()));
} }
@Activate @Activate
@ -136,29 +134,33 @@ public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvi
@Modified @Modified
protected void modified(Map<String, Object> config) { protected void modified(Map<String, Object> config) {
config.entrySet().stream().filter(e -> e.getKey().startsWith(CONFIG_DAYSET_PREFIX)).forEach(e -> { config.entrySet().stream().filter(e -> e.getKey().startsWith(CONFIG_DAYSET_PREFIX)).forEach(e -> {
String[] setNameParts = e.getKey().split("-"); try {
if (setNameParts.length > 1) { String[] setNameParts = e.getKey().split("-");
String setName = setNameParts[1]; if (setNameParts.length > 1) {
Object entry = e.getValue(); String setName = setNameParts[1];
if (entry instanceof String) { Object entry = e.getValue();
String value = entry.toString(); if (entry instanceof String) {
while (value.startsWith("[")) { String value = entry.toString();
value = value.substring(1); while (value.startsWith("[")) {
value = value.substring(1);
}
while (value.endsWith("]")) {
value = value.substring(0, value.length() - 1);
}
String[] setDefinition = value.split(",");
if (setDefinition.length > 0) {
addDayset(setName, List.of(setDefinition));
} else {
logger.warn("Erroneous day set definition {} : {}", e.getKey(), entry);
}
} else if (entry instanceof Iterable) {
addDayset(setName, (Iterable<?>) entry);
} }
while (value.endsWith("]")) { } else {
value = value.substring(0, value.length() - 1); logger.warn("Erroneous day set definition {}", e.getKey());
}
String[] setDefinition = value.split(",");
if (setDefinition.length > 0) {
addDayset(setName, List.of(setDefinition));
} else {
logger.warn("Erroneous dayset definition {} : {}", e.getKey(), entry);
}
} else if (entry instanceof Iterable) {
addDayset(setName, (Iterable<?>) entry);
} }
} else { } catch (IllegalArgumentException ex) {
logger.warn("Erroneous dayset definition {}", e.getKey()); logger.warn("Erroneous day set definition {}: {}", e.getKey(), ex.getMessage());
} }
}); });
@ -243,25 +245,17 @@ public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvi
LocalDate toDate = from.plusDays(span).toLocalDate(); LocalDate toDate = from.plusDays(span).toLocalDate();
Set<Holiday> days = holidayManager.getHolidays(fromDate, toDate, countryParameters.toArray(new String[0])); Set<Holiday> days = holidayManager.getHolidays(fromDate, toDate, countryParameters.toArray(new String[0]));
List<Holiday> sortedHolidays = days.stream().sorted(Comparator.comparing(Holiday::getDate)) return days.stream().sorted(Comparator.comparing(Holiday::getDate)).collect(Collectors.toList());
.collect(Collectors.toList());
return sortedHolidays;
} }
@Override @Override
public long getDaysUntil(ZonedDateTime from, String searchedHoliday) { public long getDaysUntil(ZonedDateTime from, String searchedHoliday) {
List<Holiday> sortedHolidays = getHolidays(from, 366, getHolidayManager(country)); return getDaysUntil(from, searchedHoliday, getHolidayManager(country));
Optional<Holiday> result = sortedHolidays.stream()
.filter(holiday -> searchedHoliday.equalsIgnoreCase(holiday.getPropertiesKey())).findFirst();
return result.isPresent() ? from.toLocalDate().until(result.get().getDate(), ChronoUnit.DAYS) : -1;
} }
@Override @Override
public long getDaysUntil(ZonedDateTime from, String searchedHoliday, URL resource) { public long getDaysUntil(ZonedDateTime from, String searchedHoliday, URL resource) {
List<Holiday> sortedHolidays = getHolidays(from, 366, getHolidayManager(resource)); return getDaysUntil(from, searchedHoliday, getHolidayManager(resource));
Optional<Holiday> result = sortedHolidays.stream()
.filter(holiday -> searchedHoliday.equalsIgnoreCase(holiday.getPropertiesKey())).findFirst();
return result.isPresent() ? from.toLocalDate().until(result.get().getDate(), ChronoUnit.DAYS) : -1;
} }
@Override @Override
@ -269,9 +263,16 @@ public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvi
return getDaysUntil(from, searchedHoliday, getUrl(filename)); return getDaysUntil(from, searchedHoliday, getUrl(filename));
} }
private long getDaysUntil(ZonedDateTime from, String searchedHoliday, HolidayManager holidayManager) {
List<Holiday> sortedHolidays = getHolidays(from, 366, holidayManager);
Optional<Holiday> result = sortedHolidays.stream()
.filter(holiday -> searchedHoliday.equalsIgnoreCase(holiday.getPropertiesKey())).findFirst();
return result.map(holiday -> from.toLocalDate().until(holiday.getDate(), ChronoUnit.DAYS)).orElse(-1L);
}
private @Nullable String getFirstBankHolidayKey(ZonedDateTime from, int span, HolidayManager holidayManager) { private @Nullable String getFirstBankHolidayKey(ZonedDateTime from, int span, HolidayManager holidayManager) {
Optional<Holiday> holiday = getHolidays(from, span, holidayManager).stream().findFirst(); Optional<Holiday> holiday = getHolidays(from, span, holidayManager).stream().findFirst();
return holiday.map(p -> p.getPropertiesKey()).orElse(null); return holiday.map(Holiday::getPropertiesKey).orElse(null);
} }
@Override @Override
@ -338,7 +339,9 @@ public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvi
private void addDayset(String setName, Iterable<?> values) { private void addDayset(String setName, Iterable<?> values) {
Set<DayOfWeek> dayset = new HashSet<>(); Set<DayOfWeek> dayset = new HashSet<>();
for (Object day : values) { for (Object day : values) {
dayset.add(DayOfWeek.valueOf(day.toString().trim().toUpperCase())); // fix illegal entries by stripping all non A-Z characters
String dayString = day.toString().toUpperCase().replaceAll("[^A-Z]", "");
dayset.add(DayOfWeek.valueOf(dayString));
} }
daysets.put(setName, dayset); daysets.put(setName, dayset);
} }
@ -346,7 +349,8 @@ public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvi
/** /**
* Parses each entry of a properties list loaded from 'jolliday/descriptions/country_descriptions.properties'. The * Parses each entry of a properties list loaded from 'jolliday/descriptions/country_descriptions.properties'. The
* file has been copied from * file has been copied from
* https://github.com/svendiedrichsen/jollyday/blob/master/src/main/resources/descriptions/country_descriptions.properties * <a href=
* "https://github.com/svendiedrichsen/jollyday/blob/master/src/main/resources/descriptions/country_descriptions.properties">https://github.com/svendiedrichsen/jollyday/blob/master/src/main/resources/descriptions/country_descriptions.properties</a>
* and contains values in the following format - some examples: * and contains values in the following format - some examples:
* *
* country.description.at = Austria * country.description.at = Austria
@ -358,7 +362,7 @@ public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvi
* *
* "at" and "au" are keys of countries * "at" and "au" are keys of countries
* "sa" and "tas" are keys of regions inside the country "au" * "sa" and "tas" are keys of regions inside the country "au"
* "ho" and "nh" are kess of cities or areas inside the region "tas" * "ho" and "nh" are keys of cities or areas inside the region "tas"
* *
* @param key key of the property will be parsed * @param key key of the property will be parsed
* @param value value of the property will be used as name * @param value value of the property will be used as name

View File

@ -67,6 +67,21 @@ public class EphemerisManagerImplOSGiTest extends JavaOSGiTest {
entry(CONFIG_COUNTRY, Locale.GERMANY.getCountry()))); entry(CONFIG_COUNTRY, Locale.GERMANY.getCountry())));
} }
@Test
public void testEphemerisManagerFixesIllegalCharacters() {
ephemerisManager.modified(Map.ofEntries(entry(CONFIG_DAYSET_PREFIX + CONFIG_DAYSET_WEEKEND, "\"( \"MONDAY\"")));
assertTrue(ephemerisManager.daysets.containsKey(CONFIG_DAYSET_WEEKEND));
}
@Test
public void testEphemerisManagerDoesNotCrashOnIllegalName() {
ephemerisManager.modified(Map.ofEntries(entry(CONFIG_DAYSET_PREFIX + CONFIG_DAYSET_WEEKEND, "Mondax")));
// assertion only to check if no exception occurs
assertFalse(ephemerisManager.daysets.isEmpty());
}
@Test @Test
public void testEphemerisManagerLoadedProperly() { public void testEphemerisManagerLoadedProperly() {
assertTrue(ephemerisManager.daysets.containsKey(CONFIG_DAYSET_WEEKEND)); assertTrue(ephemerisManager.daysets.containsKey(CONFIG_DAYSET_WEEKEND));
@ -77,20 +92,14 @@ public class EphemerisManagerImplOSGiTest extends JavaOSGiTest {
} }
@Test @Test
public void testConfigurtationDaysetWeekendFailed() { public void testConfigurationDaysetWeekendIterable() {
assertThrows(IllegalArgumentException.class,
() -> ephemerisManager.modified(Map.of(CONFIG_DAYSET_PREFIX + CONFIG_DAYSET_WEEKEND, "Foo,Bar")));
}
@Test
public void testConfigurtationDaysetWeekendIterable() {
ephemerisManager.modified(Map.of(CONFIG_DAYSET_PREFIX + CONFIG_DAYSET_WEEKEND, List.of("Saturday", "Sunday"))); ephemerisManager.modified(Map.of(CONFIG_DAYSET_PREFIX + CONFIG_DAYSET_WEEKEND, List.of("Saturday", "Sunday")));
assertTrue(ephemerisManager.daysets.containsKey(CONFIG_DAYSET_WEEKEND)); assertTrue(ephemerisManager.daysets.containsKey(CONFIG_DAYSET_WEEKEND));
assertEquals(Set.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY), ephemerisManager.daysets.get(CONFIG_DAYSET_WEEKEND)); assertEquals(Set.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY), ephemerisManager.daysets.get(CONFIG_DAYSET_WEEKEND));
} }
@Test @Test
public void testConfigurtationDaysetWeekendListAsString() { public void testConfigurationDaysetWeekendListAsString() {
ephemerisManager.modified(Map.of(CONFIG_DAYSET_PREFIX + CONFIG_DAYSET_WEEKEND, List.of("Saturday", "Sunday"))); ephemerisManager.modified(Map.of(CONFIG_DAYSET_PREFIX + CONFIG_DAYSET_WEEKEND, List.of("Saturday", "Sunday")));
assertTrue(ephemerisManager.daysets.containsKey(CONFIG_DAYSET_WEEKEND)); assertTrue(ephemerisManager.daysets.containsKey(CONFIG_DAYSET_WEEKEND));
assertEquals(Set.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY), ephemerisManager.daysets.get(CONFIG_DAYSET_WEEKEND)); assertEquals(Set.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY), ephemerisManager.daysets.get(CONFIG_DAYSET_WEEKEND));