TSK-1745: use annotations to link config files with TaskanaEngineConfiguration
This commit is contained in:
parent
b5ee514628
commit
8b5d372222
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue