oracle / graalvm-reachability-metadata

Repository which contains community-driven collection of GraalVM reachability metadata for open-source libraries.
Creative Commons Zero v1.0 Universal
337 stars 76 forks source link

Hibernate spatial with PostGIS failing when running in native #505

Open loonis opened 1 week ago

loonis commented 1 week ago

Describe the bug

Hibernate spatial is failing when running in native image.

To Reproduce

Create a spring boot 3.3.1 project using hibernate-spatial

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.test</groupId>
    <artifactId>test</artifactId>

    <version>0.0.1-SNAPSHOT</version>
    <name>test</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>21</java.version>
        <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
        <graphql-java-extended-scalars.version>22.0</graphql-java-extended-scalars.version>
        <hibernate.version>6.5.2.Final</hibernate.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.google.cloud</groupId>
                <artifactId>spring-cloud-gcp-dependencies</artifactId>
                <version>5.4.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-graphql</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java-extended-scalars</artifactId>
            <version>${graphql-java-extended-scalars.version}</version>
        </dependency>

        <dependency>
            <artifactId>mapstruct</artifactId>
            <groupId>org.mapstruct</groupId>
            <version>${org.mapstruct.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-spatial</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>spring-cloud-gcp-starter-sql-postgresql</artifactId>
        </dependency>

<!--        TESTS-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.graphql</groupId>
            <artifactId>spring-graphql-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <artifactId>mapstruct-processor</artifactId>
                            <groupId>org.mapstruct</groupId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <path>
                            <artifactId>lombok</artifactId>
                            <groupId>org.projectlombok</groupId>
                            <version>${lombok.version}</version>
                        </path>

                        <path>
                            <groupId>org.hibernate.orm</groupId>
                            <artifactId>hibernate-jpamodelgen</artifactId>
                            <version>${hibernate.version}</version>
                        </path>

                    </annotationProcessorPaths>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>io.github.deweyjose</groupId>
                <artifactId>graphqlcodegen-maven-plugin</artifactId>
                <version>1.61.0</version>
                <executions>
                    <execution>
                        <id>dgs-codegen</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaPaths>
                                <param>src/main/resources/graphql</param>
                            </schemaPaths>
                            <packageName>com.kc.dto</packageName>
                            <addGeneratedAnnotation>true</addGeneratedAnnotation>
                            <generateClientApiV2>false</generateClientApiV2>
                            <generateDataTypes>true</generateDataTypes>

                            <onlyGenerateChanged>false</onlyGenerateChanged>
                            <addGeneratedAnnotation>true</addGeneratedAnnotation>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>add-dgs-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/generated-sources</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-image</goal>
                        </goals>
                    </execution>
                </executions>

                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.hibernate.orm.tooling</groupId>
                <artifactId>hibernate-enhance-maven-plugin</artifactId>
                <version>${hibernate.version}</version>
                <executions>
                    <execution>
                        <configuration>
                            <failOnError>true</failOnError>
                            <enableLazyInitialization>true</enableLazyInitialization>
                        </configuration>
                        <goals>
                            <goal>enhance</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>

        </plugins>
    </build>
</project>
spring:
  jpa:
    properties:
      hibernate:
        format_sql=true:
    defer-datasource-initialization: true
    hibernate:
      ddl-auto: none

One of my entity contains a field org.locationtech.jts.geom.Point; which is a PostGIS GEOGRAPHY(POINT, 4326),

I filter my entities based on these gps coordinates

      org.locationtech.jts.geom.GeometryFactory geometryFactory = new GeometryFactory();
      org.locationtech.jts.geom.Point refPoint = geometryFactory.createPoint(new Coordinate(latitude, longitude));
      refPoint.setSRID(4326);

      jakarta.persistence.criteria.Predicate distancePredicate = cb.equal(cb.literal(true), cb.function(
          "ST_DWithin",
          Boolean.class,
          root.get("location"),
          cb.literal(refPoint),
          cb.literal(radiusMeter)
      ));

The native image generation is working, but then running the executable will generate these error log bellow.

Error 1

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.geolatte.geom.crs.CrsRegistry
        at org.geolatte.geom.jts.JTS.determineCrsFromCoordinatesAndSrid(JTS.java:163) ~[na:na]
        at org.geolatte.geom.jts.JTS.from(JTS.java:126) ~[na:na]
        at org.hibernate.spatial.JTSGeometryJavaType.unwrap(JTSGeometryJavaType.java:102) ~[na:na]
        at org.hibernate.spatial.JTSGeometryJavaType.unwrap(JTSGeometryJavaType.java:37) ~[na:na]

Error2

Caused by: java.lang.InstantiationException: org.geolatte.geom.codec.PostgisWkbDecoder
        at java.base@21.0.3/java.lang.Class.newInstance(DynamicHub.java:719) ~[com.kc.KnightclubbersApplication:na]
        at org.geolatte.geom.codec.Wkb.createInstance(Wkb.java:222) ~[na:na]
        ... 64 common frames omitted
Caused by: java.lang.NoSuchMethodException: org.geolatte.geom.codec.PostgisWkbDecoder.<init>()
        at java.base@21.0.3/java.lang.Class.checkMethod(DynamicHub.java:1078) ~[com.kc.KnightclubbersApplication:na]
        at java.base@21.0.3/java.lang.Class.getConstructor0(DynamicHub.java:1241) ~[com.kc.KnightclubbersApplication:na]
        at java.base@21.0.3/java.lang.Class.newInstance(DynamicHub.java:706) ~[com.kc.KnightclubbersApplication:na]
        ... 65 common frames omitted

Error 3

Caused by: java.lang.IllegalStateException: Can't find spatial_ref_sys definitions.
        at org.geolatte.geom.crs.CrsRegistry.createReader(CrsRegistry.java:80) ~

I followed the indication given here: https://github.com/oracle/graalvm-reachability-metadata/issues/233#issuecomment-2152227329 which solves some problems, but not theses ones.

System Info

OS: Windows 11 GraalVM Version 22 Java Version Java 21 CE Plugin version: graalvm-reachability-metadata-0.10.2-repository.zip

jma-9code commented 4 days ago

You need to create :

  1. src/main/resources/META-INF/native-image/resource-config.json inside your project and add the following content:
{
  "resources": [
    {
      "pattern": "spatial_ref_sys.txt"
    }
  ]
}
  1. src/main/resources/META-INF/native-image/reflect-config.json inside your project and add the following content: [ { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name":"org.geolatte.geom.codec.PostgisWkbDecoder", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name":"org.geolatte.geom.codec.PostgisWktEncoder", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name": "org.geolatte.geom.crs.CrsRegistry", "allDeclaredConstructors": true, "allDeclaredMethods": true }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name": "org.geolatte.geom.crs.CoordinateReferenceSystems", "allDeclaredConstructors": true, "allDeclaredMethods": true }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name":"org.locationtech.jts.geom.Point" }, { "name": "org.hibernate.spatial.HSMessageLogger_$logger", "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "methods": [ { "name": "<init>", "parameterTypes": [ "org.jboss.logging.Logger" ] } ] } ]