Skip to content

Commit

Permalink
WMTS CITE compliance fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Jan 4, 2025
1 parent e2f6559 commit 3ae40fe
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,28 @@ public String toString() {
str.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
str.append(
" xsi:schemaLocation=\"http://www.opengis.net/ows/1.1 http://geowebcache.org/schema/ows/1.1.0/owsExceptionReport.xsd\">\n");
str.append(" <Exception exceptionCode=\"" + exceptionCode + "\" locator=\"" + locator + "\">\n");
if (locator != null) {
str.append(" <Exception exceptionCode=\"" + exceptionCode + "\" locator=\"" + locator + "\">\n");
} else {
str.append(" <Exception exceptionCode=\"" + exceptionCode + "\">\n");
}

str.append(" <ExceptionText>" + exceptionText + "</ExceptionText>\n");
str.append(" </Exception>\n");
str.append("</ExceptionReport>\n");

return str.toString();
}

public int getHttpCode() {
return httpCode;
}

public String getExceptionCode() {
return exceptionCode;
}

public String getLocator() {
return locator;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,10 @@ private void serviceIdentification(XMLBuilder xml, ServiceInformation servInfo)
appendTag(xml, "ows:Title", servInfo.getTitle(), "Web Map Tile Service - GeoWebCache");
appendTag(xml, "ows:Abstract", servInfo.getDescription(), null);

if (servInfo != null && servInfo.getKeywords() != null) {
// a keywords element cannot be empty
if (servInfo != null
&& servInfo.getKeywords() != null
&& !servInfo.getKeywords().isEmpty()) {
xml.indentElement("ows:Keywords");
Iterator<String> keywordIter = servInfo.getKeywords().iterator();
while (keywordIter.hasNext()) {
Expand Down Expand Up @@ -453,17 +456,17 @@ private void layer(XMLBuilder xml, TileLayer layer, String baseurl, Set<GridSet>

appendTag(xml, "ows:Identifier", layer.getName(), null);

// WMTS 1.0 Layer is a ows:DatasetDescriptionSummary, which in turn can hold a ows:Metadata,
// which finally
// has the xlink:simpleAttrs attribute group, see https://www.w3.org/1999/xlink.xsd.
// Unfortunately those links do not have a format attribute, so we can't use them for
// metadata links.
if (layer.getMetadataURLs() != null) {
for (MetadataURL metadataURL : layer.getMetadataURLs()) {
xml.indentElement("MetadataURL");
xml.attribute("type", metadataURL.getType());
xml.simpleElement("Format", metadataURL.getFormat(), true);
xml.indentElement("OnlineResource")
.attribute("xmlns:xlink", "http://www.w3.org/1999/xlink")
xml.indentElement("ows:Metadata")
.attribute("xlink:type", "simple")
.attribute("xlink:href", metadataURL.getUrl().toString())
.endElement();
xml.endElement();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ protected void writeResponse(RuntimeStats stats) throws OWSException {
TileLayer layer = convTile.getLayer();

GridSet gridSet = convTile.getGridSubset().getGridSet();
if (gridSet.getTileHeight() < j || j < 0) {
if (gridSet.getTileHeight() <= j || j < 0) {
throw new OWSException(
400, "PointIJOutOfRange", "J", "J was " + j + ", must be between 0 and " + gridSet.getTileHeight());
}

if (gridSet.getTileWidth() < i || i < 0) {
if (gridSet.getTileWidth() <= i || i < 0) {
throw new OWSException(
400, "PointIJOutOfRange", "I", "I was " + i + ", must be between 0 and " + gridSet.getTileWidth());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.geowebcache.util.NullURLMangler;
import org.geowebcache.util.ServletUtils;
import org.geowebcache.util.URLMangler;
import org.springframework.http.MediaType;

public class WMTSService extends Service {

Expand Down Expand Up @@ -248,6 +249,21 @@ public Conveyor getConveyor(HttpServletRequest request, HttpServletResponse resp

public Conveyor getRestConveyor(HttpServletRequest request, HttpServletResponse response)
throws GeoWebCacheException, OWSException {
// CITE compliance, if the representation is not available a 406 should be returned
// This is also the behavior mandated by the HTTP standard
String accept = request.getHeader("Accept");
if (accept != null) {
List<MediaType> mediaTypes = MediaType.parseMediaTypes(accept);
boolean representationAvailable = false;
for (MediaType mediaType : mediaTypes) {
if (mediaType.includes(MediaType.APPLICATION_XML)) {
representationAvailable = true;
break;
}
}
if (!representationAvailable) throw new HttpErrorCodeException(406, "Representation not available");
}

final String path = request.getPathInfo();

// special simpler case for GetCapabilities
Expand Down Expand Up @@ -302,14 +318,13 @@ public Conveyor getKvpConveyor(HttpServletRequest request, HttpServletResponse r
// if provided handle accepted versions parameter
if (acceptedVersions != null) {
// we only support version 1.0.0, so make sure that's one of the accepted versions
String[] versions = acceptedVersions.split("\\s*,\\s*");
int foundIndex = Arrays.binarySearch(versions, "1.0.0");
if (foundIndex < 0) {
List<String> versions = Arrays.asList(acceptedVersions.split("\\s*,\\s*"));
if (!versions.contains("1.0.0")) {
// no supported version is accepted
throw new OWSException(
400,
"VersionNegotiationFailed",
null,
"null",
"List of versions in AcceptVersions parameter value, in GetCapabilities "
+ "operation request, did not include any version supported by this server.");
}
Expand Down Expand Up @@ -409,6 +424,8 @@ private ConveyorTile getTile(
}

MimeType mimeType = null;
// the format should be present and valid also for GetFeatureInfo, while in CITE compliance
// mode
if (reqType == RequestType.TILE) {
String format = values.get("format");
if (format == null) {
Expand Down Expand Up @@ -437,6 +454,14 @@ private ConveyorTile getTile(
"INFOFORMAT",
"Unable to determine requested INFOFORMAT, " + infoFormat);
}

if (isCiteCompliant() && !isRestRequest(request)) {
String format = values.get("format");
if (format == null) {
throw new OWSException(
400, "MissingParameterValue", "FORMAT", "Unable to determine requested FORMAT, " + format);
}
}
}

final String tilematrixset = values.get("tilematrixset");
Expand Down Expand Up @@ -485,7 +510,7 @@ private ConveyorTile getTile(
throw new OWSException(
400,
"TileOutOfRange",
"TILECOLUMN",
"TILECOL",
"Column " + x + " is out of range, min: " + gridCov[0] + " max:" + gridCov[2]);
}

Expand Down Expand Up @@ -621,7 +646,7 @@ ServerConfiguration getMainConfiguration() {
*
* @return TRUE if GWC main configuration or at least one of the WMTS extensions forces CITE compliance
*/
private boolean isCiteCompliant() {
protected boolean isCiteCompliant() {
// let's see if main GWC configuration forces WMTS implementation to be CITE compliant
if (mainConfiguration != null && mainConfiguration.isWmtsCiteCompliant()) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
Expand Down Expand Up @@ -64,6 +65,7 @@
import org.geowebcache.layer.meta.VectorLayerMetadata;
import org.geowebcache.mime.ApplicationMime;
import org.geowebcache.mime.MimeType;
import org.geowebcache.service.HttpErrorCodeException;
import org.geowebcache.service.OWSException;
import org.geowebcache.stats.RuntimeStats;
import org.geowebcache.storage.StorageBroker;
Expand Down Expand Up @@ -142,6 +144,15 @@ public void testGetCap() throws Exception {
doc);
}

@Test
public void testGetCapInvalidFormat() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setPathInfo("geowebcache/service/wmts/rest/WMTSCapabilities.xml");
req.addHeader("Accept", "invalid/format");
HttpErrorCodeException exception = assertThrows(HttpErrorCodeException.class, () -> dispatch(req));
assertEquals(406, exception.getErrorCode());
}

@Test
public void testGetTileWithStyle() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
Expand Down
Loading

0 comments on commit 3ae40fe

Please sign in to comment.