java.io.InvalidClassException を取得すると、必要な serialVersionUID と取得した serialVersionUID が返されます。何十もの jar のどれが間違った serialVersionUID を使用しているかを簡単に確認する方法はありますか?
更新: 私たちの意図はすべてを同時に更新することですが、ビルドとデプロイのプロセスで問題をデバッグしようとしています。
java.io.InvalidClassException を取得すると、必要な serialVersionUID と取得した serialVersionUID が返されます。何十もの jar のどれが間違った serialVersionUID を使用しているかを簡単に確認する方法はありますか?
更新: 私たちの意図はすべてを同時に更新することですが、ビルドとデプロイのプロセスで問題をデバッグしようとしています。
jar内のクラスごとにsunjdkのserialverツールを使用します。
さまざまな JVM/コンテナ/クラスローダーの組み合わせによって、どのクラスを bootclasspath からロードするか、アプリケーション/webapp クラスパスからロードするかについて意見が異なることに注意してください。
これは、serialver が常に最初に bootclasspath から読み込まれるという事実によって複雑になるため、異なる動作をシミュレートするには、以下のように -J-Xbootclasspath を使用する必要がある場合があります。
f=/System/Library/Frameworks/JavaVM.framework/Versions/1.5/Classes/
serialver -J-Xbootclasspath:.:$f/dt.jar:$f/classes.jar:$f/ui.jar javax.xml.namespace.QName
もう 1 つの方法は、次のように javap を使用することです。
javap -verbose -bootclasspath . javax.xml.namespace.QName | sed -n -e '/static.*serialVersionUID/{N;p;}'
SOのいくつかの回答に基づいて、これを実行するためのユーティリティを思いつきました。これが私のクラスです。
package jar;
import java.io.File;
import java.io.IOException;
import java.io.ObjectStreamClass;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Searches all the jars in a given directory for a class with the given UID.
* Additional directories can be specified to support with loading the jars.
* For example, you might want to load the lib dir from your app server to get
* the Servlet API, etc.
*/
public class JarUidSearch
{
public static void main(String args[])
throws IOException, ClassNotFoundException
{
if( args.length < 2)
{
System.err.println("Usage: <UID to search for> <directory with jars to search> [additional directories with jars]");
System.exit(-1);
}
long targetUID = Long.parseLong(args[0]);
ArrayList<URL> urls = new ArrayList<URL>();
File libDir = new File(args[1]);
for (int i = 1; i < args.length; i++)
{
gatherJars(urls, new File(args[i]));
}
File[] files = libDir.listFiles();
for (File file : files)
{
try
{
checkJar(targetUID, urls, file);
}
catch(Throwable t)
{
System.err.println("checkJar for " + file + " threw: " + t);
t.printStackTrace();
}
}
}
/**
*
* @param urls
* @param libDir
* @throws MalformedURLException
*/
public static void gatherJars(ArrayList<URL> urls, File libDir)
throws MalformedURLException
{
File[] files = libDir.listFiles();
for (File file : files)
{
urls.add(file.toURL());
}
}
/**
*
* @param urls
* @param file
* @throws IOException
* @throws ClassNotFoundException
*/
public static void checkJar(long targetUID, ArrayList<URL> urls, File file)
throws IOException, ClassNotFoundException
{
System.out.println("Checking: " + file);
JarFile jarFile = new JarFile(file);
Enumeration allEntries = jarFile.entries();
while (allEntries.hasMoreElements())
{
JarEntry entry = (JarEntry) allEntries.nextElement();
String name = entry.getName();
if (!name.endsWith(".class"))
{
// System.out.println("Skipping: " + name);
continue;
}
try
{
URLClassLoader loader = URLClassLoader.newInstance((URL[]) urls.toArray(new URL[0]));
String className = name.substring(0,
name.length() - ".class".length()).replaceAll("/", ".");
Class<?> clazz = loader.loadClass(className);
ObjectStreamClass lookup = ObjectStreamClass.lookup(clazz);
if (lookup != null)
{
long uid = lookup.getSerialVersionUID();
if (targetUID == uid)
{
System.out.println(file + " has class: " + clazz);
}
}
}
catch (Throwable e)
{
System.err.println("entry " + name + " caused Exception: " + e);
}
}
}
}
この種の問題に対処する最善の方法は、サーバー側とクライアント側で同時に jar を更新することです。これにより、両側で同じバージョンのクラスが保証され、シリアライズ/デシリアライズ時に問題が発生することはありません。この問題が発生するたびにシリアル UID を追跡しても、何の解決にもなりません。かなりの時間とリソースを浪費するだけです。時間をかけて適切な展開/パッケージ化戦略を実装することをお勧めします。
本当に他に選択肢がない場合は、(URLClassLoader を使用して) 各 jar からクラスをロードするツールを作成し、それを使用java.io.ObjectStreamClass.getSerialVersionUID()
して必要な情報を取得できます。
不器用だがうまくいく
リバース エンジニアリング ツールを使用します
。1) jar を解凍します
。2) クラス ファイル ツリーで、たとえば jad を実行します
。3) この新しいソース ツリーで grep またはその他の検索ツールを実行して、シリアル バージョンの uid を探します。