From 9406994d538be8061bdafe2c5a61fb688a726a27 Mon Sep 17 00:00:00 2001 From: rng Date: Fri, 6 Feb 2026 16:01:00 +1100 Subject: [PATCH] Update to use geotools lib instead of hardcode url --- pom.xml | 5 ++ server/pom.xml | 4 + .../core/model/ogc/wfs/FeatureTypeInfo.java | 60 +------------ .../server/core/service/wfs/WfsServer.java | 90 ++++++++++--------- 4 files changed, 58 insertions(+), 101 deletions(-) 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(); } /**