我相信我已经看过这个问题上的所有其他帖子。我认为这是一个不同的问题,因为我正在寻找一种方法来确定导致问题的类。
我使用Maven构建我的jar。
如果我要求它为Java 5构建并且我在Java 6下运行它可以正常工作。
如果我要求它为Java 6构建并且我在Java 6下运行它可以正常工作。
如果我要求它为Java 5构建并且我在Java 5下运行它就会失败:
java.lang.RuntimeException: public static void ....main(java.lang.String[]) failed for arguments (String[]{...})
Caused by: java.lang.reflect.InvocationTargetException:
...
Caused by: java.lang.UnsupportedClassVersionError: Bad version number in .class file
at java.lang.ClassLoader.defineClass1(Native Method)
...
我使用Java Version Check检查了jar,它报告了jar中为Java 5构建的所有类。
我只能得出结论,它是黑客攻击它自己的类路径并连接到我的机器上不是Java 5的其他库/ jar。这很可能但我无法访问该进程的源代码据我所知,我无法单步进入类加载器以找出它正在加载的类。
我可以使用哪些技巧来帮助我找出哪个类导致异常?
答案 0 :(得分:3)
捕获几乎所有类定义的一种方法是使用Instrumentation API使用 Java代理。此API允许在使用之前转换所有字节代码(某些核心类除外),但当然,您可以使用它来提取版本号并生成报告而不更改字节代码。
类文件转换的一个不错的属性是JVM将在拒绝格式错误/不支持的字节代码之前调用转换器,以使它们有机会将代码转换为可用的代码。因此,我们将在抛出UnsupportedClassVersionError
之前看到负责的字节代码。
以下是完成工作的Java代理的代码:
package versioncheck;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class CheckAgent implements ClassFileTransformer
{
public static void premain(String agentArgs, Instrumentation inst)
{
inst.addTransformer(new CheckAgent(), inst.isRetransformClassesSupported());
}
public static void agentmain(String agentArgs, Instrumentation inst)
{
inst.addTransformer(new CheckAgent(), inst.isRetransformClassesSupported());
}
private int supported;
CheckAgent()
{
supported=(int)Double.parseDouble(System.getProperty("java.class.version"));
}
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException
{
int version=(classfileBuffer[6]&0xff)<<8 | (classfileBuffer[7]&0xff);
if(version>supported)
System.out.println(className+" v"+version+" from "
+protectionDomain.getCodeSource());
return null;
}
}
然后你需要将这个类与包含以下行的清单一起放在一个罐子里:
Premain-Class: versioncheck.CheckAgent
Agent-Class: versioncheck.CheckAgent
Can-Redefine-Classes: true
最后要做的是将以下参数添加到启动Java应用程序的命令行:-javaagent:pathtothe.jar
另见http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html
作为旁注,这样的Java代理也可以通过将字节代码转换(向下移植)到较低版本来解决问题。但这远远超出了最初的问题......
答案 1 :(得分:0)
要检查类路径中的JVM版本JAR,我会做类似下面的配方。但正如我在上一篇评论中已经提到的,这个解决方案不包括JAR有一个自定义类加载器修改类路径并做其他邪恶事情的情况。不过,您可以尝试使用此解决方案,看看它是否有帮助。如果不是,则需要更复杂的解决方案......
检索应用程序的完整类路径。通常我会使用Maven AppAssembler执行此步骤,但肯定还有其他方法。 AppAssembler很方便,因为它构建了一个显式指定类路径的shell脚本,所以你可以简单地复制粘贴它。
以classpath作为参数运行以下bash脚本。该脚本在内部使用file
,unzip
和bc
,因此您应该安装这些工具。
剧本:
#!/bin/sh -e
# force numeric to use decimal point
LC_NUMERIC=C
export LC_NUMERIC
tempdir=
cleanup() {
trap - EXIT
if test -n "$tempdir"; then
rm -rf "$tempdir" ||:
fi
exit "$@"
}
debug() {
test -z "$DEBUG" || echo "$@" >&2 ||:
}
get_jar_version() {
local ver maxVersion compare
maxVersion=0.0
for ver in $(find "$1" -type f -name '*.class' -exec file {} \; | grep 'Java class data, version' | sed -e 's;.*Java class data, version ;;'); do
debug "Version = '$ver'"
compare=$(echo "$ver > $maxVersion" | bc)
if [ "x$compare" = "x1" ]; then
maxVersion="$ver"
fi
done
debug "maxVersion=$maxVersion"
echo -n "$maxVersion"
}
process_classpath_jars() {
trap cleanup EXIT
local cp jar oldIFS maxVersion
oldIFS="$IFS"
for cp in "$@"; do
IFS=':'
for jar in $cp; do
if [ -z "$jar" -o "${jar%.jar}" = "$jar" ]; then
continue
fi
debug "processing JAR $jar"
tempdir=$(mktemp -d jar.XXXXXXX)
unzip -qq "$jar" -d "$tempdir"
IFS="$oldIFS"
maxVersion=$(get_jar_version "$tempdir")
rm -rf "$tempdir" ||:
tempdir=
IFS=':'
echo "$jar is compiled for $maxVersion JVM"
done
IFS="$oldIFS"
done
}
process_classpath_jars "$@"