Skip to content

Commit

Permalink
Resolve ambiguous handler mapping in SeedController
Browse files Browse the repository at this point in the history
Override the `@PostMapping` path pattern in `SeedController` to exclude `.xml`
and `.json` suffixes, addressing a stricter behavior in Spring Boot compared
to regular Spring. This resolves a potential `IllegalStateException` caused by
ambiguous handler methods.

- Updated `doPost` endpoint path to use regex excluding `.xml` and `.json`.
- Prevents conflicts between JSON and XML payload handlers (`seedOrTruncateWithJsonPayload`
  and `seedOrTruncateWithXmlPayload`).
- Fixes mapping collision issues observed when calling endpoints like
  `/rest/seed/workspace:layer.xml` under Spring Boot.

This change ensures compatibility with Spring Boot's stricter handler mapping
rules while maintaining functionality consistent with the base `SeedController` logic.
  • Loading branch information
groldan committed Dec 18, 2024
1 parent 3df0760 commit cdcc3c8
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import java.net.URI;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -57,4 +58,31 @@ protected ResponseEntity<String> testGetRequestContentType(String uri, MediaType
assertThat(response.getHeaders().getContentType()).isEqualTo(expected);
return response;
}

@Test
void testPostSeedDoesNotThrowAmbiguousHandlerMapping() {
String payload =
"""
<seedRequest>
<name>workspace:layer</name>
<srs><number>3857</number></srs>
<zoomStart>0</zoomStart>
<zoomStop>8</zoomStop>
<format>image/png</format>
<type>reseed</type>
<threadCount>2</threadCount>
</seedRequest>
""";
String uri = "/gwc/rest/seed/workspace:layer.xml";

ResponseEntity<String> response = restTemplate.postForEntity(URI.create(uri), payload, String.class);
HttpStatus statusCode = response.getStatusCode();
String body = response.getBody();

// SeedService will throw a 500 error when the layer is not found
// it's ok we just need to check it doesn't result in an "ambiguous handler mapping" error
// see org.geoserver.cloud.gwc.config.services.SeedController
assertThat(statusCode).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(body).contains("Unknown layer workspace:layer");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
import org.geoserver.catalog.Catalog;
import org.geoserver.gwc.controller.GwcUrlHandlerMapping;
import org.geoserver.gwc.layer.GWCGeoServerRESTConfigurationProvider;
import org.geowebcache.rest.controller.SeedController;
import org.geowebcache.rest.converter.GWCConverter;
import org.geowebcache.util.ApplicationContextProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

/**
* The original {@literal geowebcache-rest-context.xml}:
Expand Down Expand Up @@ -51,9 +53,16 @@
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(GWCConverter.class)
@ComponentScan(basePackages = "org.geowebcache.rest")
@ComponentScan(
basePackages = "org.geowebcache.rest",
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SeedController.class))
public class RESTConfigConfiguration {

@Bean
org.geoserver.cloud.gwc.config.services.SeedController seedController() {
return new org.geoserver.cloud.gwc.config.services.SeedController();
}

/**
* The original {@literal geowebcache-rest-context.xml}:
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* (c) 2024 Open Source Geospatial Foundation - all rights reserved This code is licensed under the
* GPL 2.0 license, available at the root application directory.
*/
package org.geoserver.cloud.gwc.config.services;

import java.io.InputStream;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

@Component
@RestController
@RequestMapping(path = "${gwc.context.suffix:}/rest")
public class SeedController extends org.geowebcache.rest.controller.SeedController {

/**
*
* {@inheritDoc}
* <p>
* Override for the {@code path} pattern to exclude {@code .xml} and
* {@code .json} suffixes to avoid an exception like the following with
* {@link #seedOrTruncateWithJsonPayload} and
* {@link #seedOrTruncateWithXmlPayload}:
* <pre>
* <code>
* java.lang.IllegalStateException: Ambiguous handler methods mapped for '.../rest/seed/workspace:layer.xml': {
* public org.springframework.http.ResponseEntity org.geoserver.cloud.gwc.config.services.SeedController.seedOrTruncateWithXmlPayload(...),
* public org.springframework.http.ResponseEntity org.geoserver.cloud.gwc.config.services.SeedController.doPost(...)
* }
* </code>
* </pre>
*/
@Override
@PostMapping("/seed/{layer:^(?!.*\\.(?:xml|json)$).+}")
public ResponseEntity<?> doPost(
HttpServletRequest request,
InputStream inputStream,
@PathVariable String layer,
@RequestParam Map<String, String> params) {

return super.doPost(request, inputStream, layer, params);
}
}

0 comments on commit cdcc3c8

Please sign in to comment.