wake-up-neo.net

Rufen Sie Quellcode einer beliebigen Klasse aus einem Java-Programm ab

Ich versuche, den Quellcode einer Klasse (falls verfügbar) aus einem Java-Programm zu Debugging-Zwecken abzurufen. Nehmen wir an, ich habe die Referenz von Class[_], auf die ich den Quellcode abrufen möchte.

Was ich bisher versucht habe - in Scala:

val clazz = classOf[ClassDefinedInSeparateFile]

  1. clazz.getProtectionDomain.getCodeSource.getLocation.toString + "/" + clazz.getPackage.getName.replaceAll("\\.","/") + "/" + clazz.getSimpleName + ".scala" - sieht in Ordnung aus, die JAR-Datei ist vorhanden und enthält die .scala-Datei, konnte jedoch nicht mit Source.fromFile(...) geöffnet werden.
  2. "/" + clazz.getPackage.getName.replaceAll("\\.","/") + "/" + clazz.getSimpleName + ".scala" - sieht in Ordnung aus, konnte jedoch nicht mit Source.fromInputStream(...) geöffnet werden

Bemerkungen:

  • In Produktions- oder Staging-Umgebungen ist kein IDE verfügbar.
  • In unserer Einstellung enthalten JARs die Quelltextdateien .Java oder .scala, daher ist kein Dekompiler erforderlich. (Zumindest für den Quellcode der Anwendung, nicht aber für die Abhängigkeiten. Wenn ein Ausschnitt des Anwendungsquellcodes verfügbar ist, reicht das aus - die meisten Ausnahmen werden auf Anwendungsebene abgefangen und sind dort relevant.)

Vielen Dank.

11
Dyin

Befindet sich die Quelle im Glas, das sich im Klassenpfad befindet, müssen Sie herausfinden, wo sich die Quelle genau befindet. 

clazz.getName.replaceAll("\\.", "/") + ".scala" ist eine gute Vermutung, aber : (1) Quellcode befindet sich möglicherweise nicht an derselben Stelle wie die Klassen. Möglicherweise gibt es ein Präfix (wie src/ oder was auch immer). und (2) scala-Klassen müssen nicht in Dateien mit demselben Namen enthalten sein - Sie können mehrere Klassen in einer Datei enthalten, die Datei kann foo.scala heißen, einige Klassen werden fliegend generiert usw. Ein Paket ist jedoch nicht immer verfügbar ein Verzeichnis in scala (es könnte zum Beispiel ein Paketobjekt sein). 

Wenn Sie wissen, wo sich die Dose befindet (und die Dose befindet sich im Klassenpfad), können Sie sie wie folgt öffnen: clazz.getClassLoader.getResourceAsStream ... aber wie oben erwähnt, besteht der Trick darin, die Position herauszufinden. Es ist nicht einfach (und es gibt keine Standardmethode). 

Am besten verwenden Sie eine IDE. Ich verstehe, dass Sie es in der Produktionsumgebung nicht zur Verfügung haben, aber Sie brauchen das nicht wirklich. Was Sie brauchen, ist der Produktionsquellcode, der auf einer Maschine verfügbar ist, auf der Sie eine IDE haben, und die Sie mit einem einfachen scp-Befehl erreichen können.

3
Dima

Wenn ich die Quelle einfach in demselben Ordner wie die Klasse abgelegt habe, funktionierte der folgende Code sowohl für den Code im Klassenpfad als auch in Gläsern

Beachten Sie, dass es keinen Grund gibt, dem Namen "/" vorangestellt zu werden. Sie sollten jedoch nach der obersten Klasse suchen.

Ich entschuldige mich dafür, dass es sich im Kern von Java befindet, aber ich wollte keine zusätzlichen Abhängigkeiten hinzufügen und wollte so klar wie möglich sein. 

package com.stackoverflow.q53749060;

import Java.io.ByteArrayOutputStream;
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.UncheckedIOException;
import Java.util.Arrays;
import Java.util.stream.Stream;

import org.junit.Test;

import com.stackoverflow.q53749060.Answer.Result.Found;
import com.stackoverflow.q53749060.Answer.Result.NotFound;
import com.stackoverflow.q53749060.MyTopLevelClass.MyNestedClass;
import com.stackoverflow.q53749060.MyTopLevelClassInAnotherJar.MyNestedClassInAnotherJar;

@SuppressWarnings("javadoc")
public class Answer {

    static final String[] EXTENSIONS = { "Java", "scala" };

    @Test
    public void test() {

        Arrays.stream(EXTENSIONS)
            .flatMap(ext -> toSource(ext, MyTopLevelClass.class, MyNestedClass.class,MyTopLevelClassInAnotherJar.class,MyNestedClassInAnotherJar.class, String.class))
            .forEach(System.out::println);

    }

    public Stream<Result> toSource(final String extension, final Class<?>... classes) {

        return Arrays.stream(classes)
            .map(clazz -> toSource(extension, clazz));
    }

    public Result toSource(final String extension, final Class<?> clazz) {

        Class<?> topLevelClass = clazz;

        while (topLevelClass.getEnclosingClass() != null) {
            topLevelClass = topLevelClass.getEnclosingClass();
        }

        final String name = topLevelClass.getName()
            .replaceAll("\\.", "/") + "." + extension;

        final Thread currentThread = Thread.currentThread();

        final ClassLoader contextClassLoader = currentThread.getContextClassLoader();

        if (contextClassLoader.getResource(name) == null) {
            return new NotFound(clazz);
        }

        final String source = toSource(name, contextClassLoader);

        return new Found(clazz, name, source);
    }

    public String toSource(final String name, final ClassLoader contextClassLoader) {

        try (final InputStream resourceInputStream = contextClassLoader.getResourceAsStream(name);
                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {

            int length;
            byte[] data = new byte[1024];

            while ((length = resourceInputStream.read(data, 0, data.length)) != -1) {
                byteArrayOutputStream.write(data, 0, length);
            }

            byteArrayOutputStream.flush();

            byte[] byteArray = byteArrayOutputStream.toByteArray();

            return new String(byteArray);

        } catch (IOException ioe) {
            throw new UncheckedIOException("Failed to read source file: " + name, ioe);
        }
    }

    static class Result {

        final Class<?> clazz;

        Result(Class<?> clazz) {
            super();
            this.clazz = clazz;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((this.clazz == null) ? 0 : this.clazz.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            Result other = (Result) obj;
            if (this.clazz == null) {
                if (other.clazz != null) {
                    return false;
                }
            } else if (!this.clazz.equals(other.clazz)) {
                return false;
            }
            return true;
        }

        @Override
        public String toString() {
            return "Result [clazz=" + this.clazz + "]";
        }

        static class Found extends Result {

            final String source;

            final String path;

            Found(Class<?> clazz, String path, String source) {
                super(clazz);
                this.path = path;
                this.source = source;
            }

            @Override
            public int hashCode() {
                final int prime = 31;
                int result = super.hashCode();
                result = prime * result + ((this.source == null) ? 0 : this.source.hashCode());
                return result;
            }

            @Override
            public boolean equals(Object obj) {
                if (this == obj) {
                    return true;
                }
                if (!super.equals(obj)) {
                    return false;
                }
                if (getClass() != obj.getClass()) {
                    return false;
                }
                Found other = (Found) obj;
                if (this.source == null) {
                    if (other.source != null) {
                        return false;
                    }
                } else if (!this.source.equals(other.source)) {
                    return false;
                }
                return true;
            }

            @Override
            public String toString() {
                return "Found [source=" + this.source + ", clazz=" + this.clazz + "]";
            }

        }

        static class NotFound extends Result {

            NotFound(Class<?> clazz) {
                super(clazz);

            }

            @Override
            public String toString() {
                return "NotFound [clazz=" + this.clazz + "]";
            }

        }
    }
}
1
Jeff

Sie benötigen einen Java-Decompiler, wenn Sie etwas Ähnliches zum ursprünglichen Quellcode in Java erhalten möchten.

Sie können nicht auf den Quellcode zugreifen (Ihr ausführbares Programm besteht aus Java-Bytecode , nicht aus Java-Quellcode).

Hier ist ein Link zu meinem Favoriten Java Decompiler .

0