Несогласованное исключение, анализирующее геометрию WKB с использованием JTS

У меня самая странная проблема, которую я просто не могу понять. Мой веб-API, использующий Spring Boot и postgresql / postgis, выдает несогласованные ошибки при попытке чтения геометрии из базы данных. Я использую этот код (конечно, с редкими модификациями) много-много лет, и это только начало происходить в моем последнем выпуске.

Я использую openjdk 11.0.4 2019-07-16 на ubuntu 18.04. Отменить записи pom.xml ...

        <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>1.16.1</version>
        </dependency>

Я получаю различные ошибки из-за вызовов api следующих типов ...

например шестнадцатеричная строка: 0101000020E6100000795C548B88184FC0206118B0E42750C0

org.locationtech.jts.io.ParseException: Unknown WKB type 0
    at org.locationtech.jts.io.WKBReader.readGeometry(WKBReader.java:235)
    at org.locationtech.jts.io.WKBReader.read(WKBReader.java:156)
    at org.locationtech.jts.io.WKBReader.read(WKBReader.java:137)
    at net.crowmagnumb.database.RecordSet.getGeom(RecordSet.java:1073)

например шестнадцатеричная строка: 0101000020E61000000080FB3F354F5AC0F3D30EF2C0773540

java.lang.ArrayIndexOutOfBoundsException: arraycopy: length -1 is negative
    at java.base/java.lang.System.arraycopy(Native Method)
    at org.locationtech.jts.io.ByteArrayInStream.read(ByteArrayInStream.java:59)
    at org.locationtech.jts.io.ByteOrderDataInStream.readDouble(ByteOrderDataInStream.java:83)
    at org.locationtech.jts.io.WKBReader.readCoordinate(WKBReader.java:378)
    at org.locationtech.jts.io.WKBReader.readCoordinateSequence(WKBReader.java:345)
    at org.locationtech.jts.io.WKBReader.readPoint(WKBReader.java:256)
    at org.locationtech.jts.io.WKBReader.readGeometry(WKBReader.java:214)
    at org.locationtech.jts.io.WKBReader.read(WKBReader.java:156)
    at org.locationtech.jts.io.WKBReader.read(WKBReader.java:137)
    at net.crowmagnumb.database.RecordSet.getGeom(RecordSet.java:1073)

например шестнадцатеричная строка: 0101000020E610000066666666669663C00D96D7371DD63440

org.locationtech.jts.io.ParseException: Unknown WKB type 326
    at org.locationtech.jts.io.WKBReader.readGeometry(WKBReader.java:235)
    at org.locationtech.jts.io.WKBReader.read(WKBReader.java:156)
    at org.locationtech.jts.io.WKBReader.read(WKBReader.java:137)
    at net.crowmagnumb.database.RecordSet.getGeom(RecordSet.java:1073)

Соответствующие части моего кода RecordSet приведены ниже (поэтому номера строк не будут совпадать с трассировками стека).

public class RecordSet {
    private static final Logger logger = LoggerFactory.getLogger(RecordSet.class);
    private static WKBReader wkbReader;

    private static WKBReader getWKBReader() {
        if (wkbReader == null) {
            wkbReader = new WKBReader();
        }
        return wkbReader;
    }

    private static byte[] hexStringToByteArray(final String hex) {
        if (StringUtils.isBlank(hex)) {
            return null;
        }

        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    public static Geometry getGeom(final String geomStr) {
        byte[] byteArray = hexStringToByteArray(geomStr);
        if (byteArray == null) {
            return null;
        }
        try {
            return getWKBReader().read(byteArray);
        } catch (Throwable ex) {
            logger.error(String.format("Error parsing geometry [%s]", geomStr), ex);
            return null;
        }
    }
}

Итак, крайняя странность в том, что

  1. Это не происходит постоянно. Тот же самый вызов API, когда я пытаюсь повторить, работает нормально.
  2. Сообщенные шестнадцатеричные строки в сообщении об исключении совершенно верны! Если я запустил их в тестовой программе с использованием того же кода, я дам правильный ответ и без исключения.

Опять же, все вышеперечисленные шестнадцатеричные строки, которые приводят к ошибкам в производственных вызовах API, являются действительными представлениями геометрии POINT.

Это какая-то странная проблема с потенциальной утечкой памяти?


person crowmagnumb    schedule 07.03.2020    source источник


Ответы (1)


Может быть, это должно было быть очевидным, но в свою защиту я использую приведенный выше код в течение многих-многих лет (как я уже сказал) без проблем, поэтому я думаю, что просто упустил из виду очевидное? Во всяком случае, меня внезапно осенило, следует ли мне повторно использовать один и тот же WKBReader снова и снова в многопоточной среде? Оказывается, нет!

Если я просто создаю новый WBBReader () с каждым вызовом (вместо получения одного статического WKBReader), он будет работать нормально. Ну вот и источник моей "утечки памяти". Самостоятельно!

person crowmagnumb    schedule 08.03.2020
comment
Хорошо, что вы это поняли. В документации Javadoc действительно сказано, что WKBReader не является потокобезопасным, но это легко упустить. Было бы неплохо, если бы существовал стандартный способ аннотировать, является ли класс потокобезопасным или нет. - person dr_jts; 21.04.2020
comment
Если вы хотите повторно использовать один экземпляр WKBReader, вы можете сделать его ThreadLocal переменной в Java. (Что-то вроде ThreadLocal<WKBReader> threadLocalReader = ThreadLocal.withInitial(() -> new WKBReader());) Надеюсь, это поможет. - person Kevin; 25.05.2021