diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentEntitiesResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentEntitiesResourceMappings.java index a6ef045ea..5e5b262ae 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentEntitiesResourceMappings.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/PersistentEntitiesResourceMappings.java @@ -16,7 +16,6 @@ package org.springframework.data.rest.core.mapping; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/OrderRepository.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/OrderRepository.java index 9c3d9a467..be170a77b 100644 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/OrderRepository.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/domain/OrderRepository.java @@ -30,7 +30,8 @@ public interface OrderRepository extends CrudRepository { * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object) */ @Override - S save(S entity); + @SuppressWarnings("unchecked") + Order save(Order entity); /* * (non-Javadoc) diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java index ba4268557..9ff0f16e2 100755 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/RepositoryMethodResourceMappingUnitTests.java @@ -113,7 +113,6 @@ public class RepositoryMethodResourceMappingUnitTests { } @Test // DATAREST-467 - @SuppressWarnings("rawtypes") public void returnsDomainTypeAsProjectionSourceType() throws Exception { Method method = PersonRepository.class.getMethod("findByLastname", String.class); diff --git a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java index 613eb5728..4286464db 100755 --- a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java +++ b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java @@ -25,10 +25,10 @@ import java.util.Optional; import org.junit.Test; import org.mockito.Mockito; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.Pageable; import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.support.RepositoryInvoker; @@ -116,13 +116,14 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll @Test // DATAREST-330 public void exposesHeadForCollectionResourceIfExported() throws Exception { ResponseEntity entity = controller.headCollectionResource(getResourceInformation(Person.class), - new DefaultedPageable(null, false)); + new DefaultedPageable(Pageable.unpaged(), false)); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); } @Test(expected = ResourceNotFoundException.class) // DATAREST-330 public void doesNotExposeHeadForCollectionResourceIfNotExported() throws Exception { - controller.headCollectionResource(getResourceInformation(CreditCard.class), new DefaultedPageable(null, false)); + controller.headCollectionResource(getResourceInformation(CreditCard.class), + new DefaultedPageable(Pageable.unpaged(), false)); } @Test // DATAREST-330 diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java index 98c25cd35..2a60e951e 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java @@ -15,8 +15,6 @@ */ package org.springframework.data.rest.webmvc; -import static org.springframework.data.rest.webmvc.ControllerUtils.*; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -81,7 +79,7 @@ class AbstractRepositoryRestController { } else if (source instanceof Iterable) { return entitiesToResources((Iterable) source, assembler, domainType); } else { - return new CollectionModel(EMPTY_RESOURCE_LIST); + return CollectionModel.empty(); } } @@ -103,7 +101,7 @@ class AbstractRepositoryRestController { if (!entities.iterator().hasNext()) { List content = Arrays. asList(WRAPPERS.emptyCollectionOf(domainType)); - return new CollectionModel(content, getDefaultSelfLink()); + return CollectionModel.of(content, getDefaultSelfLink()); } List> resources = new ArrayList>(); @@ -112,7 +110,7 @@ class AbstractRepositoryRestController { resources.add(obj == null ? null : assembler.toModel(obj)); } - return new CollectionModel>(resources, getDefaultSelfLink()); + return CollectionModel.of(resources, getDefaultSelfLink()); } protected Link getDefaultSelfLink() { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ControllerUtils.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ControllerUtils.java index 8433d1f08..bcb77492d 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ControllerUtils.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ControllerUtils.java @@ -18,6 +18,7 @@ package org.springframework.data.rest.webmvc; import java.util.Collections; import java.util.Optional; +import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.RepresentationModel; import org.springframework.http.HttpHeaders; @@ -31,7 +32,12 @@ import org.springframework.util.Assert; */ public class ControllerUtils { - public static final Iterable> EMPTY_RESOURCE_LIST = Collections.emptyList(); + /** + * Use {@link CollectionModel#empty()} instead. + * + * @deprecated since 3.3 + */ + @Deprecated public static final Iterable> EMPTY_RESOURCE_LIST = Collections.emptyList(); public static > ResponseEntity> toResponseEntity( HttpStatus status, HttpHeaders headers, Optional resource) { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java index 65b29c0d2..bcf6e144c 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java @@ -24,10 +24,10 @@ import java.util.List; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.Links; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.server.core.EmbeddedWrapper; import org.springframework.util.Assert; @@ -65,6 +65,7 @@ public class PersistentEntityResource extends EntityModel { * @param links must not be {@literal null}. * @param embeddeds can be {@literal null}. */ + @SuppressWarnings("deprecation") private PersistentEntityResource(PersistentEntity entity, Object content, Iterable links, Iterable embeddeds, boolean isNew, boolean nested) { @@ -205,6 +206,7 @@ public class PersistentEntityResource extends EntityModel { private static class NoLinksResources extends CollectionModel { + @SuppressWarnings("deprecation") public NoLinksResources(Iterable content) { super(content); } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java index 08c59f33a..56e7d1edc 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java @@ -19,7 +19,6 @@ import static org.springframework.http.HttpMethod.*; import java.io.Serializable; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Optional; @@ -199,16 +198,16 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem throw new ResourceNotFoundException(); } - Iterable results = pageable.getPageable() != null ? invoker.invokeFindAll(pageable.getPageable()) + Iterable results = pageable.getPageable() != null // + ? invoker.invokeFindAll(pageable.getPageable()) // : invoker.invokeFindAll(sort); ResourceMetadata metadata = resourceInformation.getResourceMetadata(); Optional baseLink = Optional.of(entityLinks.linkToPagedResource(resourceInformation.getDomainType(), pageable.isDefault() ? null : pageable.getPageable())); - CollectionModel result = toCollectionModel(results, assembler, metadata.getDomainType(), baseLink); - result.add(getCollectionResourceLinks(resourceInformation, pageable)); - return result; + return toCollectionModel(results, assembler, metadata.getDomainType(), baseLink) + .add(getCollectionResourceLinks(resourceInformation, pageable)); } private Links getCollectionResourceLinks(RootResourceInformation resourceInformation, DefaultedPageable pageable) { @@ -226,7 +225,6 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem } @ResponseBody - @SuppressWarnings({ "unchecked" }) @RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/x-spring-data-compact+json", "text/uri-list" }) public CollectionModel getCollectionResourceCompact(@QuerydslPredicate RootResourceInformation resourceinformation, @@ -234,18 +232,17 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem throws ResourceNotFoundException, HttpRequestMethodNotSupportedException { CollectionModel resources = getCollectionResource(resourceinformation, pageable, sort, assembler); - Links links = resources.getLinks(); - for (EntityModel resource : ((CollectionModel>) resources).getContent()) { - PersistentEntityResource persistentEntityResource = (PersistentEntityResource) resource; - links = links.and(resourceLink(resourceinformation, persistentEntityResource)); - } + Links links = resources.getContent().stream() // + .map(PersistentEntityResource.class::cast) // + .map(it -> resourceLink(resourceinformation, it)) // + .reduce(resources.getLinks(), Links::and, Links::and); - if (resources instanceof PagedModel) { - return new PagedModel(Collections.emptyList(), ((PagedModel) resources).getMetadata(), links); - } else { - return new CollectionModel(Collections.emptyList(), links); - } + CollectionModel model = resources instanceof PagedModel // + ? PagedModel.empty(((PagedModel) resources).getMetadata()) // + : CollectionModel.empty(); + + return model.add(links); } /** diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java index 04da63309..eed60d59c 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java @@ -25,7 +25,6 @@ import lombok.RequiredArgsConstructor; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -252,7 +251,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro @RequestBody(required = false) CollectionModel incoming, @BackendId Serializable id, @PathVariable String property) throws Exception { - CollectionModel source = incoming == null ? new CollectionModel(Collections.emptyList()) : incoming; + CollectionModel source = incoming == null ? CollectionModel.empty() : incoming; RepositoryInvoker invoker = resourceInformation.getInvoker(); Function> handler = prop -> { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java index 14965fccd..578899308 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java @@ -15,8 +15,6 @@ */ package org.springframework.data.rest.webmvc; -import static org.springframework.data.rest.webmvc.ControllerUtils.*; - import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; @@ -266,7 +264,7 @@ class RepositorySearchController extends AbstractRepositoryRestController { links.add(resourceLink(resourceInformation, res)); } - return new CollectionModel>(EMPTY_RESOURCE_LIST, links); + return CollectionModel.empty(links); } /** diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/JsonPatchHandler.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/JsonPatchHandler.java index 14c2615c3..37e5b0950 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/JsonPatchHandler.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/JsonPatchHandler.java @@ -22,6 +22,7 @@ import org.springframework.data.rest.webmvc.RestMediaTypes; import org.springframework.data.rest.webmvc.json.DomainObjectReader; import org.springframework.data.rest.webmvc.json.patch.JsonPatchPatchConverter; import org.springframework.data.rest.webmvc.json.patch.Patch; +import org.springframework.data.rest.webmvc.util.InputStreamHttpInputMessage; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.util.Assert; @@ -95,7 +96,7 @@ class JsonPatchHandler { return reader.read(source, existingObject, mapper); } - T applyPut(ObjectNode source, T existingObject) { + T applyPut(ObjectNode source, T existingObject) throws Exception { return reader.readPut(source, existingObject, mapper); } @@ -112,7 +113,8 @@ class JsonPatchHandler { return new JsonPatchPatchConverter(mapper).convert(mapper.readTree(source)); } catch (Exception o_O) { throw new HttpMessageNotReadableException( - String.format("Could not read PATCH operations! Expected %s!", RestMediaTypes.JSON_PATCH_JSON), o_O); + String.format("Could not read PATCH operations! Expected %s!", RestMediaTypes.JSON_PATCH_JSON), o_O, + InputStreamHttpInputMessage.of(source)); } } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index 604e9b508..97842b16f 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -15,7 +15,6 @@ */ package org.springframework.data.rest.webmvc.config; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -23,7 +22,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Properties; import java.util.Set; import org.springframework.beans.factory.BeanClassLoaderAware; @@ -33,7 +31,6 @@ import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -43,7 +40,6 @@ import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; import org.springframework.core.Ordered; import org.springframework.core.convert.ConversionService; -import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.data.auditing.AuditableBeanWrapperFactory; import org.springframework.data.auditing.MappingAuditableBeanWrapperFactory; @@ -410,21 +406,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon new ValueTypeSchemaPropertyCustomizerFactory(repositoryInvokerFactory(defaultConversionService()))); } - private final Properties lookupDefaultMessages() { - - try { - - PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); - propertiesFactoryBean.setLocation(new ClassPathResource("rest-default-messages.properties")); - propertiesFactoryBean.afterPropertiesSet(); - - return propertiesFactoryBean.getObject(); - - } catch (IOException o_O) { - throw new IllegalStateException("Unable to resolve default rest-default-messages.properties!", o_O); - } - } - /** * The Jackson {@link ObjectMapper} used internally. * diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/convert/UriListHttpMessageConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/convert/UriListHttpMessageConverter.java index 68a255f66..4e4fd8c15 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/convert/UriListHttpMessageConverter.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/convert/UriListHttpMessageConverter.java @@ -15,25 +15,25 @@ */ package org.springframework.data.rest.webmvc.convert; +import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Scanner; import org.springframework.core.convert.converter.Converter; -import org.springframework.hateoas.Link; -import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.Links; +import org.springframework.hateoas.RepresentationModel; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; -import org.springframework.util.StringUtils; /** * {@link Converter} to render all {@link Link}s contained in a {@link ResourceSupport} as {@code text/uri-list} and @@ -43,7 +43,7 @@ import org.springframework.util.StringUtils; * @author Greg Turnquist * @author Oliver Gierke */ -public class UriListHttpMessageConverter implements HttpMessageConverter { +public class UriListHttpMessageConverter implements HttpMessageConverter> { private static final List MEDIA_TYPES = new ArrayList(); @@ -57,10 +57,10 @@ public class UriListHttpMessageConverter implements HttpMessageConverter clazz, MediaType mediaType) { - if (null == mediaType) { - return false; - } - return RepresentationModel.class.isAssignableFrom(clazz) && mediaType.getSubtype().contains("uri-list"); + + return null != mediaType // + && RepresentationModel.class.isAssignableFrom(clazz) // + && mediaType.getSubtype().contains("uri-list"); } /* @@ -86,28 +86,17 @@ public class UriListHttpMessageConverter implements HttpMessageConverter clazz, HttpInputMessage inputMessage) + public RepresentationModel read(Class> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { - List links = new ArrayList(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputMessage.getBody()))) { - Scanner scanner = new Scanner(inputMessage.getBody()); + Links links = reader.lines() // + .map(Link::of) // + .reduce(Links.NONE, Links::and, Links::and); - try { - - while (scanner.hasNextLine()) { - - String line = scanner.nextLine(); - if (StringUtils.hasText(line)) { - links.add(Link.of(line)); - } - } - - } finally { - scanner.close(); + return CollectionModel.empty(links); } - - return new CollectionModel(Collections.emptyList(), links); } /* @@ -115,7 +104,7 @@ public class UriListHttpMessageConverter implements HttpMessageConverter resource, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputMessage.getBody())); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java index 62c65efd8..e46614e60 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java @@ -42,6 +42,7 @@ import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.rest.webmvc.mapping.Associations; +import org.springframework.data.rest.webmvc.util.InputStreamHttpInputMessage; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -88,7 +89,7 @@ public class DomainObjectReader { try { return doMerge((ObjectNode) mapper.readTree(source), target, mapper); } catch (Exception o_O) { - throw new HttpMessageNotReadableException("Could not read payload!", o_O); + throw new HttpMessageNotReadableException("Could not read payload!", o_O, InputStreamHttpInputMessage.of(source)); } } @@ -101,24 +102,14 @@ public class DomainObjectReader { * @return */ @SuppressWarnings("unchecked") - public T readPut(final ObjectNode source, T target, final ObjectMapper mapper) { + public T readPut(final ObjectNode source, T target, final ObjectMapper mapper) throws Exception { Assert.notNull(source, "ObjectNode must not be null!"); Assert.notNull(target, "Existing object instance must not be null!"); Assert.notNull(mapper, "ObjectMapper must not be null!"); - Class type = target.getClass(); - - entities.getRequiredPersistentEntity(type); - - try { - - Object intermediate = mapper.readerFor(target.getClass()).readValue(source); - return (T) mergeForPut(intermediate, target, mapper); - - } catch (Exception o_O) { - throw new HttpMessageNotReadableException("Could not read payload!", o_O); - } + Object intermediate = mapper.readerFor(target.getClass()).readValue(source); + return (T) mergeForPut(intermediate, target, mapper); } /** @@ -186,6 +177,17 @@ public class DomainObjectReader { } } + /** + * Only for internal use. To be removed in 3.4. + * + * @param + * @param source + * @param target + * @param mapper + * @return + * @deprecated + */ + @Deprecated public T merge(ObjectNode source, T target, ObjectMapper mapper) { try { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMetadata.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMetadata.java index 67c796b52..dfd1734ba 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMetadata.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JacksonMetadata.java @@ -69,7 +69,7 @@ public class JacksonMetadata implements Iterable { BeanDescription description = serializationConfig.introspect(javaType); this.definitions = description.findProperties(); - this.isValue = description.findJsonValueMethod() != null; + this.isValue = description.findJsonValueAccessor() != null; DeserializationConfig deserializationConfig = mapper.getDeserializationConfig(); JavaType deserializationType = deserializationConfig.constructType(type); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java index 0c530032e..a02d63439 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java @@ -195,6 +195,7 @@ public class PersistentEntityJackson2Module extends SimpleModule { return; } + @SuppressWarnings("deprecation") EntityModel resourceToRender = new EntityModel(resource.getContent(), links) { @JsonUnwrapped @@ -689,7 +690,7 @@ public class PersistentEntityJackson2Module extends SimpleModule { ResourceMetadata metadata = associations.getMetadataFor(value.getTargetClass()); Links links = metadata.isExported() ? collector.getLinksFor(target) : Links.NONE; - EntityModel resource = invoker.invokeProcessorsFor(new EntityModel(value, links)); + EntityModel resource = invoker.invokeProcessorsFor(EntityModel.of(value, links)); return new ProjectionResource(resource.getContent(), resource.getLinks()); } @@ -697,6 +698,7 @@ public class PersistentEntityJackson2Module extends SimpleModule { static class ProjectionResource extends EntityModel { + @SuppressWarnings("deprecation") ProjectionResource(TargetAware projection, Iterable links) { super(new ProjectionResourceContent(projection, projection.getClass().getInterfaces()[0]), links); } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/NestedLinkCollectingAssociationHandler.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/NestedLinkCollectingAssociationHandler.java index ecdbfe69b..df3e9b770 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/NestedLinkCollectingAssociationHandler.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/NestedLinkCollectingAssociationHandler.java @@ -32,8 +32,8 @@ import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.rest.core.mapping.ResourceMapping; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; -import org.springframework.hateoas.server.EntityLinks; import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.EntityLinks; /** * @author Oliver Gierke @@ -43,7 +43,7 @@ public class NestedLinkCollectingAssociationHandler implements SimpleAssociation private final EntityLinks entityLinks; private final PersistentEntities entities; - private final PersistentPropertyAccessor accessor; + private final PersistentPropertyAccessor accessor; private final ResourceMappings mappings; private final @Getter List links = new ArrayList(); @@ -70,10 +70,10 @@ public class NestedLinkCollectingAssociationHandler implements SimpleAssociation links.add(entityLinks.linkForItemResource(element.getClass(), identifierAccessor.getIdentifier()) .withRel(propertyMapping.getRel())); - } } else { + PersistentEntity entity = entities.getRequiredPersistentEntity(propertyValue.getClass()); IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(propertyValue); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DefaultedPageable.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DefaultedPageable.java index 932118712..7e9ea33fe 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DefaultedPageable.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DefaultedPageable.java @@ -15,38 +15,28 @@ */ package org.springframework.data.rest.webmvc.support; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.Value; + import org.springframework.data.domain.Pageable; /** - * Value object to capture a {@link Pageable} as well it is the default one configured. + * Value object to capture a {@link Pageable} as well whether it is the default one configured. * * @author Oliver Gierke */ +@Value public class DefaultedPageable { - private final Pageable pageable; - private final boolean isDefault; - - /** - * Creates a new {@link DefaultedPageable} with the given {@link Pageable} and default flag. - * - * @param pageable can be {@literal null}. - * @param isDefault - */ - public DefaultedPageable(Pageable pageable, boolean isDefault) { - - this.pageable = pageable; - this.isDefault = isDefault; - } - /** * Returns the delegate {@link Pageable}. * * @return can be {@literal null}. */ - public Pageable getPageable() { - return pageable; - } + private final @NonNull Pageable pageable; + private final @Getter(value = AccessLevel.NONE) boolean isDefault; /** * Returns whether the contained {@link Pageable} is the default one configured. @@ -56,4 +46,14 @@ public class DefaultedPageable { public boolean isDefault() { return isDefault; } + + /** + * Returns {@link Pageable#unpaged()} if the contained {@link Pageable} is the default one. + * + * @return will never be {@literal null}. + * @since 3.3 + */ + public Pageable unpagedIfDefault() { + return isDefault ? Pageable.unpaged() : pageable; + } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java index a3d219e2f..53571b258 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java @@ -167,7 +167,7 @@ public final class ETag { Assert.notNull(entity, "PersistentEntity must not be null!"); Assert.notNull(bean, "Target bean must not be null!"); - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(bean); + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(bean); return Optional.ofNullable(entity.getVersionProperty())// .map(it -> accessor.getProperty(it))// diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/util/InputStreamHttpInputMessage.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/util/InputStreamHttpInputMessage.java new file mode 100644 index 000000000..883dd7679 --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/util/InputStreamHttpInputMessage.java @@ -0,0 +1,44 @@ +/* + * Copyright 2020 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 + * + * https://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.util; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.io.InputStream; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpInputMessage; + +/** + * {@link HttpInputMessage} based on a plain {@link InputStream}, i.e. exposing no headers. + * + * @author Oliver Drotbohm + */ +@RequiredArgsConstructor(staticName = "of") +public class InputStreamHttpInputMessage implements HttpInputMessage { + + private final @Getter InputStream body; + + /* + * (non-Javadoc) + * @see org.springframework.http.HttpMessage#getHeaders() + */ + @Override + public HttpHeaders getHeaders() { + return HttpHeaders.EMPTY; + } +} diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java index 549653f92..bbca84fda 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java @@ -20,8 +20,8 @@ import static org.mockito.Mockito.*; import org.junit.Test; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.support.AopUtils; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; -import org.springframework.util.ClassUtils; /** * Unit tests for {@link BasePathAwareHandlerMapping}. @@ -54,7 +54,7 @@ public class BasePathAwareHandlerMappingUnitTests { ProxyFactory factory = new ProxyFactory(source); Object proxy = factory.getProxy(); - assertThat(ClassUtils.isCglibProxy(proxy)).isTrue(); + assertThat(AopUtils.isCglibProxy(proxy)).isTrue(); return proxy.getClass(); } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/PersistentEntityResourceUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/PersistentEntityResourceUnitTests.java index 62b4c9fc6..d04e7d9a0 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/PersistentEntityResourceUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/PersistentEntityResourceUnitTests.java @@ -25,9 +25,9 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.data.mapping.PersistentEntity; +import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; -import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.server.core.EmbeddedWrapper; import org.springframework.hateoas.server.core.EmbeddedWrappers; @@ -50,7 +50,7 @@ public class PersistentEntityResourceUnitTests { EmbeddedWrappers wrappers = new EmbeddedWrappers(false); EmbeddedWrapper wrapper = wrappers.wrap("Embedded", LinkRelation.of("foo")); - this.resources = new CollectionModel(Collections.singleton(wrapper)); + this.resources = CollectionModel.of(Collections.singleton(wrapper)); } @Test(expected = IllegalArgumentException.class) // DATAREST-317 diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceControllerUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceControllerUnitTests.java index 25279dd6d..7303c9e5d 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceControllerUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceControllerUnitTests.java @@ -44,8 +44,8 @@ import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.core.mapping.ResourceType; import org.springframework.data.rest.core.mapping.SupportedHttpMethods; import org.springframework.data.web.PagedResourcesAssembler; -import org.springframework.hateoas.Link; import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.Link; import org.springframework.http.HttpMethod; /** @@ -84,7 +84,7 @@ public class RepositoryPropertyReferenceControllerUnitTests { doReturn(new Sample()).when(invoker).invokeSave(any(Object.class)); RootResourceInformation information = new RootResourceInformation(metadata, entity, invoker); - CollectionModel request = new CollectionModel(Collections.emptySet(), Link.of("/reference/some-id")); + CollectionModel request = CollectionModel.empty(Link.of("/reference/some-id")); controller.createPropertyReference(information, HttpMethod.POST, request, 4711, "references"); diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestExceptionHandlerUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestExceptionHandlerUnitTests.java index ae7643981..cf3147036 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestExceptionHandlerUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestExceptionHandlerUnitTests.java @@ -30,6 +30,7 @@ import org.springframework.data.rest.webmvc.support.ExceptionMessage; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.mock.http.MockHttpInputMessage; /** * Unit tests for {@link RepositoryRestExceptionHandler}. @@ -60,7 +61,7 @@ public class RepositoryRestExceptionHandlerUnitTests { public void handlesHttpMessageNotReadableException() { ResponseEntity result = HANDLER - .handleNotReadable(new HttpMessageNotReadableException("Message!")); + .handleNotReadable(new HttpMessageNotReadableException("Message!", new MockHttpInputMessage(new byte[0]))); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java index b4fdffb6f..27cac021f 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java @@ -27,6 +27,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.support.AopUtils; import org.springframework.data.domain.Sort; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.Path; @@ -41,7 +42,6 @@ import org.springframework.data.rest.webmvc.support.DefaultedPageable; import org.springframework.http.HttpHeaders; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; -import org.springframework.util.ClassUtils; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.method.HandlerMethod; import org.springframework.web.util.pattern.PathPattern; @@ -308,7 +308,7 @@ public class RepositoryRestHandlerMappingUnitTests { ProxyFactory factory = new ProxyFactory(source); Object proxy = factory.getProxy(); - assertThat(ClassUtils.isCglibProxy(proxy)).isTrue(); + assertThat(AopUtils.isCglibProxy(proxy)).isTrue(); return proxy.getClass(); } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java index 7d592a46e..1d59b25fa 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java @@ -36,7 +36,6 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; - import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Immutable; @@ -59,6 +58,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; @@ -172,7 +172,7 @@ public class DomainObjectReaderUnitTests { assertThat(((Map) object).get("c")).isEqualTo("2"); } - @Test(expected = IllegalArgumentException.class) // DATAREST-701 + @Test(expected = JsonMappingException.class) // DATAREST-701 public void rejectsMergingUnknownDomainObject() throws Exception { ObjectMapper mapper = new ObjectMapper(); @@ -486,6 +486,7 @@ public class DomainObjectReaderUnitTests { ObjectMapper mapper = new ObjectMapper(); JsonNode node = mapper.readTree("{ \"enums\" : [ \"SECOND\", \"FIRST\" ] }"); + @SuppressWarnings("deprecation") CollectionOfEnumWithMethods result = reader.merge((ObjectNode) node, sample, mapper); assertThat(result.enums).containsExactly(SampleEnum.SECOND, SampleEnum.FIRST); @@ -568,18 +569,19 @@ public class DomainObjectReaderUnitTests { ObjectMapper mapper = new ObjectMapper(); ObjectNode source = (ObjectNode) mapper.readTree("{ \"lastLogin\" : null, \"email\" : \"bar@foo.com\"}"); + @SuppressWarnings("deprecation") SampleUser result = reader.merge(source, user, mapper); assertThat(result.lastLogin).isNotNull(); assertThat(result.email).isEqualTo("foo@bar.com"); } - + @Test // DATAREST-1068 public void arraysCanBeResizedDuringMerge() throws Exception { ObjectMapper mapper = new ObjectMapper(); - ArrayHolder target = new ArrayHolder(new String[] { }); + ArrayHolder target = new ArrayHolder(new String[] {}); JsonNode node = mapper.readTree("{ \"array\" : [ \"new\" ] }"); - + ArrayHolder updated = reader.doMerge((ObjectNode) node, target, mapper); assertThat(updated.array).containsExactly("new"); } @@ -807,7 +809,7 @@ public class DomainObjectReaderUnitTests { static class WithNullCollection { List strings; } - + // DATAREST-1068 @Value static class ArrayHolder { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java index 7108c8544..a9d201fbe 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java @@ -77,7 +77,7 @@ public class ProjectionJacksonIntegrationTests { customer.address = new Address(); CustomerProjection projection = factory.createProjection(CustomerProjection.class, customer); - CollectionModel resources = new CollectionModel(Arrays.asList(projection)); + CollectionModel resources = CollectionModel.of(Arrays.asList(projection)); String result = mapper.writeValueAsString(resources); diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mapping/AssociationsUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mapping/AssociationsUnitTests.java index 0138e93c1..22657f9db 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mapping/AssociationsUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mapping/AssociationsUnitTests.java @@ -147,6 +147,7 @@ public class AssociationsUnitTests { assertThat(links).contains(Link.of("/relatedAndExported{?" + projectionParameterName + "}", "relatedAndExported")); } + @SuppressWarnings({ "rawtypes", "unchecked" }) private Association> getAssociation(Class type, String name) { KeyValuePersistentEntity> rootEntity = mappingContext