diff --git a/pom.xml b/pom.xml
index 8f401a87..7ff66a2a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,6 +91,11 @@
gt-cql
${org.geotools.version}
+
+ org.geotools
+ gt-wfs-ng
+ ${org.geotools.version}
+
org.geotools
gt-geojson
diff --git a/server/pom.xml b/server/pom.xml
index 7f5c80c9..7a3258a4 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -137,6 +137,10 @@
org.geotools
gt-cql
+
+ org.geotools
+ gt-wfs-ng
+
org.geotools
gt-geojson
diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/ogc/wfs/FeatureTypeInfo.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/ogc/wfs/FeatureTypeInfo.java
index 98586980..849f4bd4 100644
--- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/ogc/wfs/FeatureTypeInfo.java
+++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/ogc/wfs/FeatureTypeInfo.java
@@ -1,70 +1,16 @@
package au.org.aodn.ogcapi.server.core.model.ogc.wfs;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+import lombok.*;
-import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
-@JacksonXmlRootElement(localName = "FeatureType")
+@Getter
+@Setter
public class FeatureTypeInfo {
- @JacksonXmlProperty(localName = "Name")
protected String name;
-
- @JacksonXmlProperty(localName = "Title")
protected String title;
-
- @JsonProperty("abstract")
- @JacksonXmlProperty(localName = "Abstract")
- private String abstract_;
-
- @JacksonXmlProperty(localName = "Keywords", namespace = "http://www.opengis.net/ows/1.1")
- private Keywords keywords;
-
- @JacksonXmlProperty(localName = "DefaultCRS")
- private String defaultCRS;
-
- @JacksonXmlProperty(localName = "WGS84BoundingBox", namespace = "http://www.opengis.net/ows/1.1")
- private WGS84BoundingBox wgs84BoundingBox;
-
- @JacksonXmlProperty(localName = "MetadataURL")
- private MetadataURL metadataURL;
-
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public static class Keywords {
- @JacksonXmlElementWrapper(useWrapping = false)
- @JacksonXmlProperty(localName = "Keyword", namespace = "http://www.opengis.net/ows/1.1")
- private List keyword;
- }
-
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public static class WGS84BoundingBox {
- @JacksonXmlProperty(localName = "LowerCorner", namespace = "http://www.opengis.net/ows/1.1")
- private String lowerCorner;
-
- @JacksonXmlProperty(localName = "UpperCorner", namespace = "http://www.opengis.net/ows/1.1")
- private String upperCorner;
- }
-
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public static class MetadataURL {
- @JacksonXmlProperty(isAttribute = true, localName = "href", namespace = "http://www.w3.org/1999/xlink")
- private String href;
- }
}
diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/wfs/WfsServer.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/wfs/WfsServer.java
index c1ac75ae..f587458a 100644
--- a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/wfs/WfsServer.java
+++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/wfs/WfsServer.java
@@ -5,7 +5,6 @@
import au.org.aodn.ogcapi.server.core.model.StacCollectionModel;
import au.org.aodn.ogcapi.server.core.model.ogc.FeatureRequest;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.WfsDescribeFeatureTypeResponse;
-import au.org.aodn.ogcapi.server.core.model.ogc.wfs.WfsGetCapabilitiesResponse;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.FeatureTypeInfo;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.DownloadableFieldModel;
import au.org.aodn.ogcapi.server.core.service.ElasticSearchBase;
@@ -16,6 +15,9 @@
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
+import org.geotools.data.DataStoreFinder;
+import org.geotools.data.store.ContentFeatureSource;
+import org.geotools.data.wfs.WFSDataStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
@@ -27,14 +29,11 @@
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
+import java.io.IOException;
import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
-import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.DOWNLOADABLE_FIELDS;
-import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.GET_CAPABILITIES_WFS_FEATURE_TYPES;
+import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.*;
import static au.org.aodn.ogcapi.server.core.service.wfs.WfsDefaultParam.WFS_LINK_MARKER;
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.extractLayernameOrTypenameFromUrl;
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.roughlyMatch;
@@ -193,6 +192,30 @@ public Optional getFeatureServerUrlByTitleOrQueryParam(String collection
}
}
+ public WFSDataStore createDataStore(String wfsServerUrl) throws IOException {
+ // Parse the base URL to construct GetCapabilities request
+ UriComponents components = UriComponentsBuilder.fromUriString(wfsServerUrl).build();
+
+ // Build GetCapabilities URL
+ UriComponentsBuilder builder = UriComponentsBuilder
+ .newInstance()
+ .scheme("https") // hardcode to be https to avoid redirect
+ .port(components.getPort())
+ .host(components.getHost())
+ .path(components.getPath() != null ? components.getPath() : "/geoserver/ows")
+ .queryParam("service", "wfs")
+ .queryParam("request", "GetCapabilities");
+
+ String url = builder.build().toUriString();
+ Map params = new HashMap<>();
+ params.put("WFSDataStoreFactory:GET_CAPABILITIES_URL", url);
+ params.put("WFSDataStoreFactory:PROTOCOL", "1.1.0");
+ params.put("WFSDataStoreFactory:HTTP_HEADERS", pretendUserEntity.getHeaders().toSingleValueMap());
+
+ log.debug("WFS GetCapabilities URL: {}", url);
+
+ return (WFSDataStore)DataStoreFinder.getDataStore(params);
+ }
/**
* Fetch raw feature types from WFS GetCapabilities - cached by URL.
* This allows multiple collections sharing the same WFS server to use cached results.
@@ -203,49 +226,28 @@ public Optional getFeatureServerUrlByTitleOrQueryParam(String collection
@Cacheable(value = GET_CAPABILITIES_WFS_FEATURE_TYPES)
public List fetchCapabilitiesFeatureTypesByUrl(String wfsServerUrl) {
try {
- // Parse the base URL to construct GetCapabilities request
- UriComponents components = UriComponentsBuilder.fromUriString(wfsServerUrl).build();
-
- // Build GetCapabilities URL
- UriComponentsBuilder builder = UriComponentsBuilder
- .newInstance()
- .scheme("https") // hardcode to be https to avoid redirect
- .port(components.getPort())
- .host(components.getHost())
- .path(components.getPath() != null ? components.getPath() : "/geoserver/ows")
- .queryParam("service", "wfs")
- .queryParam("request", "GetCapabilities");
-
- String url = builder.build().toUriString();
- log.debug("WFS GetCapabilities URL: {}", url);
-
- // Make the HTTPS call
- ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, pretendUserEntity, String.class);
-
- if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
- // Parse XML response
- WfsGetCapabilitiesResponse capabilitiesResponse = xmlMapper.readValue(
- response.getBody(),
- WfsGetCapabilitiesResponse.class
+ WFSDataStore ds = self.createDataStore(wfsServerUrl);
+ String[] typeNames = ds.getTypeNames();
+ List schemas = new ArrayList<>();
+
+ for (String typeName : typeNames) {
+ ContentFeatureSource c = ds.getFeatureSource(typeName);
+
+ schemas.add(
+ FeatureTypeInfo.builder()
+ .name(c.getInfo().getName())
+ .title(c.getInfo().getTitle())
+ .build()
);
+ }
- // Extract all feature types
- if (capabilitiesResponse != null
- && capabilitiesResponse.getFeatureTypeList() != null
- && capabilitiesResponse.getFeatureTypeList().getFeatureTypes() != null) {
-
- List featureTypes = capabilitiesResponse.getFeatureTypeList().getFeatureTypes();
+ log.info("Fetched {} WFS feature types", schemas.size());
+ return schemas;
- log.info("Fetched and cached WFS get-capabilities feature types: {} ", featureTypes.size());
- return featureTypes;
- }
- }
- } catch (RestClientException | JsonProcessingException e) {
+ } catch (RestClientException | IOException e) {
log.error("Error fetching WFS GetCapabilities for URL: {}", wfsServerUrl, e);
throw new RuntimeException(e);
}
-
- return Collections.emptyList();
}
/**