TSK-1745: use annotations to link config files with TaskanaEngineConfiguration

This commit is contained in:
Benjamin Eckstein 2021-09-16 12:02:44 +02:00 committed by Mustapha Zorgati
parent b5ee514628
commit 8b5d372222
3 changed files with 314 additions and 267 deletions

View File

@ -0,0 +1,210 @@
package pro.taskana.common.internal.configuration;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.common.api.CustomHoliday;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.api.exceptions.WrongCustomHolidayFormatException;
import pro.taskana.common.internal.util.CheckedFunction;
import pro.taskana.common.internal.util.Pair;
public class TaskanaConfigurationInitializer {
private static final Logger LOGGER =
LoggerFactory.getLogger(TaskanaConfigurationInitializer.class);
private static final String TASKANA_CUSTOM_HOLIDAY_DAY_MONTH_SEPARATOR = ".";
private static final String TASKANA_CLASSIFICATION_CATEGORIES_PROPERTY =
"taskana.classification.categories";
public static <T> Optional<T> parseProperty(
Properties props, String key, CheckedFunction<String, T, Exception> function) {
String property = props.getProperty(key, "");
if (!property.isEmpty()) {
try {
return Optional.ofNullable(function.apply(property));
} catch (Throwable t) {
LOGGER.warn(
"Could not parse property {} ({}). Using default. Exception: {}",
key,
property,
t.getMessage());
}
}
return Optional.empty();
}
static List<String> splitStringAndTrimElements(String str, String separator) {
return splitStringAndTrimElements(str, separator, UnaryOperator.identity());
}
static List<String> splitStringAndTrimElements(
String str, String separator, UnaryOperator<String> modifier) {
return Arrays.stream(str.split(Pattern.quote(separator)))
.filter(s -> !s.isEmpty())
.map(String::trim)
.map(modifier)
.collect(Collectors.toList());
}
static CustomHoliday createCustomHolidayFromPropsEntry(String customHolidayEntry)
throws WrongCustomHolidayFormatException {
List<String> parts =
splitStringAndTrimElements(customHolidayEntry, TASKANA_CUSTOM_HOLIDAY_DAY_MONTH_SEPARATOR);
if (parts.size() == 2) {
return CustomHoliday.of(Integer.valueOf(parts.get(0)), Integer.valueOf(parts.get(1)));
}
throw new WrongCustomHolidayFormatException(customHolidayEntry);
}
public static Map<String, List<String>> configureClassificationCategoriesForType(
Properties props, List<String> classificationTypes) {
Function<String, List<String>> getClassificationCategoriesForType =
type ->
parseProperty(
props,
TASKANA_CLASSIFICATION_CATEGORIES_PROPERTY + "." + type.toLowerCase(),
p -> splitStringAndTrimElements(p, ",", String::toUpperCase))
.orElseGet(ArrayList::new);
return classificationTypes.stream()
.map(type -> Pair.of(type, getClassificationCategoriesForType.apply(type)))
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
}
public static Map<TaskanaRole, Set<String>> configureRoles(
String separator, Properties props, boolean shouldUseLowerCaseForAccessIds) {
Function<TaskanaRole, Set<String>> getAccessIdsForRole =
role ->
new HashSet<>(
splitStringAndTrimElements(
props.getProperty(role.getPropertyName().toLowerCase(), ""),
separator,
shouldUseLowerCaseForAccessIds
? String::toLowerCase
: UnaryOperator.identity()));
return Arrays.stream(TaskanaRole.values())
.map(role -> Pair.of(role, getAccessIdsForRole.apply(role)))
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
}
public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
fields.addAll(Arrays.asList(type.getDeclaredFields()));
if (type.getSuperclass() != null) {
getAllFields(fields, type.getSuperclass());
}
return fields;
}
public static void configureAnnotatedFields(Object instance, String separator, Properties props) {
final List<Field> fields = getAllFields(new ArrayList<>(), instance.getClass());
for (Field field : fields) {
Optional.ofNullable(field.getAnnotation(TaskanaProperty.class))
.ifPresent(
taskanaProperty -> {
final String fieldPropertyName = taskanaProperty.value();
final Class<?> type = field.getType();
String name = type.getSimpleName();
switch (name) {
case "int":
parseProperty(props, fieldPropertyName, Integer::parseInt)
.ifPresent(value -> setFieldValue(instance, field, value));
break;
case "boolean":
parseProperty(props, fieldPropertyName, Boolean::parseBoolean)
.ifPresent(value -> setFieldValue(instance, field, value));
break;
case "String":
parseProperty(props, fieldPropertyName, String::new)
.ifPresent(value -> setFieldValue(instance, field, value));
break;
case "Duration":
parseProperty(props, fieldPropertyName, Duration::parse)
.ifPresent(value -> setFieldValue(instance, field, value));
break;
case "Instant":
parseProperty(props, fieldPropertyName, Instant::parse)
.ifPresent(value -> setFieldValue(instance, field, value));
break;
case "List":
final String typeName =
((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0].getTypeName();
if (typeName.equals("pro.taskana.common.api.CustomHoliday")) {
CheckedFunction<String, List<CustomHoliday>, Exception> parseFunction2 =
s ->
splitStringAndTrimElements(s, separator).stream()
.map(
str -> {
try {
return createCustomHolidayFromPropsEntry(str);
} catch (WrongCustomHolidayFormatException e) {
LOGGER.warn(e.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
parseProperty(props, fieldPropertyName, parseFunction2)
.ifPresent(value -> setFieldValue(instance, field, value));
} else {
CheckedFunction<String, List<String>, Exception> parseListFunction =
p -> splitStringAndTrimElements(p, ",", String::toUpperCase);
parseProperty(props, fieldPropertyName, parseListFunction)
.ifPresent(value -> setFieldValue(instance, field, value));
}
break;
case "Map":
// TODO
break;
default:
throw new SystemException("Unknown configuration type " + name);
}
});
}
}
private static void setFieldValue(Object instance, Field field, Object value) {
final Optional<Method> hasSetterMethod =
Arrays.stream(instance.getClass().getMethods())
.filter(m -> m.getParameterCount() == 1)
.filter(m -> m.getName().startsWith("set"))
.filter(m -> m.getName().toLowerCase().contains(field.getName().toLowerCase()))
.findFirst();
if (!hasSetterMethod.isPresent()) {
throw new SystemException("No setter method for " + field.getName());
}
try {
final Method method = hasSetterMethod.get();
method.invoke(instance, value);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new SystemException(
"Property value " + value + " is invalid for field " + field.getName(), e);
}
}
}

View File

@ -0,0 +1,13 @@
package pro.taskana.common.internal.configuration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TaskanaProperty {
String value();
}

View File

@ -1,5 +1,9 @@
package pro.taskana;
import static pro.taskana.common.internal.configuration.TaskanaConfigurationInitializer.configureAnnotatedFields;
import static pro.taskana.common.internal.configuration.TaskanaConfigurationInitializer.configureClassificationCategoriesForType;
import static pro.taskana.common.internal.configuration.TaskanaConfigurationInitializer.configureRoles;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
@ -7,21 +11,13 @@ import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
@ -33,12 +29,10 @@ import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.api.exceptions.WrongCustomHolidayFormatException;
import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.configuration.DB;
import pro.taskana.common.internal.util.CheckedFunction;
import pro.taskana.common.internal.configuration.TaskanaProperty;
import pro.taskana.common.internal.util.FileLoaderUtil;
import pro.taskana.common.internal.util.Pair;
/**
* This central class creates the TaskanaEngine and holds all the information about DB and Security.
@ -52,40 +46,10 @@ public class TaskanaEngineConfiguration {
private static final String TASKANA_PROPERTIES = "/taskana.properties";
private static final String TASKANA_PROPERTY_SEPARATOR = "|";
private static final String TASKANA_JOB_BATCH_SIZE = "taskana.jobs.batchSize";
private static final String TASKANA_JOB_RETRIES = "taskana.jobs.maxRetries";
private static final String TASKANA_JOB_CLEANUP_RUN_EVERY = "taskana.jobs.cleanup.runEvery";
private static final String TASKANA_JOB_CLEANUP_FIRST_RUN = "taskana.jobs.cleanup.firstRunAt";
private static final String TASKANA_JOB_CLEANUP_MINIMUM_AGE = "taskana.jobs.cleanup.minimumAge";
private static final String TASKANA_JOB_TASK_CLEANUP_ALL_COMPLETED_SAME_PARENT_BUSINESS =
"taskana.jobs.cleanup.allCompletedSameParentBusiness";
private static final String TASKANA_JOB_PRIORITY_BATCHSIZE = "taskana.jobs.priority.batchSize";
private static final String TASKANA_JOB_PRIORITY_RUN_EVERY = "taskana.jobs.priority.runEvery";
private static final String TASKANA_JOB_PRIORITY_FIRST_RUN = "taskana.jobs.priority.firstRunAt";
private static final String TASKANA_JOB_PRIORITY_ACTIVE = "taskana.jobs.priority.active";
private static final String TASKANA_JOB_USER_REFRESH_FIRST_RUN =
"taskana.jobs.user.refresh.firstRunAt";
private static final String TASKANA_JOB_USER_REFRESH_RUN_EVERY =
"taskana.jobs.user.refresh.runEvery";
private static final String TASKANA_DOMAINS_PROPERTY = "taskana.domains";
private static final String TASKANA_CLASSIFICATION_TYPES_PROPERTY =
"taskana.classification.types";
private static final String TASKANA_CLASSIFICATION_CATEGORIES_PROPERTY =
"taskana.classification.categories";
private static final String TASKANA_GERMAN_HOLIDAYS_ENABLED = "taskana.german.holidays.enabled";
private static final String TASKANA_GERMAN_HOLIDAYS_CORPUS_CHRISTI_ENABLED =
"taskana.german.holidays.corpus-christi.enabled";
private static final String TASKANA_CUSTOM_HOLIDAY = "taskana.custom.holidays";
private static final String TASKANA_CUSTOM_HOLIDAY_DAY_MONTH_SEPARATOR = ".";
private static final String TASKANA_HISTORY_DELETION_ON_TASK_DELETION_ENABLED =
"taskana.history.deletion.on.task.deletion.enabled";
private static final String TASKANA_VALIDATION_ALLOW_TIMESTAMP_SERVICE_LEVEL_MISMATCH =
"taskana.validation.allowTimestampServiceLevelMismatch";
private static final String TASKANA_ADD_ADDITIONAL_USER_INFO = "taskana.addAdditionalUserInfo";
// TASKANA_SCHEMA_VERSION
private static final String DEFAULT_SCHEMA_NAME = "TASKANA";
private final List<CustomHoliday> customHolidays = new ArrayList<>();
// Taskana properties file
protected String propertiesFileName = TASKANA_PROPERTIES;
// Taskana datasource configuration
@ -94,38 +58,79 @@ public class TaskanaEngineConfiguration {
// Taskana role configuration
protected String propertiesSeparator = TASKANA_PROPERTY_SEPARATOR;
protected Map<TaskanaRole, Set<String>> roleMap;
// global switch to enable JAAS based authentication and Taskana
// authorizations
protected boolean securityEnabled;
protected boolean useManagedTransactions;
@TaskanaProperty("taskana.custom.holidays")
private List<CustomHoliday> customHolidays = new ArrayList<>();
// List of configured domain names
@TaskanaProperty("taskana.domains")
protected List<String> domains = new ArrayList<>();
// List of configured classification types
@TaskanaProperty("taskana.classification.types")
protected List<String> classificationTypes = new ArrayList<>();
@TaskanaProperty("taskana.classification.categories")
protected Map<String, List<String>> classificationCategoriesByTypeMap = new HashMap<>();
// Properties for the monitor
@TaskanaProperty("taskana.history.deletion.on.task.deletion.enabled")
private boolean deleteHistoryOnTaskDeletionEnabled;
@TaskanaProperty("taskana.german.holidays.enabled")
private boolean germanPublicHolidaysEnabled;
@TaskanaProperty("taskana.german.holidays.corpus-christi.enabled")
private boolean corpusChristiEnabled;
// Properties for general job execution
@TaskanaProperty("taskana.jobs.batchSize")
private int jobBatchSize = 100;
@TaskanaProperty("taskana.jobs.maxRetries")
private int maxNumberOfJobRetries = 3;
// Properties for the cleanup job
@TaskanaProperty("taskana.jobs.cleanup.firstRunAt")
private Instant cleanupJobFirstRun = Instant.parse("2018-01-01T00:00:00Z");
@TaskanaProperty("taskana.jobs.cleanup.runEvery")
private Duration cleanupJobRunEvery = Duration.parse("P1D");
@TaskanaProperty("taskana.jobs.cleanup.minimumAge")
private Duration cleanupJobMinimumAge = Duration.parse("P14D");
// TASKANA behavior
@TaskanaProperty("taskana.jobs.cleanup.allCompletedSameParentBusiness")
private boolean taskCleanupJobAllCompletedSameParentBusiness = true;
@TaskanaProperty("taskana.validation.allowTimestampServiceLevelMismatch")
private boolean validationAllowTimestampServiceLevelMismatch = false;
//Property to enable/disable the addition of user full/long name through joins
@TaskanaProperty("taskana.addAdditionalUserInfo")
private boolean addAdditionalUserInfo = false;
@TaskanaProperty("taskana.jobs.priority.batchSize")
private int priorityJobBatchSize = 100;
@TaskanaProperty("taskana.jobs.priority.firstRunAt")
private Instant priorityJobFirstRun = Instant.parse("2018-01-01T00:00:00Z");
@TaskanaProperty("taskana.jobs.priority.runEvery")
private Duration priorityJobRunEvery = Duration.parse("P1D");
@TaskanaProperty("taskana.jobs.priority.active")
private boolean priorityJobActive = false;
@TaskanaProperty("taskana.jobs.user.refresh.runEvery")
private Duration userRefreshJobRunEvery = Duration.parse("P1D");
@TaskanaProperty("taskana.jobs.user.refresh.firstRunAt")
private Instant userRefreshJobFirstRun = Instant.parse("2018-01-01T23:00:00Z");
public TaskanaEngineConfiguration(
@ -170,32 +175,50 @@ public class TaskanaEngineConfiguration {
initTaskanaProperties(this.propertiesFileName, this.propertiesSeparator);
}
public static Properties loadProperties(String propertiesFile) {
Properties props = new Properties();
try (InputStream stream =
FileLoaderUtil.openFileFromClasspathOrSystem(
propertiesFile, TaskanaEngineConfiguration.class)) {
props.load(stream);
} catch (IOException e) {
LOGGER.error("caught IOException when processing properties file {}.", propertiesFile);
throw new SystemException(
"internal System error when processing properties file " + propertiesFile, e.getCause());
}
return props;
}
public void initTaskanaProperties(String propertiesFile, String separator) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"Reading taskana configuration from {} with separator {}", propertiesFile, separator);
}
Properties props = readPropertiesFromFile(propertiesFile);
initTaskanaRoles(props, separator);
initJobParameters(props);
initDomains(props);
initClassificationTypes(props);
initClassificationCategories(props);
initBooleanProperty(
props, TASKANA_GERMAN_HOLIDAYS_ENABLED, this::setGermanPublicHolidaysEnabled);
initBooleanProperty(
props, TASKANA_GERMAN_HOLIDAYS_CORPUS_CHRISTI_ENABLED, this::setCorpusChristiEnabled);
initBooleanProperty(
props,
TASKANA_HISTORY_DELETION_ON_TASK_DELETION_ENABLED,
this::setDeleteHistoryOnTaskDeletionEnabled);
initBooleanProperty(
props,
TASKANA_VALIDATION_ALLOW_TIMESTAMP_SERVICE_LEVEL_MISMATCH,
this::setValidationAllowTimestampServiceLevelMismatch);
initBooleanProperty(
props, TASKANA_ADD_ADDITIONAL_USER_INFO, this::setAddAdditionalUserInfo);
initCustomHolidays(props, separator);
Properties props = loadProperties(propertiesFile);
configureAnnotatedFields(this, separator, props);
roleMap = configureRoles(separator, props, shouldUseLowerCaseForAccessIds());
classificationCategoriesByTypeMap =
configureClassificationCategoriesForType(props, classificationTypes);
if (LOGGER.isDebugEnabled()) {
roleMap.forEach((k, v) -> LOGGER.debug("Found Taskana RoleConfig {} : {} ", k, v));
LOGGER.debug(
"Configured number of task and workbasket updates per transaction: {}", jobBatchSize);
LOGGER.debug("Number of retries of failed task updates: {}", maxNumberOfJobRetries);
LOGGER.debug("CleanupJob configuration: first run at {}", cleanupJobFirstRun);
LOGGER.debug("CleanupJob configuration: runs every {}", cleanupJobRunEvery);
LOGGER.debug(
"CleanupJob configuration: minimum age of tasks to be cleanup up is {}",
cleanupJobMinimumAge);
LOGGER.debug(
"TaskCleanupJob configuration: all completed task with the "
+ "same parent business property id {}",
taskCleanupJobAllCompletedSameParentBusiness);
LOGGER.debug("Configured classification categories : {}", classificationCategoriesByTypeMap);
LOGGER.debug("Configured domains: {}", domains);
LOGGER.debug("Configured classificationTypes: {}", classificationTypes);
LOGGER.debug("Configured custom Holidays : {}", customHolidays);
}
}
public static DataSource createDefaultDataSource() {
@ -310,6 +333,10 @@ public class TaskanaEngineConfiguration {
return customHolidays;
}
public void setCustomHolidays(List<CustomHoliday> customHolidays) {
this.customHolidays = new ArrayList<>(customHolidays);
}
public void addCustomHolidays(List<CustomHoliday> customHolidays) {
this.customHolidays.addAll(customHolidays);
}
@ -469,129 +496,7 @@ public class TaskanaEngineConfiguration {
}
public Properties readPropertiesFromFile() {
return readPropertiesFromFile(this.propertiesFileName);
}
private <T> Optional<T> parseProperty(
Properties props, String key, CheckedFunction<String, T, Exception> function) {
String property = props.getProperty(key, "");
if (!property.isEmpty()) {
try {
return Optional.ofNullable(function.apply(property));
} catch (Exception t) {
LOGGER.warn(
"Could not parse property {} ({}). Using default. Exception: {}",
key,
property,
t.getMessage());
}
}
return Optional.empty();
}
private void initJobParameters(Properties props) {
parseProperty(props, TASKANA_JOB_BATCH_SIZE, Integer::parseInt)
.ifPresent(this::setMaxNumberOfUpdatesPerTransaction);
parseProperty(props, TASKANA_JOB_RETRIES, Integer::parseInt)
.ifPresent(this::setMaxNumberOfJobRetries);
parseProperty(props, TASKANA_JOB_CLEANUP_FIRST_RUN, Instant::parse)
.ifPresent(this::setCleanupJobFirstRun);
parseProperty(props, TASKANA_JOB_CLEANUP_RUN_EVERY, Duration::parse)
.ifPresent(this::setCleanupJobRunEvery);
parseProperty(props, TASKANA_JOB_CLEANUP_MINIMUM_AGE, Duration::parse)
.ifPresent(this::setCleanupJobMinimumAge);
parseProperty(props, TASKANA_JOB_PRIORITY_BATCHSIZE, Integer::parseInt)
.ifPresent(this::setPriorityJobBatchSize);
parseProperty(props, TASKANA_JOB_PRIORITY_RUN_EVERY, Duration::parse)
.ifPresent(this::setPriorityJobRunEvery);
parseProperty(props, TASKANA_JOB_PRIORITY_FIRST_RUN, Instant::parse)
.ifPresent(this::setPriorityJobFirstRun);
parseProperty(props, TASKANA_JOB_PRIORITY_ACTIVE, Boolean::parseBoolean)
.ifPresent(this::setPriorityJobActive);
parseProperty(props, TASKANA_JOB_USER_REFRESH_RUN_EVERY, Duration::parse)
.ifPresent(this::setUserRefreshJobRunEvery);
parseProperty(props, TASKANA_JOB_USER_REFRESH_FIRST_RUN, Instant::parse)
.ifPresent(this::setUserRefreshJobFirstRun);
parseProperty(
props,
TASKANA_JOB_TASK_CLEANUP_ALL_COMPLETED_SAME_PARENT_BUSINESS,
Boolean::parseBoolean)
.ifPresent(this::setTaskCleanupJobAllCompletedSameParentBusiness);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"Configured number of task and workbasket updates per transaction: {}", jobBatchSize);
LOGGER.debug("Number of retries of failed task updates: {}", maxNumberOfJobRetries);
LOGGER.debug("CleanupJob configuration: first run at {}", cleanupJobFirstRun);
LOGGER.debug("CleanupJob configuration: runs every {}", cleanupJobRunEvery);
LOGGER.debug(
"CleanupJob configuration: minimum age of tasks to be cleanup up is {}",
cleanupJobMinimumAge);
LOGGER.debug(
"TaskCleanupJob configuration: all completed task with the "
+ "same parent business property id {}",
taskCleanupJobAllCompletedSameParentBusiness);
}
}
private void initDomains(Properties props) {
CheckedFunction<String, List<String>, Exception> parseFunction =
p -> splitStringAndTrimElements(p, ",", String::toUpperCase);
parseProperty(props, TASKANA_DOMAINS_PROPERTY, parseFunction).ifPresent(this::setDomains);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Configured domains: {}", domains);
}
}
private void initClassificationTypes(Properties props) {
CheckedFunction<String, List<String>, Exception> parseFunction =
p -> splitStringAndTrimElements(p, ",", String::toUpperCase);
parseProperty(props, TASKANA_CLASSIFICATION_TYPES_PROPERTY, parseFunction)
.ifPresent(this::setClassificationTypes);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Configured classificationTypes: {}", classificationTypes);
}
}
private void initClassificationCategories(Properties props) {
Function<String, List<String>> getClassificationCategoriesForType =
type -> {
CheckedFunction<String, List<String>, Exception> parseFunction =
s -> splitStringAndTrimElements(s, ",", String::toUpperCase);
return parseProperty(
props,
TASKANA_CLASSIFICATION_CATEGORIES_PROPERTY + "." + type.toLowerCase(),
parseFunction)
.orElseGet(ArrayList::new);
};
classificationCategoriesByTypeMap =
classificationTypes.stream()
.map(type -> Pair.of(type, getClassificationCategoriesForType.apply(type)))
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Configured classification categories : {}", classificationCategoriesByTypeMap);
}
}
private void initBooleanProperty(
Properties props, String propertyName, Consumer<Boolean> consumer) {
parseProperty(props, propertyName, Boolean::parseBoolean).ifPresent(consumer);
return loadProperties(this.propertiesFileName);
}
private void initSchemaName(String schemaName) {
@ -617,85 +522,4 @@ public class TaskanaEngineConfiguration {
LOGGER.debug("Using schema name {}", this.getSchemaName());
}
}
private void initTaskanaRoles(Properties props, String rolesSeparator) {
Function<TaskanaRole, Set<String>> getAccessIdsForRole =
role -> {
List<String> accessIds =
splitStringAndTrimElements(
props.getProperty(role.getPropertyName().toLowerCase(), ""),
rolesSeparator,
shouldUseLowerCaseForAccessIds()
? String::toLowerCase
: UnaryOperator.identity());
return new HashSet<>(accessIds);
};
roleMap =
Arrays.stream(TaskanaRole.values())
.map(role -> Pair.of(role, getAccessIdsForRole.apply(role)))
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
if (LOGGER.isDebugEnabled()) {
roleMap.forEach((k, v) -> LOGGER.debug("Found Taskana RoleConfig {} : {} ", k, v));
}
}
private void initCustomHolidays(Properties props, String separator) {
CheckedFunction<String, List<CustomHoliday>, Exception> parseFunction =
s ->
splitStringAndTrimElements(s, separator).stream()
.map(
str -> {
try {
return createCustomHolidayFromPropsEntry(str);
} catch (WrongCustomHolidayFormatException e) {
LOGGER.warn(e.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
parseProperty(props, TASKANA_CUSTOM_HOLIDAY, parseFunction).ifPresent(this::addCustomHolidays);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Configured custom Holidays : {}", customHolidays);
}
}
private CustomHoliday createCustomHolidayFromPropsEntry(String customHolidayEntry)
throws WrongCustomHolidayFormatException {
List<String> parts =
splitStringAndTrimElements(customHolidayEntry, TASKANA_CUSTOM_HOLIDAY_DAY_MONTH_SEPARATOR);
if (parts.size() == 2) {
return CustomHoliday.of(Integer.valueOf(parts.get(0)), Integer.valueOf(parts.get(1)));
}
throw new WrongCustomHolidayFormatException(customHolidayEntry);
}
private List<String> splitStringAndTrimElements(String str, String separator) {
return splitStringAndTrimElements(str, separator, UnaryOperator.identity());
}
private List<String> splitStringAndTrimElements(
String str, String separator, UnaryOperator<String> modifier) {
return Arrays.stream(str.split(Pattern.quote(separator)))
.filter(s -> !s.isEmpty())
.map(String::trim)
.map(modifier)
.collect(Collectors.toList());
}
private Properties readPropertiesFromFile(String propertiesFile) {
Properties props = new Properties();
try (InputStream stream =
FileLoaderUtil.openFileFromClasspathOrSystem(propertiesFile, getClass())) {
props.load(stream);
} catch (IOException e) {
LOGGER.error("caught IOException when processing properties file {}.", propertiesFile);
throw new SystemException(
"internal System error when processing properties file " + propertiesFile, e.getCause());
}
return props;
}
}