Domanda

Quando ottengo un java.io.InvalidClassException, mi dà il serialVersionUID che desidera e il serialVersionUID che ha ottenuto. C'è un modo semplice per dire quale delle mie dozzine di barattoli usa il serialVersionUID sbagliato?

Aggiorna : dovrei menzionare che la nostra intenzione è quella di aggiornare tutto allo stesso tempo, ma sto cercando di eseguire il debug di un problema nel nostro processo di compilazione e distribuzione.

È stato utile?

Soluzione

usa lo strumento serialver di sun jdk per ogni classe nel vaso.

Altri suggerimenti

Il modo migliore per affrontare questo tipo di problema è aggiornare contemporaneamente i vasetti sul lato server e client. Ciò garantirà la stessa versione delle tue classi su entrambi i lati e non avrai problemi durante la serializzazione / deserializzazione. Tracciare gli UID seriali ogni volta che si verifica questo problema non risolverà nulla, si perderanno solo tempo e risorse considerevoli. È molto meglio passare un po 'di tempo e attuare una corretta strategia di distribuzione / imballaggio.

Se davvero non hai altra scelta, puoi scrivere uno strumento che carica una classe da ciascun vaso (usando un URLClassLoader) e quindi usare java.io.ObjectStreamClass.getSerialVersionUID () per ottenere le informazioni di cui hai bisogno.

Tieni presente che varie combinazioni JVM / container / classloader hanno opinioni diverse su quali classi devono essere caricate dal bootclasspath rispetto al classpath dell'applicazione / webapp.

Ciò è complicato dal fatto che serialver si carica sempre prima dal bootclasspath, quindi potrebbe essere necessario utilizzare -J-Xbootclasspath come di seguito, per simulare comportamenti diversi:

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

Un altro approccio è utilizzare javap, ad esempio:

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

Mi è venuta in mente un'utilità per farlo, basata su alcune risposte in SO. Ecco la mia lezione.

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

goffo ma funziona

Vorrei usare uno strumento di reverse engineering
1) decomprimere i vasetti
2) eseguire, diciamo, jad sull'albero dei file di classe
3) esegui grep o qualsiasi altro strumento di ricerca su questo nuovo albero di sorgenti per cercare la versione seriale uid.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top