Заставить Среду выполнения Java проигнорировать serialVersionUIDs?

Вот пример, где весь импорт на самой вершине (это - единственное время, я должен был сделать это). Я хочу быть в состоянии завершить подпроцесс и на Un*x и на Windows.

import os
# ...
try:
    kill = os.kill  # will raise AttributeError on Windows
    from signal import SIGTERM
    def terminate(process):
        kill(process.pid, SIGTERM)
except (AttributeError, ImportError):
    try:
        from win32api import TerminateProcess  # use win32api if available
        def terminate(process):
            TerminateProcess(int(process._handle), -1)
    except ImportError:
        def terminate(process):
            raise NotImplementedError  # define a dummy function

(На обзоре: что сказал John Millikin .)

15
задан Eduardo Naveda 1 May 2015 в 02:21
поделиться

5 ответов

If you have access to the code base, you could use the SerialVer task for Ant to insert and to modify the serialVersionUID in the source code of a serializable class and fix the problem once for all.

If you can't, or if this is not an option (e.g. if you have already serialized some objects that you need to deserialize), one solution would be to extend ObjectInputStream. Augment its behavior to compare the serialVersionUID of the stream descriptor with the serialVersionUID of the class in the local JVM that this descriptor represents and to use the local class descriptor in case of mismatch. Then, just use this custom class for the deserialization. Something like this (credits to this message):

import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class DecompressibleInputStream extends ObjectInputStream {

    private static Logger logger = LoggerFactory.getLogger(DecompressibleInputStream.class);

    public DecompressibleInputStream(InputStream in) throws IOException {
        super(in);
    }

    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
        Class localClass; // the class in the local JVM that this descriptor represents.
        try {
            localClass = Class.forName(resultClassDescriptor.getName()); 
        } catch (ClassNotFoundException e) {
            logger.error("No local class for " + resultClassDescriptor.getName(), e);
            return resultClassDescriptor;
        }
        ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
        if (localClassDescriptor != null) { // only if class implements serializable
            final long localSUID = localClassDescriptor.getSerialVersionUID();
            final long streamSUID = resultClassDescriptor.getSerialVersionUID();
            if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
                final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: ");
                s.append("local serialVersionUID = ").append(localSUID);
                s.append(" stream serialVersionUID = ").append(streamSUID);
                Exception e = new InvalidClassException(s.toString());
                logger.error("Potentially Fatal Deserialization Operation.", e);
                resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
            }
        }
        return resultClassDescriptor;
    }
}
36
ответ дан 1 December 2019 в 00:54
поделиться

Насколько непрактично это исправлять? Если у вас есть исходный код и вы можете выполнить пересборку, не могли бы вы просто запустить сценарий для всей кодовой базы , чтобы вставить

private long serialVersionUID = 1L;

везде?

2
ответ дан 1 December 2019 в 00:54
поделиться

Использовать CGLIB для вставки их в двоичные классы?

2
ответ дан 1 December 2019 в 00:54
поделиться

Ошибки сериализации во время выполнения явно сообщают вам, каким должен быть идентификатор. Просто измените свои классы, объявив их как идентификаторы, и все будет в порядке. Это требует внесения изменений, но я не верю, что этого можно избежать

1
ответ дан 1 December 2019 в 00:54
поделиться

Вы могли бы использовать Aspectj для «введения» поля в каждый сериализуемый класс по мере его загрузки. Я бы сначала ввел интерфейс маркера в каждый класс с помощью пакета, а затем ввел бы поле, используя хэш файла класса для serialVersionUID

public aspect SerializationIntroducerAspect {

   // introduce marker into each class in the org.simple package
   declare parents: (org.simple.*) implements SerialIdIntroduced;

   public interface SerialIdIntroduced{}

   // add the field to each class marked with the interface above.
   private long SerialIdIntroduced.serialVersionUID = createIdFromHash(); 

   private long SerialIdIntroduced.createIdFromHash()
   {
       if(serialVersionUID == 0)
       {
           serialVersionUID = getClass().hashCode();
       }
       return serialVersionUID;
   }
}

. Вам нужно будет добавить агент ткача времени загрузки аспектj в файл VM, чтобы он мог вплетать советы в ваши существующие сторонние классы. Это забавно, хотя, как только вы приступите к настройке Aspectj, вы увидите огромное количество применений, которые вы будете использовать.

HTH

ste

0
ответ дан 1 December 2019 в 00:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: