使用JTS解析WKB几何体时出现异常不一致

3

我遇到了一个非常奇怪的问题,让我无法理解。我的Web 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 几何表示形式。
这是一些奇怪的潜在内存泄漏问题吗?
1个回答

2
也许这应该是显而易见的,但为了辩护我已经使用上面的代码很多年了(就像我说的那样),没有问题,所以我认为我只是忽略了显而易见的问题?无论如何,突然间我想到,在多线程环境中,我应该重复使用相同的WKBReader吗?嗯,结果是否定的!如果我每次调用时创建一个新的WBBReader()(而不是获取单个静态WKBReader),它就可以正常工作。好吧,这就是我的“内存泄漏”的根源。自己造成的!

很好,你解决了这个问题。虽然在 Javadoc 中确实提到了 WKBReader 不是线程安全的,但很容易忽略这一点。如果有一种标准的方法来注释一个类是否线程安全,那就太好了。 - dr_jts
1
如果您想重复使用单个 WKBReader 实例,您可以将其作为 Java 中的 ThreadLocal 变量。 (类似于 ThreadLocal<WKBReader> threadLocalReader = ThreadLocal.withInitial(() -> new WKBReader());)希望这可以帮助到您。 - Kevin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接