DATAREST-279 - Marshalling support for new geo types.
Added StringTo(Distance|Point)Converter implementations to be able to submit Distances and Points via URIs. See the JavaDoc of those classes for more details. RepositoryRestMvcConfiguration now also registers the GeoModule provided by Spring Data Commons with the ObjectMapper registered for our custom HttpMessageConverters.
This commit is contained in:
@@ -37,6 +37,9 @@ import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.GeoModule;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
@@ -60,6 +63,8 @@ import org.springframework.data.rest.webmvc.RepositoryRestController;
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter;
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestHandlerMapping;
|
||||
import org.springframework.data.rest.webmvc.ServerHttpRequestMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.convert.StringToDistanceConverter;
|
||||
import org.springframework.data.rest.webmvc.convert.StringToPointConverter;
|
||||
import org.springframework.data.rest.webmvc.convert.UriListHttpMessageConverter;
|
||||
import org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module;
|
||||
@@ -167,6 +172,15 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
conversionService.addConverter(UUIDConverter.INSTANCE);
|
||||
configureConversionService(conversionService);
|
||||
|
||||
if (!conversionService.canConvert(String.class, Point.class)) {
|
||||
conversionService.addConverter(StringToPointConverter.INSTANCE);
|
||||
}
|
||||
|
||||
if (!conversionService.canConvert(String.class, Distance.class)) {
|
||||
conversionService.addConverter(StringToDistanceConverter.INSTANCE);
|
||||
}
|
||||
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
@@ -562,12 +576,15 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
peraResolver, backendIdHandlerMethodArgumentResolver());
|
||||
}
|
||||
|
||||
@Autowired GeoModule geoModule;
|
||||
|
||||
private ObjectMapper basicObjectMapper() {
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
|
||||
// Our special PersistentEntityResource Module
|
||||
objectMapper.registerModule(persistentEntityJackson2Module());
|
||||
objectMapper.registerModule(geoModule);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
Jackson2DatatypeHelper.configureObjectMapper(objectMapper);
|
||||
// Configure custom Modules
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.convert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Converter to create {@link Distance} instances from {@link String} representations. The supported format is a decimal
|
||||
* followed by whitespace and a metric abbreviation. We currently support the following abbreviations:
|
||||
* {@value #SUPPORTED_METRICS}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public enum StringToDistanceConverter implements Converter<String, Distance> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private static final Map<String, Metric> SUPPORTED_METRICS;
|
||||
private static final String INVALID_DISTANCE = "Expected double amount optionally followed by a metrics abbreviation (%s) but got '%s'!";
|
||||
|
||||
static {
|
||||
|
||||
Map<String, Metric> metrics = new LinkedHashMap<String, Metric>();
|
||||
metrics.put("km", Metrics.KILOMETERS);
|
||||
metrics.put("miles", Metrics.MILES);
|
||||
metrics.put("mile", Metrics.MILES);
|
||||
|
||||
SUPPORTED_METRICS = Collections.unmodifiableMap(metrics);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Distance convert(String source) {
|
||||
|
||||
source = source.trim();
|
||||
|
||||
for (Entry<String, Metric> metric : SUPPORTED_METRICS.entrySet()) {
|
||||
if (source.endsWith(metric.getKey())) {
|
||||
return fromString(source, metric);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return new Distance(Double.parseDouble(source));
|
||||
} catch (NumberFormatException o_O) {
|
||||
throw new IllegalArgumentException(String.format(INVALID_DISTANCE,
|
||||
StringUtils.collectionToCommaDelimitedString(SUPPORTED_METRICS.keySet()), source));
|
||||
}
|
||||
}
|
||||
|
||||
private Distance fromString(String source, Entry<String, Metric> metric) {
|
||||
|
||||
String amountString = source.substring(0, source.indexOf(metric.getKey()));
|
||||
|
||||
try {
|
||||
return new Distance(Double.parseDouble(amountString), metric.getValue());
|
||||
} catch (NumberFormatException o_O) {
|
||||
throw new IllegalArgumentException(String.format(INVALID_DISTANCE,
|
||||
StringUtils.collectionToCommaDelimitedString(SUPPORTED_METRICS.keySet()), source));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.convert;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
|
||||
import org.springframework.data.geo.Point;
|
||||
|
||||
/**
|
||||
* Converter to parse two comma-separated doubles into a {@link Point}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public enum StringToPointConverter implements Converter<String, Point> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
public static final ConvertiblePair CONVERTIBLE = new ConvertiblePair(String.class, Point.class);
|
||||
|
||||
private static final String INVALID_FORMAT = "Expected two doubles separated by a semicolon but got '%s'!";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Point convert(String source) {
|
||||
|
||||
String[] parts = source.split(",");
|
||||
|
||||
if (parts.length != 2) {
|
||||
throw new IllegalArgumentException(String.format(INVALID_FORMAT, source));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
double latitude = Double.parseDouble(parts[0]);
|
||||
double longitude = Double.parseDouble(parts[1]);
|
||||
|
||||
return new Point(latitude, longitude);
|
||||
|
||||
} catch (NumberFormatException o_O) {
|
||||
throw new IllegalArgumentException(String.format(INVALID_FORMAT, source), o_O);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.convert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.rest.webmvc.convert.StringToDistanceConverter.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link StringToDistanceConverter}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class StringToDistanceConverterUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAREST-279
|
||||
*/
|
||||
@Test
|
||||
public void parsesDistanceFromString() {
|
||||
|
||||
Distance reference = new Distance(10.8, Metrics.KILOMETERS);
|
||||
|
||||
assertThat(INSTANCE.convert("10.8km"), is(reference));
|
||||
assertThat(INSTANCE.convert(" 10.8km"), is(reference));
|
||||
assertThat(INSTANCE.convert(" 10.8 km"), is(reference));
|
||||
assertThat(INSTANCE.convert(" 10.8 km "), is(reference));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-279
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsArbitraryNonsense() {
|
||||
INSTANCE.convert("foo");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-279
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsUnsupportedMetric() {
|
||||
INSTANCE.convert("10.8cm");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.convert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.rest.webmvc.convert.StringToPointConverter.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.geo.Point;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link StringToPointConverter}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class StringToPointConverterUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAREST-279
|
||||
*/
|
||||
@Test
|
||||
public void parsesPointFromString() {
|
||||
|
||||
Point reference = new Point(10.8, 20.9);
|
||||
|
||||
assertThat(INSTANCE.convert("10.8,20.9"), is(reference));
|
||||
assertThat(INSTANCE.convert(" 10.8,20.9 "), is(reference));
|
||||
assertThat(INSTANCE.convert(" 10.8 ,20.9"), is(reference));
|
||||
assertThat(INSTANCE.convert(" 10.8 , 20.9 "), is(reference));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-279
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsArbitraryNonsense() {
|
||||
INSTANCE.convert("foo");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-279
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsMoreThanTwoCoordinates() {
|
||||
INSTANCE.convert("10.8,20.9,30.10");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-279
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsInvalidCoordinate() {
|
||||
INSTANCE.convert("10.8,foo");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user