All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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()));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy