From 2bc41b4a519dcb123e62c263e399d38029e8497d Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 15 Jul 2024 01:43:32 +0200 Subject: [PATCH 01/13] Add workflow to build package and run tests --- .github/workflows/maven.yml | 36 +++++++++++++++++++ .../ares/integration/AstAssertionTest.java | 7 ++-- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..d906a3d5 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,36 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'adopt' + + - name: Install Maven + run: | + MAVEN_VERSION=3.9.4 # Replace with the version you need + wget https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz + tar -xvzf apache-maven-${MAVEN_VERSION}-bin.tar.gz + sudo mv apache-maven-${MAVEN_VERSION} /opt/maven + sudo ln -s /opt/maven/bin/mvn /usr/bin/mvn + + - name: Build with Maven + run: mvn -B package --file pom.xml diff --git a/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java b/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java index 5a1792e5..fde13659 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java @@ -689,8 +689,11 @@ void test_testExcludesPassedMethods_Success() { @TestTest void test_testExcludesPassedMethod_Fail() { String testExcludesPassedMethod_Fail = "testExcludesPassedMethod_Fail"; - tests.assertThatEvents().haveExactly(1, testFailedWith(testExcludesPassedMethod_Fail, AssertionError.class, "Unwanted recursion found:\n" + - "de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.ClassWithNoExcludeMethods.something(de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.RandomParameterThatShouldBeResolved)")); + tests.assertThatEvents().haveExactly(1, testFailedWith(testExcludesPassedMethod_Fail, AssertionError.class, "Unwanted statement found:" + System.lineSeparator() + " - In " + + Path.of("src", "test", "java", "de", "tum", "cit", "ase", "ares", "integration", "testuser", "subject", "structural", "astTestFiles", "recursions" , "excludeMethods", + "ClassWithNoExcludeMethods.java") + + ":" + System.lineSeparator() + " - Unwanted Recursion was found:" + + System.lineSeparator() + " - Between line 5 (column 5) and line 7 (column 5) in Method de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.ClassWithNoExcludeMethods.something(de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.RandomParameterThatShouldBeResolved)" + System.lineSeparator())); } @TestTest From f38aada04fc0adf3f90f1107e5df3b3381443454 Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 15 Jul 2024 01:52:20 +0200 Subject: [PATCH 02/13] Remove maven install since it is already installed --- .github/workflows/maven.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d906a3d5..72f209a2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -11,7 +11,6 @@ on: jobs: build: - runs-on: ubuntu-latest steps: @@ -24,13 +23,5 @@ jobs: java-version: '21' distribution: 'adopt' - - name: Install Maven - run: | - MAVEN_VERSION=3.9.4 # Replace with the version you need - wget https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz - tar -xvzf apache-maven-${MAVEN_VERSION}-bin.tar.gz - sudo mv apache-maven-${MAVEN_VERSION} /opt/maven - sudo ln -s /opt/maven/bin/mvn /usr/bin/mvn - - name: Build with Maven run: mvn -B package --file pom.xml From 6751c108f130292478f8a3b3e3744da41bd13ead Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 15 Jul 2024 02:02:19 +0200 Subject: [PATCH 03/13] Update maven --- .github/workflows/maven.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 72f209a2..036cd14d 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -11,6 +11,7 @@ on: jobs: build: + runs-on: ubuntu-latest steps: @@ -23,5 +24,10 @@ jobs: java-version: '21' distribution: 'adopt' + - name: Install Maven + run: | + sudo wget -q -O - "https://apache.osuosl.org/maven/maven-3/3.9.8/binaries/apache-maven-3.9.8-bin.tar.gz" | sudo tar xzf - -C /usr/share + sudo ln -s /usr/share/apache-maven-3.9.8/bin/mvn /usr/bin/mvn + - name: Build with Maven run: mvn -B package --file pom.xml From 6ce484001005dfb3f9a64362acf3afeedf333fcb Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 15 Jul 2024 02:05:08 +0200 Subject: [PATCH 04/13] Update maven version --- .github/workflows/maven.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 036cd14d..187bb2c2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -26,6 +26,7 @@ jobs: - name: Install Maven run: | + sudo rm -rf /usr/share/maven sudo wget -q -O - "https://apache.osuosl.org/maven/maven-3/3.9.8/binaries/apache-maven-3.9.8-bin.tar.gz" | sudo tar xzf - -C /usr/share sudo ln -s /usr/share/apache-maven-3.9.8/bin/mvn /usr/bin/mvn From d7c6370024ec686384f423aac757b1b3a41b66bc Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 15 Jul 2024 02:06:03 +0200 Subject: [PATCH 05/13] Update maven version --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 187bb2c2..7e635f74 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -26,7 +26,7 @@ jobs: - name: Install Maven run: | - sudo rm -rf /usr/share/maven + sudo rm -rf /usr/bin/mvn sudo wget -q -O - "https://apache.osuosl.org/maven/maven-3/3.9.8/binaries/apache-maven-3.9.8-bin.tar.gz" | sudo tar xzf - -C /usr/share sudo ln -s /usr/share/apache-maven-3.9.8/bin/mvn /usr/bin/mvn From 9afa6704e0b3e08f722f6e3756b76d2b54c0d8a1 Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 29 Jul 2024 14:45:30 +0200 Subject: [PATCH 06/13] Fix failing tests --- .../java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java | 2 +- .../java/de/tum/cit/ase/ares/integration/AstAssertionTest.java | 3 +-- .../tum/cit/ase/ares/integration/testuser/PathAccessUser.java | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java b/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java index 173c6490..ae6bff1a 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java +++ b/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java @@ -108,7 +108,7 @@ public static String formatVertexInfoWithinCycle(String vertexName, MethodCallGr } return localized("ast.method.get_formatted_file_string_prefix", pathOfSourceRoot.toString() - + "/" + Objects.requireNonNull(extractClassName(vertexName))) + + File.separator + Objects.requireNonNull(extractClassName(vertexName))) + System.lineSeparator() + localized("ast.method.get_formatted_unwanted_node_string_prefix", "Unwanted Recursion") diff --git a/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java b/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java index fde13659..26b87255 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java @@ -690,8 +690,7 @@ void test_testExcludesPassedMethods_Success() { void test_testExcludesPassedMethod_Fail() { String testExcludesPassedMethod_Fail = "testExcludesPassedMethod_Fail"; tests.assertThatEvents().haveExactly(1, testFailedWith(testExcludesPassedMethod_Fail, AssertionError.class, "Unwanted statement found:" + System.lineSeparator() + " - In " - + Path.of("src", "test", "java", "de", "tum", "cit", "ase", "ares", "integration", "testuser", "subject", "structural", "astTestFiles", "recursions" , "excludeMethods", - "ClassWithNoExcludeMethods.java") + + Path.of("src", "test", "java", "de", "tum", "cit", "ase", "ares", "integration", "testuser", "subject", "structural", "astTestFiles", "recursions" , "excludeMethods", "ClassWithNoExcludeMethods.java") + ":" + System.lineSeparator() + " - Unwanted Recursion was found:" + System.lineSeparator() + " - Between line 5 (column 5) and line 7 (column 5) in Method de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.ClassWithNoExcludeMethods.something(de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.RandomParameterThatShouldBeResolved)" + System.lineSeparator())); } diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java index c6bad35a..a645fa21 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java @@ -102,7 +102,6 @@ void accessPathTest() throws IOException { } @PublicTest - @Policy(value = "src/test/resources/de/tum/cit/ase/ares/integration/testuser/securitypolicies/NoAllowedPathPolicy.yaml", withinPath = "test-classes/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess") void weAccessPath() throws IOException { Files.readString(Path.of("pom.xml")); } From 590fdd2547f18a8d11b1392611978a3d40fe3ca9 Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 29 Jul 2024 15:13:18 +0200 Subject: [PATCH 07/13] Change name of the job --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 7e635f74..c3c37667 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -10,7 +10,7 @@ on: branches: [ "main" ] jobs: - build: + build-and-test: runs-on: ubuntu-latest From 4ee9d55e5f72789a2573264ce5aba8a75750a109 Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 29 Jul 2024 19:50:46 +0200 Subject: [PATCH 08/13] New tests added, improved the performance of the transitive lookup --- .../java/postcompile/CustomClassResolver.java | 3 - .../TransitivelyAccessesMethodsCondition.java | 45 +- src/main/resources/archunit.properties | 1 + .../ase/ares/integration/PathAccessTest.java | 5 + .../integration/testuser/PathAccessUser.java | 4 + .../subject/pathaccess/PathAccessPenguin.java | 2 - .../student/FileSystemAccessPenguin.java | 416 ++++++++++++++++++ .../ThirdPartyPackagePenguin.java | 18 + 8 files changed, 483 insertions(+), 11 deletions(-) create mode 100644 src/main/resources/archunit.properties create mode 100644 src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/student/FileSystemAccessPenguin.java create mode 100644 src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/thirdpartypackage/ThirdPartyPackagePenguin.java 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 f0ff7dc9..ee17c34b 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,7 @@ package de.tum.cit.ase.ares.api.architecturetest.java.postcompile; -import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.importer.ClassFileImporter; -import com.tngtech.archunit.core.importer.resolvers.ClassResolverFromClasspath; import java.net.URL; import java.util.Optional; @@ -30,7 +28,6 @@ private CustomClassResolver() { * @return The resolved class if it exists. */ public static Optional tryResolve(String typeName) { - ArchConfiguration.get().setClassResolver(ClassResolverFromClasspath.class); URL url = CustomClassResolver.class.getResource("/" + typeName.replace(".", "/") + ".class"); return url != null ? Optional.of(classFileImporter.importUrl(url).get(typeName)) : Optional.empty(); } 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 3fbe5cbf..f76b26b3 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 @@ -12,10 +12,7 @@ import de.tum.cit.ase.ares.api.architecturetest.java.JavaSupportedArchitectureTestCase; import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import static com.tngtech.archunit.lang.ConditionEvent.createMessage; import static com.tngtech.archunit.thirdparty.com.google.common.base.Preconditions.checkNotNull; @@ -30,15 +27,38 @@ */ public class TransitivelyAccessesMethodsCondition extends ArchCondition { + /** + * Set of classes that does not lead to a violation if they are accessed + */ + private final static Set bannedClasses = Set.of( + "java.lang.Object", + "java.lang.String", + "java.util", + "sun.util" + ); + + /** + * Predicate to match the accessed methods + */ private final DescribedPredicate> conditionPredicate; + + /** + * Transitive access path to find the path to the accessed method + */ private final TransitiveAccessPath transitiveAccessPath = new TransitiveAccessPath(); + /** + * Map to store the resolved classes + * This maximizes the performance of the condition by resolving the classes only once + */ + private final Map resolvedClasses; + /** * @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 { @@ -143,7 +163,20 @@ 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) { - Optional resolvedTarget = CustomClassResolver.tryResolve(item.getTargetOwner().getFullName()); + if (bannedClasses.contains(item.getTargetOwner().getFullName())) { + return Collections.emptySet(); + } + + + // TODO somehow check the subclasses as well since they are not detected by accessesFromSelf + Optional resolvedTarget; + if (resolvedClasses.get(item.getTargetOwner().getFullName()) != null) { + resolvedTarget = Optional.of(item.getTargetOwner()); + } else { + resolvedTarget = CustomClassResolver.tryResolve(item.getTargetOwner().getFullName()); + resolvedTarget.ifPresent(javaClass -> resolvedClasses.put(javaClass.getFullName(), javaClass)); + } + return resolvedTarget.map(javaClass -> javaClass.getAccessesFromSelf() .stream() .filter(a -> a diff --git a/src/main/resources/archunit.properties b/src/main/resources/archunit.properties new file mode 100644 index 00000000..019def8c --- /dev/null +++ b/src/main/resources/archunit.properties @@ -0,0 +1 @@ +resolveMissingDependenciesFromClassPath=false \ No newline at end of file diff --git a/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java b/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java index 005dd40b..d39b95c5 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java @@ -99,4 +99,9 @@ void test_accessPathTest() { void test_weAccessPath() { tests.assertThatEvents().haveExactly(1, finishedSuccessfully(weAccessPath)); } + + @TestTest + void test_accessFileSystem() { + tests.assertThatEvents().haveExactly(1, testFailedWith("accessFileSystem", SecurityException.class)); + } } diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java index a645fa21..938785e9 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java @@ -105,4 +105,8 @@ void accessPathTest() throws IOException { void weAccessPath() throws IOException { Files.readString(Path.of("pom.xml")); } + + @PublicTest + @Policy(value = "src/test/resources/de/tum/cit/ase/ares/integration/testuser/securitypolicies/NoAllowedPathPolicy.yaml", withinPath = "test-classes/de/tum/cit/ase/ares/integration/testuser/subject/student") + void accessFileSystem() throws IOException {} } diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java index 168c830b..d4bf573e 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java @@ -1,7 +1,5 @@ package de.tum.cit.ase.ares.integration.testuser.subject.pathaccess; -import ch.qos.logback.classic.log4j.XMLLayout; - import java.io.*; import java.nio.file.*; diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/student/FileSystemAccessPenguin.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/student/FileSystemAccessPenguin.java new file mode 100644 index 00000000..fb506f30 --- /dev/null +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/student/FileSystemAccessPenguin.java @@ -0,0 +1,416 @@ +package de.tum.cit.ase.ares.integration.testuser.subject.student; + +import de.tum.cit.ase.ares.integration.testuser.subject.thirdpartypackage.ThirdPartyPackagePenguin; + +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.MidiSystem; +import javax.swing.*; +import java.awt.*; +import java.io.*; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.*; +import java.nio.file.attribute.FileTime; +import java.nio.file.spi.FileSystemProvider; +import java.util.logging.FileHandler; + +/** + * This class is used to access the file system using various classes from the {@link java.nio.file} package. + * It is used to test if Ares can detect and handle file system access correctly. + */ +public class FileSystemAccessPenguin { + + /** + * Access the file system using the {@link FileChannel} class. + */ + void accessFileSystemViaFileChannel() { + try { + FileChannel fileChannel = FileChannel.open(null, null); + fileChannel.read((ByteBuffer) null); + fileChannel.write((ByteBuffer) null); + fileChannel.force(true); + fileChannel.position(0); + fileChannel.size(); + fileChannel.tryLock(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileInputStream} class. + */ + void accessFileSystemViaFileInputStream() { + try { + new FileInputStream("file.txt").read(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileOutputStream} class. + */ + void accessFileSystemViaFileOutputStream() { + // This method is intentionally left blank + try { + new FileOutputStream("file.txt").write(0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileReader} class. + */ + void accessFileSystemViaFileReader() { + // This method is intentionally left blank + try { + FileReader reader = new FileReader("file.txt"); + reader.read(); + reader.read(new char[0]); + reader.read(new char[0], 0, 0); + reader.ready(); + reader.skip(0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileWriter} class. + */ + void accessFileSystemViaFileWriter() { + // This method is intentionally left blank + try { + FileWriter writer = new FileWriter("file.txt"); + writer.write(0); + writer.flush(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link RandomAccessFile} class. + */ + void accessFileSystemViaRandomAccessFile() { + // This method is intentionally left blank + try { + RandomAccessFile file = new RandomAccessFile("file.txt", "rw"); + file.read(); + file.read(new byte[0]); + file.readBoolean(); + file.readByte(); + file.readChar(); + file.readDouble(); + file.readFloat(); + file.readInt(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link BufferedReader} class. + */ + void accessFileSystemViaFile() { + // This method is intentionally left blank + try { + File file = new File("file.txt"); + file.createNewFile(); + file.delete(); + file.exists(); + file.isDirectory(); + file.isFile(); + file.isHidden(); + file.lastModified(); + file.length(); + file.list(); + file.listFiles(); + file.mkdir(); + file.mkdirs(); + file.renameTo(new File("file.txt")); + file.setExecutable(true); + file.setReadable(true); + file.setWritable(true); + file.toPath(); + file.toURI(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileStore} class. + */ + void accessFileSystemViaFileStore() { + // This method is intentionally left blank + try { + FileStore store = Files.getFileStore(null); + store.getTotalSpace(); + store.getUsableSpace(); + store.getUnallocatedSpace(); + store.isReadOnly(); + store.type(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileTime} class. + */ + void accessFileSystemViaFileTime() { + // This method is intentionally left blank + try { + Files.setLastModifiedTime(null, FileTime.fromMillis(0)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link Files} class. + */ + void accessFileSystemViaFiles() { + // This method is intentionally left blank + try { + Files.copy(null, null); + Files.createDirectory(null); + Files.createFile(null); + Files.createLink(null, null); + Files.createSymbolicLink(null, null); + Files.createTempDirectory(null); + Files.createTempFile(null, null); + Files.createTempFile(null, null, null); + Files.createDirectories(null); + Files.createLink(null, null); + Files.createSymbolicLink(null, null); + Files.createTempDirectory(null); + Files.createTempFile(null, null); + Files.createTempFile(null, null, null); + Files.delete(null); + Files.deleteIfExists(null); + Files.exists(null); + Files.isReadable(null); + Files.isWritable(null); + Files.isExecutable(null); + Files.isHidden(null); + Files.getLastModifiedTime(null); + Files.getAttribute(null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileSystemProvider} class. + */ + void accessFileSystemViaFileSystemProvider() { + try { + FileSystemProvider provider = FileSystemProvider.installedProviders().get(0); + provider.delete(Path.of("test.txt")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.FileSystem} class. + */ + void accessFileSystemViaFileSystem() { + // This method is intentionally left blank + try { + FileSystem fileSystem = FileSystems.getDefault(); + fileSystem.close(); + fileSystem.isOpen(); + fileSystem.isReadOnly(); + fileSystem.provider(); + fileSystem.getRootDirectories(); + fileSystem.getFileStores(); + fileSystem.getPathMatcher(null); + fileSystem.getUserPrincipalLookupService(); + fileSystem.newWatchService(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.FileVisitor} class. + */ + void accessFileSystemViaFileVisitor() { + // This method is intentionally left blank + try { + SimpleFileVisitor visitor = new SimpleFileVisitor<>() { + }; + visitor.postVisitDirectory(null, null); + visitor.preVisitDirectory(null, null); + visitor.visitFile(null, null); + visitor.visitFileFailed(null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.Path} class. + */ + void accessFileSystemViaPath() { + // This method is intentionally left blank + try { + Path path = Paths.get("file.txt"); + path.getFileName(); + path.getRoot(); + path.getParent(); + path.getNameCount(); + path.getName(0); + path.subpath(0, 0); + path.startsWith((Path) null); + path.endsWith((Path) null); + path.normalize(); + path.resolve((Path) null); + path.relativize(null); + path.toAbsolutePath(); + path.toRealPath(); + path.toUri(); + path.register(null, null); + path.register(null, null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.Paths} class. + */ + void accessFileSystemViaPaths() { + // This method is intentionally left blank + try { + Paths.get("file.txt"); + Paths.get("file.txt", "file.txt"); + Paths.get("file.txt", "file.txt", "file.txt"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchService} class. + */ + void accessFileSystemViaWatchService() { + // This method is intentionally left blank + try { + WatchService service = FileSystems.getDefault().newWatchService(); + service.close(); + service.poll(); + service.poll(0, null); + service.take(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.Watchable} class. + */ + void accessFileSystemViaWatchable() { + // This method is intentionally left blank + try { + Watchable watchable = Paths.get("file.txt"); + watchable.register(null, null); + watchable.register(null, null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + void accessFileSystemViaDesktop() { + // This method is intentionally left blank + try { + Desktop.getDesktop().browse(null); + Desktop.getDesktop().edit(null); + Desktop.getDesktop().mail(null); + Desktop.getDesktop().open(null); + Desktop.getDesktop().print(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + void accessFileSystemViaFileHandler() { + // This method is intentionally left blank + try { + new FileHandler("file.txt").close(); + new FileHandler("file.txt").flush(); + new FileHandler("file.txt").publish(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + @SuppressWarnings("deprecated") + void accessFileSystemViaSoundBankReader() { + try { + // Get the default SoundbankReader + MidiSystem.getSoundbank(new URL("soundbank.sf2")); + } catch (InvalidMidiDataException | IOException e) { + e.printStackTrace(); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + void accessFileSystemViaJFileChooser() { + // Create a JFrame to hold the file chooser + // Create a JButton to trigger the file chooser + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle("Select a file to open"); + + // Show the file open dialog + // Get the selected file + File selectedFile = fileChooser.getSelectedFile(); + System.out.println("Selected file: " + selectedFile.getAbsolutePath()); + + // Read and print the file contents + try (BufferedReader reader = new BufferedReader(new FileReader(selectedFile))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /** + * Access the file system using a third party library which accesses the file system. + */ + void checkAccessRecursive() throws IOException { + ThirdPartyPackagePenguin.accessFileSystem(); + } + +} diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/thirdpartypackage/ThirdPartyPackagePenguin.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/thirdpartypackage/ThirdPartyPackagePenguin.java new file mode 100644 index 00000000..5fac8f5f --- /dev/null +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/thirdpartypackage/ThirdPartyPackagePenguin.java @@ -0,0 +1,18 @@ +package de.tum.cit.ase.ares.integration.testuser.subject.thirdpartypackage; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * This class is used to emulate a third-party package that is not part of the test user's codebase. + */ +public class ThirdPartyPackagePenguin { + + /** + * This method is used to emulate a third-party package that is not part of the test user's codebase accessing the file system. + */ + public static void accessFileSystem() throws IOException { + Files.readString(Path.of("fileUsingFilesClass.txt")); + } +} From 7ff0b65c022428854c547533220156ad69b7fdcc Mon Sep 17 00:00:00 2001 From: sarps Date: Wed, 31 Jul 2024 21:39:26 +0200 Subject: [PATCH 09/13] Improved subclass exploration --- .../TransitivelyAccessesMethodsCondition.java | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) 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 f76b26b3..19e46721 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 @@ -167,27 +167,34 @@ private Set> getDirectAccessTargetsOutsideOfAnalyzedClasses(JavaAc return Collections.emptySet(); } + Set subclasses = new HashSet<>(item.getTargetOwner().getSubclasses()); + subclasses.add(item.getTargetOwner()); + + Set> accesses = new HashSet<>(); + + 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)); + } - // TODO somehow check the subclasses as well since they are not detected by accessesFromSelf - Optional resolvedTarget; - if (resolvedClasses.get(item.getTargetOwner().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)); } - return resolvedTarget.map(javaClass -> javaClass.getAccessesFromSelf() - .stream() - .filter(a -> a - .getOrigin() - .getFullName() - .equals(item - .getTarget() - .getFullName() - ) - ) - .collect(toSet())).orElseGet(Set::of); + return accesses; } } } From 50c2f403b1e174a051bdf09959f92ce04f888dd2 Mon Sep 17 00:00:00 2001 From: sarps Date: Fri, 2 Aug 2024 00:03:26 +0200 Subject: [PATCH 10/13] Improve subclass exploration do not include generic interfaces add javadoc --- .../java/postcompile/CustomClassResolver.java | 25 +++++--- .../JavaArchitectureTestCaseCollection.java | 4 +- .../TransitivelyAccessesMethodsCondition.java | 64 ++++++++++--------- src/main/resources/archunit.properties | 1 + 4 files changed, 52 insertions(+), 42 deletions(-) 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 From 95779664343425acf30b65083f42abd57e50429d Mon Sep 17 00:00:00 2001 From: sarps Date: Sat, 3 Aug 2024 15:17:30 +0200 Subject: [PATCH 11/13] Add test workflow for Student Submission, which can be used for pre-compile tests etc. --- .github/workflows/maven.yml | 44 +++++++++++++++++++++------------- src/main/resources/logback.xml | 1 + 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c3c37667..fc8bb55b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -15,20 +15,30 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout Repository - uses: actions/checkout@v2 - - - name: Set up JDK 21 - uses: actions/setup-java@v3 - with: - java-version: '21' - distribution: 'adopt' - - - name: Install Maven - run: | - sudo rm -rf /usr/bin/mvn - sudo wget -q -O - "https://apache.osuosl.org/maven/maven-3/3.9.8/binaries/apache-maven-3.9.8-bin.tar.gz" | sudo tar xzf - -C /usr/share - sudo ln -s /usr/share/apache-maven-3.9.8/bin/mvn /usr/bin/mvn - - - name: Build with Maven - run: mvn -B package --file pom.xml + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'adopt' + + - name: Install Maven + run: | + sudo rm -rf /usr/bin/mvn + sudo wget -q -O - "https://apache.osuosl.org/maven/maven-3/3.9.8/binaries/apache-maven-3.9.8-bin.tar.gz" | sudo tar xzf - -C /usr/share + sudo ln -s /usr/share/apache-maven-3.9.8/bin/mvn /usr/bin/mvn + + - name: Build and Test with Maven + run: mvn -B package --file pom.xml + + - name: Run Tests on Student Submission + run: | + git config --global url.https://github.com/.insteadOf git://github.com/ + git clone https://github.com/sarpsahinalp/test-student-submission.git + mkdir test-student-submission/libs + cp target/*.jar test-student-submission/libs + cd test-student-submission + ./gradlew build + ./gradlew test diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 0f2c37ac..8250fafe 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -12,6 +12,7 @@ + From afa652e10aab289fda2586ad0499cf96e6b225a1 Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 5 Aug 2024 03:11:22 +0200 Subject: [PATCH 12/13] Changed properties to not fail on empty should clause --- .../java/postcompile/CustomClassResolver.java | 7 +++---- src/main/resources/archunit.properties | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) 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 2bdb4e8c..c6b2c4bd 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 @@ -3,7 +3,6 @@ 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.util.Optional; @@ -11,7 +10,6 @@ * Custom class resolver to resolve classes that are outside classpath to be able to analyze them transitively. */ public class CustomClassResolver { - /** * Class file importer to import the class files. * This is used to import the class files from the URL. @@ -19,9 +17,10 @@ public class CustomClassResolver { private final JavaClasses allClasses; public CustomClassResolver() { + // We need to import all classes to be able to resolve them later. + // TODO: We definitely need to improve this. We should not import all classes as it is not memory efficient. + // https://www.javadoc.io/doc/com.tngtech.archunit/archunit/0.10.2/com/tngtech/archunit/core/importer/ClassFileImporter.html allClasses = new ClassFileImporter() - .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) - .withImportOption(location -> location.toString().contains("jrt:/")) .importClasspath(); } diff --git a/src/main/resources/archunit.properties b/src/main/resources/archunit.properties index a536fac5..cd4e78b0 100644 --- a/src/main/resources/archunit.properties +++ b/src/main/resources/archunit.properties @@ -1,2 +1,3 @@ # 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 +resolveMissingDependenciesFromClassPath=false +archRule.failOnEmptyShould=false \ No newline at end of file From b20640521a3349d50c515cea15e9f85bc5587555 Mon Sep 17 00:00:00 2001 From: sarps Date: Mon, 5 Aug 2024 18:14:30 +0200 Subject: [PATCH 13/13] Added Javadocs --- .../java/postcompile/CustomClassResolver.java | 1 + .../postcompile/TransitivelyAccessesMethodsCondition.java | 5 +++++ 2 files changed, 6 insertions(+) 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 c6b2c4bd..a7a8ea6a 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 @@ -32,6 +32,7 @@ public CustomClassResolver() { */ public Optional tryResolve(String typeName) { try { + // Try to resolve the class by the given type name. 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/TransitivelyAccessesMethodsCondition.java b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java index 1380c407..48f39b53 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 @@ -175,6 +175,11 @@ private Set> getDirectAccessTargetsOutsideOfAnalyzedClasses(JavaAc Set subclasses = resolvedTarget.getSubclasses().stream().map(this::resolveTargetOwner).collect(toSet()); subclasses.add(resolvedTarget); + /** + * If the number of subclasses is more than 20, return an empty set. + * These classes are always generic interfaces or abstract classes + * TODO: Check if this is also the case for foreign packages + */ if (subclasses.size() > 20) { return Collections.emptySet(); }