java - 使用 JTS 解析 WKB 几何的不一致异常
问题描述
我遇到了一个我无法解决的最奇怪的问题。我使用 Spring Boot 和 postgresql/postgis 的 web api 在尝试从数据库中读取几何图形时出现不一致的错误。多年来,我一直在使用此代码(当然偶尔会进行修改),而这只是在我的上一个版本中开始发生。
我在 ubuntu 18.04 上使用 openjdk 11.0.4 2019-07-16。相关 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;
}
}
}
所以最奇怪的是
- 它不会始终如一地发生。当我尝试重复它时,完全相同的 api 调用工作正常。
- 异常消息中报告的十六进制字符串完全正确!如果我使用相同的代码在测试程序中运行它们,则给出正确的答案,没有例外。
同样,上述所有导致生产 api 调用错误的十六进制字符串都是 POINT 几何的有效表示。
这是一些奇怪的潜在内存泄漏问题吗?
解决方案
也许这应该是显而易见的,但在我的辩护中,我已经使用上面的代码很多年(正如我所说)没有问题,所以我认为我只是忽略了显而易见的事情?无论如何,我突然意识到我应该在多线程环境中一遍又一遍地重用同一个 WKBReader 吗?好吧,事实证明没有!
如果我只是在每次调用时创建一个新的 WBBReader()(而不是获得一个静态 WKBReader),它就可以正常工作。那么这就是我的“内存泄漏”的来源。自己造成的!
推荐阅读
- wpf - NuGet 还原前解决方案以使 wix 在 Azure Devops 中失败
- java - 指定 AWS Cloudwatch 日志客户端的凭证
- swift - 每次编译他们正在测试的目标时运行单元测试
- android - 如何在 Android 设备上运行 VS Code
- node.js - Nodejs/MongoDB:将动态创建的对象推送到数组中
- typescript - 返回类型中的打字稿条件泛型类型
- javascript - 调用和读取外部 JS 文件 - Angular
- python - tf.keras.losses 中“BinaryCrossentropy”和“binary_crossentropy”的区别?
- mysql - 如何在 SQL 中按记录类型获取最新记录
- angularjs - angularjs:http.post() 成功执行,但没有向服务器文件发布任何内容