Известно ли, что LittleEndianDataInputStream нестабилен, особенно в Linux? - PullRequest
0 голосов
/ 24 июня 2019

Недавно я занялся разработкой программного обеспечения для телеметрии для автомобиля Formula Student.Он использует Java8 и JavaFX8.Есть одна серьезная ошибка, которую я сейчас пытаюсь исправить, но сейчас я полностью застрял.Программа может загрузить зарегистрированные данные в произвольном формате позже для просмотра.Это нормально работает на Mac и Windows, но вызывает проблемы в Linux.Некоторым дистрибутивам иногда удается загрузить данные, а некоторым - вообще не удается.Проблема может быть связана с LittleEndianDataInputStream.

Я еще не понял всей проблемы.Поэтому мне было интересно, может ли кто-нибудь показать мне какой-нибудь способ «отладки» или решения этой проблемы.И скажите мне, является ли использование LittleEndianDataInputStream (не реализованным мной) хорошей идеей или нет (даже IntelliJ помечает этот API как нестабильный).Если нет, то что будет подходящей альтернативой для использования?Код всегда достигает «Исключения загрузчика» из «readVersion ()» из-за неправильного номера версии.Загрузка того же файла из Windows работает нормально.

Может ли

private static Set<Integer> SUPPORTED_VERSIONS = new HashSet<>(Arrays.asList(
        1 << 16
    ));

рассчитываться по-разному в операционных системах?

Например, Fedora вызывает ErrorLog (каждый раз) в конце.Соответствующий код приведен ниже.

private static Set<Integer> SUPPORTED_VERSIONS = new HashSet<>(Arrays.asList(
        1 << 16
    ));

....

public long readTimestampFromHeader(File file) throws LoaderException
    {
        try(FileInputStream fis = new FileInputStream(file);
                   BufferedInputStream bis = new BufferedInputStream(fis);
                   LittleEndianDataInputStream input = new LittleEndianDataInputStream(bis))
        {
            bytesRead.set(0);
            readVersion(input, file);
            input.readUnsignedShort();  // ID
            readString(input);          // Name
            readString(input);          // Type
            return input.readLong();
        }
        catch(EOFException e)
        {
            // File corrupt, much too short. Simply ignore
            log.log(Level.WARNING, "Could not read record header. Ignoring ...", e);
            return 0L;
        }
        catch(IOException e)
        {
            throw new LoaderException(file, e);
        }
    }

private void readVersion(LittleEndianDataInputStream input, File file) throws IOException, LoaderException
    {
        int version = input.readInt();
        bytesRead.set(bytesRead.get() + 4);

        if(!SUPPORTED_VERSIONS.contains(version))
        {
            throw new LoaderException(file, new Exception("File version "
                    + version + " is not supported"));
        }
    }

Интересные части ErrorLog:

de.***.***.loader.LoaderException: The loader failed loading from /home/****/****/****/****/***/***/01 - 05.06.2018/Logfiles/1970-01-01-01-01-08/brake_pressure-front.ebl
    at de.***.***.loader.ebl.EboxLogReader.readVersion(EboxLogReader.java:155)
    at de.***.***.loader.ebl.EboxLogReader.readTimestampFromHeader(EboxLogReader.java:130)

.....

Caused by: java.lang.Exception: File version 83946924 is not supported

РЕДАКТИРОВАТЬ: Подводя итог, что я пытался из предложений в комментариях или сам, и его эффекты:

  • Добавление отсутствующей версии вручную из ErrorLog -> Файлзагружается в графический интерфейс без ошибок, но вызывается 08.01.105917131 вместо «xyz.ebl» и не используется
  • Обновление зависимости API com.google.guava для более поздних выпусков ничего не меняет
  • Простозамена LittleEndianInputStream на ObjectInputStream приводит к тому же ErrorLog, но с версией: 256
  • Файл, который я пытаюсь загрузить, кажется, записан как ObjectOutputStream и читается LittleEndianInputStream.Это кажется странным, но с этим сталкиваются только Linux.Запись файла (который я пытаюсь загрузить) выполняется здесь:
public class DataRecordSerializer implements Serializer<DataRecord, FileDataLocator>
{
    public static final int VERSION = (0 << 16) + (1 << 8) + 0;

    private final boolean append;

    public DataRecordSerializer()
    {
        this(false);
    }

    public DataRecordSerializer(boolean append)
    {
        this.append = append;
    }

    @Override
    public void serialize(DataRecord data, FileDataLocator locator) throws SerializerException
    {
        File target = locator.getFile();
        boolean actuallyAppends = this.append && target.exists();

        // Save the data
        try(FileOutputStream fos = new FileOutputStream(target, this.append);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            ObjectOutputStream out = actuallyAppends ?
                    new AppendingObjectOutputStream(bos) :
                    new ObjectOutputStream(bos))
        {
            if(!actuallyAppends)
            {
                // Version info
                out.writeInt(VERSION);

                // Save the concrete class
                out.writeUTF(data.getClass().getName());

                // Header
                out.writeObject(data.getHeader());
            }

            // Data
            float[] x = data.getXValues();
            float[] y = data.getYValues();

            for(int i = 0; i < x.length; i++)
            {
                out.writeFloat(x[i]);
                out.writeFloat(y[i]);
            }
        }
        catch(IOException e)
        {
            throw new SerializerException(e);
        }
    }

    public static class AppendingObjectOutputStream extends ObjectOutputStream
    {
        public AppendingObjectOutputStream(OutputStream out) throws IOException
        {
            super(out);
        }

        @Override
        protected void writeStreamHeader() throws IOException
        {
            // do not write a header, but reset
            reset();
        }
    }
}

  • Изменение Supported_Version на Value из DataRecordSerializer
public static final int VERSION = (0 << 16) + (1 << 8) + 0;

делаетничего не менять.После слов я сохранил это как Supported_Versions и поменял местами LittleEndianInputStream и ObjectInputStream и получил этот ErrorLog:

 java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.readFully(ObjectInputStream.java:3106)
    at java.io.ObjectInputStream.readFully(ObjectInputStream.java:1076)
    at de.*.loader.ebl.EboxLogReader.readString(EboxLogReader.java:158)
    at de.*.loader.ebl.EboxLogReader.readTimestampFromHeader(EboxLogReader.java:127)
    at de.*.loader.ebl.EboxLogLoader.load(EboxLogLoader.java:65)
    at de.*.loader.ebl.EboxLogLoader.load(EboxLogLoader.java:24)
    at de.*.loader.LoaderManager.load(LoaderManager.java:122)
    at de.*.model.DataManager.forceLoadRunData(DataManager.java:193)
    at de.*.model.DataManager.getRunData(DataManager.java:218)
    at de.*.gui.fx.job.runData.LoadRunDataJob.call(LoadRunDataJob.java:22)
    at de.*.gui.fx.job.runData.LoadRunDataJob.call(LoadRunDataJob.java:8)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
WARNING - 26.06.19 19:32:24: Could not read record header. Ignoring ...
java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.readFully(ObjectInputStream.java:3106)
    at java.io.ObjectInputStream.readFully(ObjectInputStream.java:1076)
    at de.*.loader.ebl.EboxLogReader.readString(EboxLogReader.java:158)
    at de.*.loader.ebl.EboxLogReader.readHeader(EboxLogReader.java:52)
    at de.*.loader.ebl.EboxLogLoader.load(EboxLogLoader.java:86)
    at de.*.loader.ebl.EboxLogLoader.load(EboxLogLoader.java:24)
    at de.*.loader.LoaderManager.load(LoaderManager.java:122)
    at de.*.model.DataManager.forceLoadRunData(DataManager.java:193)
    at de.*.model.DataManager.getRunData(DataManager.java:218)
    at de.*.gui.fx.job.runData.LoadRunDataJob.call(LoadRunDataJob.java:22)
    at de.*.gui.fx.job.runData.LoadRunDataJob.call(LoadRunDataJob.java:8)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

1 Ответ

0 голосов
/ 24 июня 2019

SUPPORTED_VERSIONS инициализируется без элементов, а начальное значение 1 << 16. Вы можете отладить или попробовать: </p>

static {
    SUPPORTED_VERSIONS.add(83946924);
}

Вы не показали заполнение SUPPORTED_VERSIONS, в котором может возникнуть ошибка параллелизма, загрузки класса из-за параллелизма. AFAIK Linux, например, имеет лучшую поддержку параллелизма. Возможно, инициализация происходит во втором классе, и загрузка класса идет неправильно.

83946924 - это 0x500_EDAC, который содержит байт 0x00, но я не могу представить такой C-подобной ошибки или связанного со строкой модифицированного преобразования UTF-8, где 0x00 также превращается в многобайтовую последовательность.

Возможно, сама версия была неправильной и должна была быть 0xACED_0005.

Кстати, исключение можно записать как менее хитрое:

        throw new LoaderException("File version "
                + version + " is not supported: " + file);

Альтернативные классы:

Вы можете использовать отображенный в память файл, используя ByteBuffer с .order(ByteOrder.LITTLE_ENDIAN);. Это стандарт Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...