[JAVA] Ich habe den Paketnamen überprüft, auf den die in der JAR-Datei enthaltene Klasse verweist

Überblick

In diesem Eintrag werde ich eine Notiz hinterlassen, was ich versucht habe:

Annahme

Quelle

Was ich geschrieben habe, ist GitHub [https://github.com/hrkt/gohatto-j/releases/tag/0.0.1-qiita](https://github.com/hrkt/gohatto-j/releases/tag/ Ich habe es in 0.0.1-Qiita) gesetzt.

Angenommene Szene

――Der Leser ist für die Programmierausbildung mit Java verantwortlich, und der Auszubildende muss eine JAR-Datei der Übung einreichen und den Vorgang überprüfen.

Was ich versucht habe

Schauen Sie sich den Inhalt der JAR-Datei an

Es gibt eine Klasse namens JarFile, daher werde ich diese Kraft nutzen.

Die Aufzählung kann mit Einträgen () abgerufen werden, sodass der Inhalt der Reihe nach festgelegt wird. Es ist praktisch, dass Sie den Verzeichnisneustartprozess nicht selbst schreiben müssen.

        try (JarFile jarFile = new JarFile(jarFilePath)) {
            val e = jarFile.entries();

            val urls = new URL[]{new URL("jar:file:" + jarFilePath + "!/")};
            val ucl = URLClassLoader.newInstance(urls);
            val repository = new ClassLoaderRepository(ucl);

            return Collections.list(e).stream()
                    .filter(je -> !je.isDirectory())
                    .filter(je -> je.getName().endsWith(".class"))
                    .flatMap(je -> check(repository, je).stream())

Untersuchen Sie den Code in der Klassendatei mit BCEL

BCEL

Die Java-Standardfunktion Reflection benötigt diese Zeit. Sie können keine Informationen wie die Klasse abrufen, auf die sich der Code in der Klassendatei bezieht (nachdem er in Bytecode kompiliert wurde).

Daher verwenden wir Apache Commons BCEL, eine Bibliothek für den Umgang mit Java-Bytecode.

Verwenden Sie die Commons JavaClass-Klasse, um die vom Java-Klassenladeprogramm oben erhaltene Klasse zu laden und den darin enthaltenen Bytecode anzuzeigen. Insbesondere BCELs "ConstantPool" (= analysierte Klassendatei) Untersuchen Sie die Zeichenfolge in der Liste der Konstanten in).

Testcode zur Erkennung

Ich habe den folgenden Code geschrieben, eine JAR-Datei erstellt und es versucht.

package io.hrkt;


import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("message", "Hello");
        System.out.println(map.get("message"));
    }
}

Wenn Sie zu diesem Zeitpunkt den konstanten Pool in BCEL ausschließen, können Sie die folgende Liste abrufen.

CONSTANT_Methodref[10](class_index=12,name_and_type_index=30)
CONSTANT_Class[7](name_index=31)
CONSTANT_Methodref[10](class_index=2,name_and_type_index=30)
CONSTANT_String[8](string_index=32)
CONSTANT_String[8](string_index=33)
CONSTANT_InterfaceMethodref[11](class_index=34,name_and_type_index=35)
CONSTANT_Fieldref[9](class_index=36,name_and_type_index=37)
CONSTANT_InterfaceMethodref[11](class_index=34,name_and_type_index=38)
CONSTANT_Class[7](name_index=39)
CONSTANT_Methodref[10](class_index=40,name_and_type_index=41)
CONSTANT_Class[7](name_index=42)
CONSTANT_Class[7](name_index=43)
CONSTANT_Utf8[1]("<init>")
CONSTANT_Utf8[1]("()V")
CONSTANT_Utf8[1]("Code")
CONSTANT_Utf8[1]("LineNumberTable")
CONSTANT_Utf8[1]("LocalVariableTable")
CONSTANT_Utf8[1]("this")
CONSTANT_Utf8[1]("Lio/hrkt/Main;")
CONSTANT_Utf8[1]("main")
CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
CONSTANT_Utf8[1]("args")
CONSTANT_Utf8[1]("[Ljava/lang/String;")
CONSTANT_Utf8[1]("map")
CONSTANT_Utf8[1]("Ljava/util/Map;")
CONSTANT_Utf8[1]("LocalVariableTypeTable")
CONSTANT_Utf8[1]("Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;")
CONSTANT_Utf8[1]("SourceFile")
CONSTANT_Utf8[1]("Main.java")
CONSTANT_NameAndType[12](name_index=13,signature_index=14)
CONSTANT_Utf8[1]("java/util/HashMap")
CONSTANT_Utf8[1]("message")
CONSTANT_Utf8[1]("Hello")
CONSTANT_Class[7](name_index=44)
CONSTANT_NameAndType[12](name_index=45,signature_index=46)
CONSTANT_Class[7](name_index=47)
CONSTANT_NameAndType[12](name_index=48,signature_index=49)
CONSTANT_NameAndType[12](name_index=50,signature_index=51)
CONSTANT_Utf8[1]("java/lang/String")
CONSTANT_Class[7](name_index=52)
CONSTANT_NameAndType[12](name_index=53,signature_index=54)
CONSTANT_Utf8[1]("io/hrkt/Main")
CONSTANT_Utf8[1]("java/lang/Object")
CONSTANT_Utf8[1]("java/util/Map")
CONSTANT_Utf8[1]("put")
CONSTANT_Utf8[1]("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
CONSTANT_Utf8[1]("java/lang/System")
CONSTANT_Utf8[1]("out")
CONSTANT_Utf8[1]("Ljava/io/PrintStream;")
CONSTANT_Utf8[1]("get")
CONSTANT_Utf8[1]("(Ljava/lang/Object;)Ljava/lang/Object;")
CONSTANT_Utf8[1]("java/io/PrintStream")
CONSTANT_Utf8[1]("println")
CONSTANT_Utf8[1]("(Ljava/lang/String;)V")

Code

Von den in ConstantPool enthaltenen Zeichenfolgen werden diejenigen extrahiert, deren Tag eine Klassendatei angibt.

        JavaClass clazz = null;
        try {
            clazz = repository.loadClass(fqcn);
            val cp = clazz.getConstantPool();
            int cpLength = cp.getLength();

            val referencedClasses = new ArrayList<ConstantUtf8>();
            IntStream.range(0, cpLength).forEach(i -> {
                val constant = cp.getConstant(i);
                if (null == constant || !(constant instanceof ConstantClass)) {
                    // do nothing
                } else {
                    val constantClass = (ConstantClass) constant;
                    val referencedConstant = cp.getConstant(constantClass.getNameIndex());
                    referencedClasses.add((ConstantUtf8) referencedConstant);
                }
            });
            val tc = referencedClasses.stream()
                    .map(c -> c.getBytes())
                    .map(s -> s.replace("/", "."))
                    .collect(Collectors.toList());
            return tc;
        } catch (ClassNotFoundException e) {
            throw new GohattoApplicationException(e);
        }

Inspektionsregeln

Dieses Mal wird es ermittelt, indem es mit dem Paketnamen abgeglichen wird, der in der Einstellungsdatei für das Textformat enthalten ist.

Wie Sie anhand des Codes sehen können, handelt es sich lediglich um einen Vergleich mit regulären Ausdrücken.

Muster 1

Wenn Sie eine solche Datei vorbereiten, wird sie als Fehler erkannt, wenn "java.util oder com.sun verwendet wird".

java.util
com.sun

Muster 2

Wenn Sie eine solche Datei vorbereiten, wird sie als Fehler erkannt, "wenn Sie etwas anderes als java.lang oder java.io verwenden".

^(?!java.lang|java.io)

Ausführungsbeispiel

Im folgenden Beispiel wird die Quelle im Repository verwendet.

Als Befehl

Wenn bei der Ausführung als Befehl Code vorhanden ist, der keine Java-Paketreferenz zulässt, wird ein Fehlerprotokoll wie das unten gezeigte an die Standardausgabe ausgegeben.

$ java -jar build/libs/gohatto-j-0.0.1-SNAPSHOT-all.jar --jar src/test/resources/testproject.jar --forbiddenPackageList  src/test/resources/rule_forbid_jav-util_and_com-sun.txt
[main] INFO io.hrkt.gohatto.Gohatto - ERROR     DoNotUseForbiddenPackage        java.util.HashMap violates rule
[main] INFO io.hrkt.gohatto.Gohatto - ERROR     DoNotUseForbiddenPackage        java.util.Map violates rule
[main] INFO io.hrkt.gohatto.Gohatto - rules applied to: 2

In JUnit

In der "Gohatto" -Klasse im oben genannten Repository, das der Eingang zur Prüffunktion ist,

        String resDir = "src/test/resources/";
        Gohatto gohatto = new Gohatto();
        gohatto.init(resDir + "testproject.jar", resDir + "rule_forbid_jav-util_and_com-sun.txt");
        List<Result> ret = gohatto.executeRules();
        Assert.assertTrue(ret.size() == 0);

Zusammenfassung

Ich habe Ihnen gezeigt, wie Sie die JAR-Datei lesen und das Paket der vorherigen Klasse überprüfen, auf die die Klasse darin verweist.

Der Arbeitscode wird im Repository gespeichert. Bitte beziehen Sie sich darauf.

Recommended Posts

Ich habe den Paketnamen überprüft, auf den die in der JAR-Datei enthaltene Klasse verweist
Benennen Sie den Paketnamen einer vorhandenen JAR-Datei um
[Gradle] Die Geschichte, dass die Klassendatei nicht in der JAR-Datei vorhanden war
Fügen Sie dem JAR-Dateinamen in Gradle einen Zeitstempel hinzu
Nach mehreren Feldern in der Klasse sortieren
Verhindern von Fehlern im Logger-Namen durch Kopieren
Wenn Sie sich im Klassennamen verlieren
Benennen und rufen Sie in Ruby übergeordnete Klassenmethoden explizit auf
Rufen Sie den Namen des Testfalls in der JUnit-Testklasse ab
Fügen Sie den von Doma generierten Quellcode in die Quell-JAR ein
[Java] Holen Sie sich die Datei unabhängig von der Umgebung in das JAR
So debuggen Sie die generierte JAR-Datei mit Eclipse
Ich möchte dem select-Attribut einen Klassennamen geben
Ich möchte die Liste der Klassen unter dem Paket rekursiv durchsuchen
Ich blieb in File stecken
Ich kann in IntelliJ keine Java-Klasse mit einem bestimmten Namen erstellen
Verwenden Sie eine Beispieldomäne für den Paketnamen im Beispielcode
Geben Sie das Datum (z. B. JJJJ-MM-TT) in die Protokolldatei ein, die per Logback ausgegeben wird
Korrigieren Sie den Namen der Kriegsdatei auf den in Maven festgelegten