diff --git a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java index ee17c34b..2bdb4e8c 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java +++ b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java @@ -1,9 +1,10 @@ package de.tum.cit.ase.ares.api.architecturetest.java.postcompile; import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption; -import java.net.URL; import java.util.Optional; /** @@ -11,15 +12,18 @@ */ public class CustomClassResolver { - private CustomClassResolver() { - throw new IllegalStateException("Utility class"); - } - /** * Class file importer to import the class files. * This is used to import the class files from the URL. */ - private static final ClassFileImporter classFileImporter = new ClassFileImporter(); + private final JavaClasses allClasses; + + public CustomClassResolver() { + allClasses = new ClassFileImporter() + .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + .withImportOption(location -> location.toString().contains("jrt:/")) + .importClasspath(); + } /** * Try to resolve the class by the given type name. @@ -27,8 +31,11 @@ private CustomClassResolver() { * @param typeName The type name of the class to resolve. * @return The resolved class if it exists. */ - public static Optional tryResolve(String typeName) { - URL url = CustomClassResolver.class.getResource("/" + typeName.replace(".", "/") + ".class"); - return url != null ? Optional.of(classFileImporter.importUrl(url).get(typeName)) : Optional.empty(); + public Optional tryResolve(String typeName) { + try { + return Optional.ofNullable(allClasses.get(typeName)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } } } diff --git a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java index 49f3955f..202d4dfd 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java +++ b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java @@ -42,14 +42,12 @@ private JavaArchitectureTestCaseCollection() { "java.nio.file", "java.util.prefs", "sun.print", - "sun.security", "java.util.jar", "java.util.zip", "sun.awt.X11", "javax.imageio", "javax.sound.midi", - "javax.swing.filechooser", - "java.awt.desktop"); + "javax.swing.filechooser"); /** * Load pre file contents diff --git a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java index 19e46721..1380c407 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java +++ b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java @@ -30,9 +30,10 @@ public class TransitivelyAccessesMethodsCondition extends ArchCondition bannedClasses = Set.of( + private static final Set bannedClasses = Set.of( "java.lang.Object", "java.lang.String", + "java.security.AccessControl", "java.util", "sun.util" ); @@ -48,17 +49,15 @@ public class TransitivelyAccessesMethodsCondition extends ArchCondition resolvedClasses; + private final CustomClassResolver customClassResolver; /** * @param conditionPredicate Predicate to match the accessed methods */ public TransitivelyAccessesMethodsCondition(DescribedPredicate> conditionPredicate, JavaSupportedArchitectureTestCase javaSupportedArchitectureTestCase) { super("transitively depend on classes that " + conditionPredicate.getDescription()); - this.resolvedClasses = new HashMap<>(); switch (javaSupportedArchitectureTestCase) { case FILESYSTEM_INTERACTION -> { try { @@ -75,6 +74,7 @@ public TransitivelyAccessesMethodsCondition(DescribedPredicate throw new IllegalStateException("JavaSupportedArchitecture cannot be null"); } this.conditionPredicate = checkNotNull(conditionPredicate); + this.customClassResolver = new CustomClassResolver(); } /** @@ -163,38 +163,42 @@ private boolean addAccessesToPathFrom( * @return all accesses to the same target as the supplied item that are not in the analyzed classes */ private Set> getDirectAccessTargetsOutsideOfAnalyzedClasses(JavaAccess item) { - if (bannedClasses.contains(item.getTargetOwner().getFullName())) { + // If the target owner is in the banned classes, return an empty set + if (bannedClasses.stream().anyMatch(p -> item.getTargetOwner().getFullName().startsWith(p)) || item.getTargetOwner().isAssignableTo(Exception.class) || item.getTargetOwner().isAssignableTo(Error.class)) { return Collections.emptySet(); } - Set subclasses = new HashSet<>(item.getTargetOwner().getSubclasses()); - subclasses.add(item.getTargetOwner()); + // Get all subclasses of the target owner including the target owner + JavaClass resolvedTarget = resolveTargetOwner(item.getTargetOwner()); - Set> accesses = new HashSet<>(); + // Match the accesses to the target + Set subclasses = resolvedTarget.getSubclasses().stream().map(this::resolveTargetOwner).collect(toSet()); + subclasses.add(resolvedTarget); - for (JavaClass subclass : subclasses) { - Optional resolvedTarget; - if (resolvedClasses.get(subclass.getFullName()) != null) { - resolvedTarget = Optional.of(item.getTargetOwner()); - } else { - resolvedTarget = CustomClassResolver.tryResolve(item.getTargetOwner().getFullName()); - resolvedTarget.ifPresent(javaClass -> resolvedClasses.put(javaClass.getFullName(), javaClass)); - } - - accesses.addAll(resolvedTarget.map(javaClass -> javaClass.getAccessesFromSelf() - .stream() - .filter(a -> a - .getOrigin() - .getFullName() - .equals(item - .getTarget() - .getFullName() - ) - ) - .collect(toSet())).orElseGet(Set::of)); + if (subclasses.size() > 20) { + return Collections.emptySet(); } - return accesses; + return subclasses.stream() + .map(javaClass -> getAccessesFromClass(javaClass, item.getTarget().getName())) + .flatMap(Set::stream) + .collect(toSet()); + } + + private Set> getAccessesFromClass(JavaClass javaClass, String methodName) { + return javaClass.getAccessesFromSelf() + .stream() + .filter(a -> a + .getOrigin() + .getName() + .equals(methodName)) + .filter(a -> bannedClasses.stream().noneMatch(p -> a.getTargetOwner().getFullName().startsWith(p))) + .collect(toSet()); + } + + private JavaClass resolveTargetOwner(JavaClass targetOwner) { + Optional resolvedTarget = customClassResolver.tryResolve(targetOwner.getFullName()); + return resolvedTarget.orElse(targetOwner); } } } diff --git a/src/main/resources/archunit.properties b/src/main/resources/archunit.properties index 019def8c..a536fac5 100644 --- a/src/main/resources/archunit.properties +++ b/src/main/resources/archunit.properties @@ -1 +1,2 @@ +# Set to false to ignore missing dependencies in the classpath, as they are resolved manually by the de.tum.cit.ase.ares.api.architecturetest.java.postcompile.CustomClassResolver resolveMissingDependenciesFromClassPath=false \ No newline at end of file