Найдите, какой класс, в котором jar имеет заданный serialVersionUID

#java #serialversionuid

Вопрос:

Когда я получаю исключение java.io.InvalidClassException, оно выдает мне идентификатор serialVersionUID, который ему нужен, и идентификатор serialVersionUID, который он получил. Есть ли простой способ определить, какая из моих десятков банок использует неправильный serialVersionUID?

Обновление: Я должен упомянуть, что мы намерены обновлять все одновременно, но я пытаюсь устранить проблему в процессе сборки и развертывания.

Комментарии:

1. Банки не используют serialVersionUIDs. Занятия проходят. Класс назван в сообщении об ошибке, следовательно, все, что вам нужно сделать, это найти класс.

2. И в любом случае этот вопрос на самом деле не имеет смысла. Ничто не мешает вам использовать одно и то же значение, например 1L, для каждого класса в файле JAR или для всего вашего предприятия. Непонятно, о чем ты спрашиваешь.

Ответ №1:

используйте инструмент serialver из sun jdk для каждого класса в jar.

Ответ №2:

Лучший способ справиться с такого рода проблемами-обновлять банки на стороне сервера и клиента одновременно. Это гарантирует одинаковую версию ваших классов с обеих сторон, и у вас не возникнет проблем при сериализации / десериализации. Отслеживание последовательных UID каждый раз, когда у вас возникает эта проблема, ничего не решит, вы только потратите значительное время и ресурсы. Гораздо лучше потратить некоторое время и реализовать правильную стратегию развертывания / упаковки.

Если у вас действительно нет другого выбора, вы можете написать инструмент, который загружает класс из каждой банки (используя загрузчик urlclass), а затем использовать java.io.ObjectStreamClass.getSerialVersionUID() для получения необходимой вам информации.

Ответ №3:

Имейте в виду, что различные комбинации JVM/контейнера/загрузчика классов имеют разные мнения о том, какие классы следует загружать из пути к классу начальной загрузки, а не из пути к классу приложения/веб-приложения.

Это осложняется тем фактом, что serialver всегда загружается сначала из пути начальной загрузки, поэтому вам может потребоваться использовать-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
 

Другой подход заключается в использовании javap, например:

 javap -verbose -bootclasspath . javax.xml.namespace.QName | sed -n -e '/static.*serialVersionUID/{N;p;}'
 

Ответ №4:

Я придумал утилиту для этого, основанную на некоторых ответах в 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);
            }
        }
    }
}
 

Ответ №5:

неуклюже, но работает

я бы использовал инструмент обратного проектирования
1) распакуйте банки
2) запустите, скажем, jad в дереве файлов классов
3) запустите grep или любой другой инструмент поиска в этом новом дереве исходного кода для поиска uid серийной версии.

Ответ №6:

Я использовал эту команду в Linux для проверки всех банок в текущем каталоге:

 find . -type f -name "*.jar" -exec sh -c 'javap -verbose -p -constants -cp {} com.myCompany.myClass | grep serialVersionUID' ;
 

Пожалуйста, имейте в виду, что serialVersionUID может быть не объявлен в вашем классе явно. В этом случае serialVersionUID будет вычислен во время выполнения и не может быть найден с помощью команды find выше.