Search code examples
gisgeoserver

GeoServer - Creation of featureType using Rest API fails with error "java.lang.NullPointerException"


I have successfully created a dataStore using Rest API. There are no issues when I create a layer using GeoServer web panel. But I am unable to create the same layer using Rest API.

Workspace and store name is "test" and "gis_osm_adminareas_a_07_1" respectively.

Store is of type "Shapefile" and points to a ".shp" file. There are also ".dbf", ".prj" and ".shx" files sharing the same base name at the same location with said ".shp" file. ogrinfo -al -so gis_osm_adminareas_a_07_1.shp command correctly analyzes and gives shapefile related information.

Here is the URL and post body I am using:

URL: {{host}}/geoserver/rest/workspaces/test/datastores/gis_osm_adminareas_a_07_1/featuretypes
Body:
{
    "featureType":
    {
        "name": "gis_osm_adminareas_a_07_1",
        "title": "gis_osm_adminareas_a_07_1",
        "enabled": true,
        "store":
        {
            "@class": "dataStore",
            "name": "test:gis_osm_adminareas_a_07_1"
        },
        "attributes":
        {
            "attribute":
            [
                {
                    "name": "the_geom",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "org.locationtech.jts.geom.MultiPolygon" // also tried "org.locationtech.jts.geom.Polygon" here
                },
                {
                    "name": "osm_id",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "java.lang.String",
                    "length": 10
                },
                {
                    "name": "lastchange",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "java.lang.String",
                    "length": 20
                },
                {
                    "name": "code",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "java.lang.Integer",
                    "length": 4
                },
                {
                    "name": "fclass",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "java.lang.String",
                    "length": 28
                },
                {
                    "name": "geomtype",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "java.lang.String",
                    "length": 10
                },
                {
                    "name": "postalcode",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "java.lang.String",
                    "length": 10
                },
                {
                    "name": "name",
                    "minOccurs": 0,
                    "maxOccurs": 1,
                    "nillable": true,
                    "binding": "java.lang.String",
                    "length": 100
                }
            ]
        }
    }
}

Operation fails with a 500 internal server error with no error information sent back in response body. But the stack trace appears in the logs. Here is the relevant part of the error log:

ERROR  [geoserver.rest] - 
java.lang.NullPointerException
at org.geoserver.catalog.impl.FeatureTypeValidator.validate(FeatureTypeValidator.java:40)
at org.geoserver.catalog.impl.CatalogImpl.validate(CatalogImpl.java:515)
at org.geoserver.security.SecureCatalogImpl.validate(SecureCatalogImpl.java:1320)
at org.geoserver.catalog.impl.AbstractFilteredCatalog.validate(AbstractFilteredCatalog.java:633)
at org.geoserver.catalog.impl.AbstractCatalogDecorator.validate(AbstractCatalogDecorator.java:274)
at org.geoserver.rest.catalog.FeatureTypeController.featureTypePost(FeatureTypeController.java:280)

I have tried many different combinations of post body to no success. I have also tried copying the "resource.json" created when layer is created using the web panel and sending it to featureType endpoint which did not work either.

Any help is appreciated. Thanks!

Update

Store was created using the following URL and post body

URL: POST {{host}}/workspaces/test/datastores
Body:
{
    "dataStore":
    {
        "workspace": "test",
        "enabled": true,
        "name": "gis_osm_adminareas_a_07_1",
        "nativeName": "gis_osm_adminareas_a_07_1.shp",
        "connectionParameters":
        {
            "entry":
            [
                {
                    "@key": "filetype",
                    "$": "shapefile"
                },
                {
                    "@key": "create spatial index",
                    "$": "true"
                },
                {
                    "@key": "url",
                    "$": "file:///geoserver/uploads/6b82db8f-427c-4f07-9c89-e95ae6413732/gis_osm_adminareas_a_07_1.shp"
                },
                {
                    "@key": "enable spatial index",
                    "$": "true"
                },
                {
                    "@key": "charset",
                    "$": "ISO-8859-1"
                },
                {
                    "@key": "memory mapped buffer",
                    "$": "false"
                },
                {
                    "@key": "timezone",
                    "$": "GMT"
                },
                {
                    "@key": "namespace",
                    "$": "http://test"
                },
                {
                    "@key": "cache and reuse memory maps",
                    "$": "false"
                },
                {
                    "@key": "fstype",
                    "$": "shape"
                }
            ]
        },
        "type": "Shapefile"
    }
}

Solution

  • Adding an empty "metadata.entry" array to request body seems to fix the issue. Here is the offending line of code in GeoServer:

    fti.getMetadata().get(FeatureTypeInfo.JDBC_VIRTUAL_TABLE, VirtualTable.class);
    

    I believe the get method fails because getMetadata method of fti (which corresponds to featureType field sent with request) returns a null pointer. Hence the java.lang.NullPointerException mentioned in the question is raised.

    Issue occurs when dataStore and featureType is created using two different requests

    • POST workspaces/{workspace}/datastores
    • POST workspaces/{workspace}/datastores/{datastore}/featuretypes
      ; but not when a single
    • PUT workspaces/{workspace}/datastores/{datastore}/file.shp?filename={datastore}&update=overwrite
      request is used. So the issue can also be worked around using the PUT method.

    If using a single request is out of question, here is the steps taken to create the layer with pieces of related PHP code:

    • Upload the shapefile to GeoServer

    Let's assume you somehow managed to copy the shapefile to /opt/geoserver/data_dir/data/{workspace}/gis_osm_adminareas_a_07_1/gis_osm_adminareas_a_07_1.shp with associated ".dbf", ".shx" and ".prj" files.

    • Create the dataStore
    # HTTP request is made using Guzzle [https://github.com/guzzle/guzzle]
    $this->client->post("workspaces/$this->workspace/datastores", [
        'json' => [
            'dataStore' => [
                'name' => $name,
                'connectionParameters' => [
                    'entry' => [
                        [
                            '@key' => 'url',
                            '$' => "file://$resource_path"
                        ]
                    ]
                ],
                'type' => 'Shapefile'
            ]
        ]
    ]);
    
    • Create the featureType
    # HTTP request is made using Guzzle [https://github.com/guzzle/guzzle]
    $this->client->post("workspaces/$this->workspace/datastores/$store/featuretypes", [
        'json' => [
            'featureType' => [
                'name' => $store,
                'title' => $name,
                "metadata" => [
                    "entry" => []
                ],
                'attributes' => [
                    'attribute' => [
                        [
                            "name" => "the_geom",
                            "minOccurs" => 0,
                            "maxOccurs" => 1,
                            "nillable" => true,
                            "binding" => "org.locationtech.jts.geom.MultiPolygon"
                        ]
                    ]
                ]
            ]
        ]
    ]);