I'm currently working on a client application written in Java, using the org.geotools library in version 33.2 (released July 17th 2025) to request Features from a WFS layer. The code below establishes the WFS equivalent of a connection to the WFS service, and successfully retrieves the Capabilities of the server, and properly detects the name of the feature (stored in this.name)
WFSDataAccessFactory accessFactory = new WFSDataAccessFactory();
try {
URL capabilitiesUrl = WFSDataStoreFactory.createGetCapabilitiesRequest(new URI(this.serverURL).toURL(), new Version("1.1.0"));
Path temporaryFile = Files.createTempDirectory("cache");
temporaryFile.toFile().deleteOnExit();
java.util.Map<String,Object> connectionParameters = new HashMap<>();
connectionParameters.put(WFSDataStoreFactory.URL.key, capabilitiesUrl);
connectionParameters.put(WFSDataStoreFactory.TIMEOUT.key, 60000);
connectionParameters.put(WFSDataStoreFactory.BUFFER_SIZE.key, 1000);
connectionParameters.put(WFSDataStoreFactory.WFS_STRATEGY.key, "mapserver");
connectionParameters.put(WFSDataStoreFactory.PROTOCOL.key, Boolean.FALSE);
connectionParameters.put(WFSDataStoreFactory.SCHEMA_CACHE_LOCATION.key, temporaryFile.toString());
dataStore.getFeatureSource(this.featureName);
WFSContentDataAccess dataAccess = (WFSContentDataAccess) accessFactory.createDataStore(connectionParameters);
for (Name name : dataAccess.getNames()) {
LOGGER.info("Found Name {}...",name.getLocalPart());
if (this.name.equals(name.getLocalPart())) {
this.featureName = name;
break;
}
}
if (this.featureName == null) {
LOGGER.error("WFS service at {} doesn't have a feature named {}!",this.serverURL,this.name);
}
this.wfs = (FeatureSource<T, F>) dataAccess.getFeatureSource(this.featureName);
FeatureType schema = dataAccess.getSchema(this.featureName);
geometry = schema.getGeometryDescriptor().getName();
LOGGER.info("Found Geometry {}...",geometry.toString());
} catch (IOException exception) {
LOGGER.error("Failed to connect to WFS service at {}",this.serverURL,exception);
} catch (URISyntaxException exception) {
LOGGER.error("Failed to extract URL for WFS service at {}",this.serverURL,exception);
}
The returned feature source is an instance of WFSContentComplexFeatureSource
. After the above code is run, I double-checked in the specified temp folder that the Schema is properly cached. I can also located the Schema of the sought feature.
<?xml version='1.0' encoding="UTF-8" ?>
<schema targetNamespace="http://mapserver.gis.umn.edu/mapserver" xmlns:ms="http://mapserver.gis.umn.edu/mapserver" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml"elementFormDefault="qualified" version="0.1" >
<import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/gml.xsd" />
<element name="beckerich_roads"
type="ms:beckerich_roadsType"
substitutionGroup="gml:_Feature" />
<complexType name="beckerich_roadsType">
<complexContent>
<extension base="gml:AbstractFeatureType">
<sequence>
<element name="msGeometry" type="gml:GeometryPropertyType" minOccurs="0" maxOccurs="1"/>
<element name="full_id" minOccurs="0" type="string"/>
<element name="osm_id" minOccurs="0" type="string"/>
<element name="osm_type" minOccurs="0" type="string"/>
<element name="highway" minOccurs="0" type="string"/>
<element name="name" minOccurs="0" type="string"/>
<element name="surface" minOccurs="0" type="string"/>
<element name="maxspeed" minOccurs="0" type="string"/>
<element name="ref" minOccurs="0" type="string"/>
</sequence>
</extension>
</complexContent>
</complexType>
</schema>
All this works as expected. Next, I'm trying to submit a query to the previously retrieved FeatureSource.
Query query = new Query(this.featureName.getLocalPart());
query.setPropertyNames(this.geometry.getLocalPart());
FeatureCollection<T,F> results = this.wfs.getFeatures(query)
LOGGER.info("{} Features found!",results.size());
try (FeatureIterator<F> iterator = results.features()) {
while (iterator.hasNext()) {
Feature feature = iterator.next();
... Do something with the feature ...
}
}
The request fails as soon as I'm trying to do something with the returned FeatureCollection.
Aug 01, 2025 3:16:43 PM org.geotools.data.ows.AbstractOpenWebService internalIssueRequest
SEVERE: Failed to execute request https://mapserver.domain.org/service?PROPERTYNAME=%28msGeometry%29&TYPENAME=ms%3Abeckerich_roads&REQUEST=GetFeature&RESULTTYPE=RESULTS&OUTPUTFORMAT=text%2Fxml%3B+subtype%3Dgml%2F3.1.1&VERSION=1.1.0&SERVICE=WFS
Exception in thread "AWT-EventQueue-0" java.lang.UnsupportedOperationException
at org.geotools.data.wfs.internal.parsers.GmlGetFeatureResponseParserFactory.parser(GmlGetFeatureResponseParserFactory.java:78)
at org.geotools.data.wfs.internal.parsers.AbstractGetFeatureResponseParserFactory.createResponseImpl(AbstractGetFeatureResponseParserFactory.java:82)
at org.geotools.data.wfs.internal.parsers.AbstractWFSResponseFactory.createResponse(AbstractWFSResponseFactory.java:110)
at org.geotools.data.wfs.internal.WFSRequest.createResponse(WFSRequest.java:209)
at org.geotools.data.wfs.internal.WFSRequest.createResponse(WFSRequest.java:36)
at org.geotools.data.ows.AbstractOpenWebService.internalIssueRequest(AbstractOpenWebService.java:447)
at org.geotools.data.wfs.internal.WFSClient.internalIssueRequest(WFSClient.java:322)
at org.geotools.data.wfs.internal.WFSClient.issueComplexRequest(WFSClient.java:371)
at org.geotools.data.wfs.internal.WFSContentComplexFeatureCollection.features(WFSContentComplexFeatureCollection.java:73)
...
However, if I paste request into a browser, I'm actually getting the requested features:
<?xml version='1.0' encoding="UTF-8" ?>
<wfs:FeatureCollection
xmlns:ms="http://mapserver.gis.umn.edu/mapserver"
xmlns:gml="http://www.opengis.net/gml"
xmlns:wfs="http://www.opengis.net/wfs"
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://mapserver.gis.umn.edu/mapserver https://mapserver.domain.org/service?SERVICE=WFS&VERSION=1.1.0&REQUEST=DescribeFeatureType&TYPENAME=ms:beckerich_roads&OUTPUTFORMAT=SFE_XMLSCHEMA http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">
<gml:boundedBy>
<gml:Envelope srsName="EPSG:2169">
<gml:lowerCorner>83339.896453 54689.578935</gml:lowerCorner>
<gml:upperCorner>94377.106739 65551.221427</gml:upperCorner>
</gml:Envelope>
</gml:boundedBy>
<gml:featureMember>
<ms:beckerich_roads>
<gml:boundedBy>
<gml:Envelope srsName="EPSG:2169">
<gml:lowerCorner>88388.223655 56535.597859</gml:lowerCorner>
<gml:upperCorner>88389.234600 56553.900902</gml:upperCorner>
</gml:Envelope>
</gml:boundedBy>
<ms:msGeometry>
<gml:LineString srsName="EPSG:2169">
<gml:posList srsDimension="2">88389.234600 56553.900902 88388.233812 56543.543192 88388.223655 56535.597859 </gml:posList>
</gml:LineString>
</ms:msGeometry>
</ms:beckerich_roads>
Looking into the source code of the GmlGetFeatureResponseParserFactory
it seems that it's only capable of handling SimpleFeatures. I've explicitly added the gt-complex library, thinking that maybe that's allowing the retrieval of complex features, but it doesn't. So my question, is there a way or a config to let the FeatureSource know how to process complex features?
2 Answers 2
From a quick google and from a very old memory I found this PR which was merged in 2015. I think you will need to add the gt-app-schema
module, there's some fairly comprehensive documentation to help you get started. As far as I recall you use it to replace your WFS store.
-
Hi Ian, first of thank you for getting back to me. I already hade the gt-app-schema module added, however, thanks to your link I realized that I had to replace the WFSDataAccessFactory with the AppSchemaDataAccessFactory, respectively my WFSContentDataAccess instance by AppSchemaDataAccess. The only part I still haven't figured out is what to use as the URL for the AppSchemaDataAccess connection parameters. I tried saving the XML returned by the DescribeFeatureType request, however, this doesn't seem correct.Nico– Nico2025年08月04日 12:53:33 +00:00Commented Aug 4 at 12:53
-
Just for your information, the server I'm submitting the requests to is a MapServer, not a GeoServer instance.Nico– Nico2025年08月04日 13:06:06 +00:00Commented Aug 4 at 13:06
I returned to my initial issue after some time, and I finally figured out the missing pieces. The missing part in my previous attempts was the GET_CAPABILITIES_URL parameter, which I had to add as a literal, because it wasn't available as a key. I realized this while debugging. As soon as I added this parameter, the retrieval of complex features worked as expected. Hope this helps if somebody else stumbles over this issue.
URL capabilitiesUrl = WFSDataStoreFactory.createGetCapabilitiesRequest(new URI(this.serverURL).toURL(), new Version("1.1.0"));
java.util.Map<String,Object> connectionParameters = new HashMap<>();
connectionParameters.put("WFSDataStoreFactory:GET_CAPABILITIES_URL", capabilitiesUrl);
connectionParameters.put(AppSchemaDataAccessFactory.DBTYPE.key, "app-schema");
DataAccess<FeatureType, Feature> dataAccess = DataAccessFinder.getDataStore(connectionParameters);
for (Name name : dataAccess.getNames()) {
LOGGER.info("Found Name {}...",name.getLocalPart());
if (this.name.equals(name.getLocalPart())) {
this.featureName = name;
break;
}
}
if (this.featureName == null) {
LOGGER.error("WFS service at {} doesn't have a feature named {}!",this.serverURL,this.name);
}
this.wfs = (FeatureSource<T, F>) dataAccess.getFeatureSource(this.featureName);
FeatureType schema = dataAccess.getSchema(this.featureName);
geometry = schema.getGeometryDescriptor().getName();