package com.tngtech.archunit.exampletest; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.annotation.PostConstruct; import javax.ejb.Local; import javax.ejb.Stateless; import com.google.common.base.Joiner; import com.tngtech.archunit.base.DescribedPredicate; import com.tngtech.archunit.core.domain.AccessTarget.FieldAccessTarget; import com.tngtech.archunit.core.domain.JavaAccess; import com.tngtech.archunit.core.domain.JavaAccess.Functions.Get; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaFieldAccess; import com.tngtech.archunit.core.domain.properties.HasOwner; import com.tngtech.archunit.example.ClassViolatingSessionBeanRules; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import static com.tngtech.archunit.base.DescribedPredicate.not; import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.originOwnerEqualsTargetOwner; import static com.tngtech.archunit.core.domain.JavaClass.Predicates.INTERFACES; import static com.tngtech.archunit.core.domain.JavaCodeUnit.Predicates.constructor; import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith; import static com.tngtech.archunit.lang.conditions.ArchPredicates.are; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; public class SessionBeanRulesTest { private JavaClasses classes; @Before public void setUp() throws Exception { classes = new ClassFileImportHelper().importTreesOf(ClassViolatingSessionBeanRules.class); } @Ignore @Test public void stateless_session_beans_should_not_have_state() { noClasses().should() .setFieldWhere(TARGET_IS_STATELESS_SESSION_BEAN.and(ACCESS_ORIGIN_IS_OUTSIDE_OF_CONSTRUCTION)) .as("No Stateless Session Bean should have state").check(classes); } @Ignore @Test public void business_interface_implementations_should_be_unique() { classes().that(are(BUSINESS_INTERFACES)).should(HAVE_AN_UNIQUE_IMPLEMENTATION).check(classes); } private static final DescribedPredicate<JavaFieldAccess> TARGET_IS_STATELESS_SESSION_BEAN = Get.<JavaFieldAccess, FieldAccessTarget>target() .then(HasOwner.Functions.Get.<JavaClass>owner()) .is(annotatedWith(Stateless.class)); private static final DescribedPredicate<JavaAccess<?>> ACCESS_ORIGIN_IS_OUTSIDE_OF_CONSTRUCTION = not(originOwnerEqualsTargetOwner()).or(originNeitherConstructorNorPostConstruct()); private static DescribedPredicate<JavaAccess<?>> originNeitherConstructorNorPostConstruct() { return Get.origin().is(not(constructor()).and(not(annotatedWith(PostConstruct.class)))); } private static final DescribedPredicate<JavaClass> HAVE_LOCAL_BEAN_SUBCLASS = new DescribedPredicate<JavaClass>("have subclass that is a local bean") { @Override public boolean apply(JavaClass input) { for (JavaClass subClass : input.getAllSubClasses()) { if (isLocalBeanImplementation(subClass, input)) { return true; } } return false; } // NOTE: We assume that in this project by convention @Local is always used as @Local(type) with exactly // one type, otherwise this would need to be more sophisticated private boolean isLocalBeanImplementation(JavaClass bean, JavaClass businessInterfaceType) { return bean.isAnnotatedWith(Local.class) && bean.getAnnotationOfType(Local.class).value()[0].getName() .equals(businessInterfaceType.getName()); } }; private static final DescribedPredicate<JavaClass> BUSINESS_INTERFACES = INTERFACES.and(HAVE_LOCAL_BEAN_SUBCLASS).as("business interfaces"); private static final ArchCondition<JavaClass> HAVE_AN_UNIQUE_IMPLEMENTATION = new ArchCondition<JavaClass>("have an unique implementation") { @Override public void check(JavaClass businessInterface, ConditionEvents events) { events.add(new SimpleConditionEvent<>(businessInterface, businessInterface.getAllSubClasses().size() <= 1, describe(businessInterface))); } private String describe(JavaClass businessInterface) { return String.format("%s is implemented by %s", businessInterface.getSimpleName(), joinNamesOf(businessInterface.getAllSubClasses())); } private String joinNamesOf(Set<JavaClass> implementations) { List<String> simpleNames = new ArrayList<>(); for (JavaClass impl : implementations) { simpleNames.add(impl.getSimpleName()); } return Joiner.on(", ").join(simpleNames); } }; }