com.societegenerale.commons.plugin.service.InvokableRules Maven / Gradle / Ivy
package com.societegenerale.commons.plugin.service;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.societegenerale.commons.plugin.Log;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.lang.ArchRule;
import static com.societegenerale.commons.plugin.utils.ReflectionUtils.getValue;
import static com.societegenerale.commons.plugin.utils.ReflectionUtils.invoke;
import static com.societegenerale.commons.plugin.utils.ReflectionUtils.loadClassWithContextClassLoader;
import static com.societegenerale.commons.plugin.utils.ReflectionUtils.newInstance;
import static java.lang.System.lineSeparator;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
class InvokableRules {
private final Class> rulesLocation;
private final Set archRuleFields;
private final Set archRuleMethods;
private final Log log;
private InvokableRules(String rulesClassName, List ruleChecks, Log log) {
this.log=log;
rulesLocation = loadClassWithContextClassLoader(rulesClassName);
Set allFieldsWhichAreArchRules = getAllFieldsWhichAreArchRules(rulesLocation.getDeclaredFields());
Set allMethodsWhichAreArchRules = getAllMethodsWhichAreArchRules(rulesLocation.getDeclaredMethods());
validateRuleChecks(Sets.union(allMethodsWhichAreArchRules, allFieldsWhichAreArchRules), ruleChecks);
Predicate isChosenCheck = ruleChecks.isEmpty() ? check -> true : ruleChecks::contains;
archRuleFields = filterNames(allFieldsWhichAreArchRules, isChosenCheck);
archRuleMethods = filterNames(allMethodsWhichAreArchRules, isChosenCheck);
if(log.isInfoEnabled()) {
logBuiltInvokableRules(rulesClassName);
}
}
private void logBuiltInvokableRules(String rulesClassName) {
log.info("just built "+rulesClassName+" : ");
log.info(archRuleFields.size()+ " field rules loaded ");
archRuleFields.stream().forEach(a -> log.info(a.toString()));
log.info(archRuleMethods.size()+ " method rules loaded");
archRuleMethods.stream().forEach(a -> log.info(a.toString()));
}
private void validateRuleChecks(Set extends Member> allFieldsAndMethods, Collection ruleChecks) {
Set allFieldAndMethodNames = allFieldsAndMethods.stream().map(Member::getName).collect(toSet());
Set illegalChecks = Sets.difference(ImmutableSet.copyOf(ruleChecks), allFieldAndMethodNames);
if (!illegalChecks.isEmpty()) {
throw new IllegalChecksConfigurationException(rulesLocation, illegalChecks);
}
}
private Set filterNames(Set members, Predicate namePredicate) {
return members.stream()
.filter(member -> namePredicate.test(member.getName()))
.collect(toSet());
}
private Set getAllMethodsWhichAreArchRules(Method[] methods) {
return stream(methods)
.filter(m -> m.getParameterCount() == 1 && JavaClasses.class.isAssignableFrom(m.getParameterTypes()[0]))
.collect(toSet());
}
private Set getAllFieldsWhichAreArchRules(Field[] fields) {
return stream(fields)
.filter(f -> ArchRule.class.isAssignableFrom(f.getType()))
.collect(toSet());
}
InvocationResult invokeOn(JavaClasses importedClasses) {
Object instance = newInstance(rulesLocation);
if(log.isInfoEnabled()) {
log.info("applying rules on "+importedClasses.size()+" classe(s). To see the details, enable debug logs");
if(log.isDebugEnabled()) {
importedClasses.stream().forEach(c -> log.debug(c.getName()));
}
}
InvocationResult result = new InvocationResult();
for (Method method : archRuleMethods) {
checkForFailure(() -> invoke(method, instance, importedClasses))
.ifPresent(result::add);
}
for (Field field : archRuleFields) {
ArchRule rule = getValue(field, instance);
checkForFailure(() -> rule.check(importedClasses))
.ifPresent(result::add);
}
return result;
}
private Optional checkForFailure(Runnable runnable) {
try {
runnable.run();
return Optional.empty();
} catch (RuntimeException | AssertionError e) {
return Optional.of(e.getMessage());
}
}
static InvokableRules of(String rulesClassName, List checks, Log log) {
return new InvokableRules(rulesClassName, checks, log);
}
static class InvocationResult {
private final List violations = new ArrayList<>();
private void add(String violationMessage) {
violations.add(violationMessage);
}
String getMessage() {
return violations.stream().collect(joining(lineSeparator()));
}
}
}