Pregunta

Cuando obtengo un java.io.InvalidClassException, me da el serialVersionUID que quiere y el serialVersionUID que obtuvo. ¿Hay una manera fácil de saber cuál de mis docenas de frascos usa el serialVersionUID incorrecto?

Actualización : debo mencionar que nuestra intención es actualizar todo al mismo tiempo, pero estoy tratando de depurar un problema en nuestro proceso de compilación e implementación.

¿Fue útil?

Solución

use la herramienta serialver de sun jdk para cada clase en el jar.

Otros consejos

La mejor manera de lidiar con este tipo de problemas es actualizar los archivos jar en el lado del servidor y del cliente al mismo tiempo. Esto garantizará la misma versión de sus clases en ambos lados y no tendrá problemas al serializar / deserializar. El seguimiento de los UID en serie cada vez que tenga este problema no resolverá nada, solo perderá un tiempo y recursos considerables. Es mucho mejor pasar algo de tiempo e implementar una estrategia de implementación / empaque adecuada.

Si realmente no tiene otra opción, puede escribir una herramienta que cargue una clase de cada jarra (usando un URLClassLoader) y luego usar java.io.ObjectStreamClass.getSerialVersionUID () para obtener la información que necesita.

Tenga en cuenta que varias combinaciones JVM / container / classloader tienen diferentes opiniones sobre qué clases deben cargarse desde bootclasspath versus la aplicación / webapp classpath.

Esto se complica por el hecho de que el serialver siempre se carga desde bootclasspath primero, por lo que puede que necesite usar -J-Xbootclasspath como se muestra a continuación, para simular un comportamiento diferente:

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

Otro enfoque es usar javap, por ejemplo:

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

Se me ocurrió una utilidad para realizar esto, basada en algunas respuestas en SO. Aquí está mi clase.

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);
            }
        }
    }
}

torpe pero funciona

usaría una herramienta de ingeniería inversa
1) descomprimir los frascos
2) ejecute, digamos, jad en el árbol de archivos de clase
3) ejecute grep o cualquier otra herramienta de búsqueda en este nuevo árbol de origen para buscar la versión en serie uid.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top