デバッグ目的でJavaプログラム内から任意のクラス(利用可能な場合)のソースコードを取得しようとしています。たとえば、_Class[_]
_の参照があり、ソースコードを取得します。
私がこれまでに試みたこと-Scala:
_val clazz = classOf[ClassDefinedInSeparateFile]
_
clazz.getProtectionDomain.getCodeSource.getLocation.toString + "/" + clazz.getPackage.getName.replaceAll("\\.","/") + "/" + clazz.getSimpleName + ".scala"
-正常に見えます。JARはあり、_.scala
_ファイルが含まれていますが、Source.fromFile(...)
を使用して開くことができませんでした。"/" + clazz.getPackage.getName.replaceAll("\\.","/") + "/" + clazz.getSimpleName + ".scala"
-正常に見えますが、Source.fromInputStream(...)
を使用して開くことができませんでした備考:
.Java
_または_.scala
_ファイルが含まれているため、逆コンパイラは必要ありません。 (少なくとも、アプリケーションのソースコードについては、依存関係は対象外です。アプリケーションのソースコードからスニペットにアクセスできる場合は、これで十分です。ほとんどの例外はアプリケーションレベルでキャッチされ、そこで関連します。)ありがとう。
ソースがクラスパスにあるjar内にある場合は、正確な場所を見つける必要があります。
clazz.getName.replaceAll("\\.", "/") + ".scala"
は大丈夫ですbut:(1)ソースコードがクラスと同じ場所にない可能性があります-プレフィックス(src/
など)がある可能性があります何でも)、または別のjarにある可能性もあります。(2)scalaクラスは同じ名前のファイルにある必要はありません-1つのファイルに複数のクラスを含めることができます。ファイルはfoo.scala
と呼ばれ、一部のクラスはオンザフライで生成されます。また、パッケージは必ずしもscalaのディレクトリではありません(たとえば、パッケージオブジェクトの場合もあります)。
Jar内の場所(およびjarがクラスパス内)がわかっている場合、開く方法はclazz.getClassLoader.getResourceAsStream
...ですが、上で述べたように、トリックは場所を特定することです。それは簡単ではありません(それを行うための単一の標準的な方法はありません)。
あなたの最善の策は、確かにIDEを使用することです。本番環境では利用できないとのことですが、実際には必要ありません。必要なのは、IDEがインストールされている一部のマシンで利用可能な本番環境のソースコードであり、シンプルなscp
コマンドで実行できます。
ソースをクラスと同じフォルダーに単に配置した場合、以下のコードはクラスパスとjarの両方のコードで正常に機能しました
名前の前に「/」を付ける理由はありませんが、トップレベルのクラスをスキャンする必要があることに注意してください。
コアにあることをお詫びしますJavaですが、追加の依存関係を追加したくなく、できるだけ明確にしたかったのです。
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 + "]";
}
}
}
}
Javaの元のソースコードに似たものを取得する場合は、Java Decompilerが必要です。
ソースコードにアクセスできなくなります(実行可能プログラムは、Javaソースコード)ではなく Javaバイトコード で構成されています)。
これが私の favorite Java Decompiler へのリンクです。