Skip to content

Commit

Permalink
Merge pull request geoserver#3615 from aaime/ogcapi_qa
Browse files Browse the repository at this point in the history
OgcAPI QA round
  • Loading branch information
aaime authored Jun 27, 2019
2 parents 01cd801 + fcd6652 commit 5707179
Show file tree
Hide file tree
Showing 44 changed files with 1,042 additions and 391 deletions.
7 changes: 7 additions & 0 deletions src/community/ogcapi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.geoserver</groupId>
<artifactId>gs-ows</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.geoserver</groupId>
<artifactId>gs-main</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
/*
* (c) 2019 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*
*/

/*
* (c) 2019 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*
/* (c) 2019 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.api;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.Dispatcher;
import org.geoserver.ows.DispatcherCallback;
Expand All @@ -31,6 +26,7 @@
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
Expand Down Expand Up @@ -88,6 +84,13 @@ protected <T> void writeWithMessageConverters(
ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException,
HttpMessageNotWritableException {
// handle case of null value returned by controller methods
HttpServletResponse servletResponse = outputMessage.getServletResponse();
if (value == null) {
servletResponse.setStatus(HttpStatus.NO_CONTENT.value());
return;
}

HTMLResponseBody htmlResponseBody = returnType.getMethodAnnotation(HTMLResponseBody.class);
MediaType mediaType = getMediaTypeToUse(value, returnType, inputMessage, outputMessage);
HttpMessageConverter converter;
Expand All @@ -96,6 +99,7 @@ protected <T> void writeWithMessageConverters(
converter =
new SimpleHTTPMessageConverter(
value.getClass(),
getServiceClass(returnType),
returnType.getContainingClass(),
loader,
geoServer,
Expand Down Expand Up @@ -131,8 +135,15 @@ public void write(Object value, OutputStream output, Operation operation)
.getHeaders()
.setContentType(
MediaType.parseMediaType(response.getMimeType(value, dr.getOperation())));
response.write(
value, outputMessage.getServletResponse().getOutputStream(), dr.getOperation());
response.write(value, servletResponse.getOutputStream(), dr.getOperation());
}

private Class<?> getServiceClass(MethodParameter returnType) {
APIService apiService = returnType.getContainingClass().getAnnotation(APIService.class);
if (apiService != null) {
return apiService.serviceClass();
}
throw new RuntimeException("Could not find the APIService annotation in the controller");
}

private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
Expand All @@ -141,7 +152,7 @@ private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
return contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
}

protected <T> MediaType getMediaTypeToUse(
public <T> MediaType getMediaTypeToUse(
@Nullable T value,
MethodParameter returnType,
ServletServerHttpRequest inputMessage,
Expand Down Expand Up @@ -177,8 +188,22 @@ protected <T> MediaType getMediaTypeToUse(
} else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
// if we got no indication, see if the method has a default content type, and
// if not, default to JSON as per OGC API expectations
List<MediaType> producibleTypes =
getProducibleMediaTypes(request, valueType, targetType);
if (ContentNegotiationManager.MEDIA_TYPE_ALL_LIST.equals(acceptableTypes)) {
MediaType defaultMediaType =
Optional.ofNullable(
returnType.getMethodAnnotation(DefaultContentType.class))
.map(t -> MediaType.parseMediaType(t.value()))
.orElse(null);
if (defaultMediaType != null) {
acceptableTypes = Collections.singletonList(defaultMediaType);
} else if (producibleTypes.contains(MediaType.APPLICATION_JSON)) {
acceptableTypes = Collections.singletonList(MediaType.APPLICATION_JSON);
} // otherwise let it be free
}
// we want to check if HTML is the first producible without using converters, adding it
// to the mix
producibleTypes.add(MediaType.TEXT_HTML);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/* (c) 2019 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.api;

import java.lang.reflect.InvocationTargetException;
Expand All @@ -12,6 +16,10 @@
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;

/**
* Subclass of {@link WebMvcConfigurationSupport} adding support for dispatching {@link
* DispatcherCallback#operationDispatched} events to callbacks
*/
public class APIConfigurationSupport extends WebMvcConfigurationSupport {

static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.api");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
/*
* (c) 2019 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*
*/

/*
* (c) 2019 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
/* (c) 2019 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.api;
Expand All @@ -17,22 +9,24 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.geoserver.api.features.RFCGeoJSONFeaturesResponse;
import org.springframework.http.MediaType;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.context.request.NativeWebRequest;

/**
* A ContentNegotiationManager using the "f" query parameter as a way to request a few well known
* formats in override to the HTTP Accept header
*/
public class APIContentNegotiationManager extends ContentNegotiationManager {

public APIContentNegotiationManager() {
List<ContentNegotiationStrategy> strategies = new ArrayList<>();
// first use the f parameter
strategies.add(new FormatContentNegotiationStrategy());
strategies.add(new HeaderContentNegotiationStrategy());
strategies.add(new JSONContentNegotiationStrategy());
this.getStrategies().addAll(strategies);
}

Expand All @@ -56,17 +50,4 @@ public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
}
}
}

private static class JSONContentNegotiationStrategy implements ContentNegotiationStrategy {

@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
throws HttpMediaTypeNotAcceptableException {
// default to JSON, allow all
return Arrays.asList(
MediaType.parseMediaType(RFCGeoJSONFeaturesResponse.MIME),
MediaType.APPLICATION_JSON,
MediaType.ALL);
}
}
}
Loading

0 comments on commit 5707179

Please sign in to comment.