Skip to content

Commit 898b368

Browse files
committed
Fix potential NullPointerException in ProjectionSerializer.
Fixes GH-1947.
1 parent 20a1218 commit 898b368

File tree

4 files changed

+25
-8
lines changed

4 files changed

+25
-8
lines changed

spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.rest.core.mapping;
1717

1818
import org.springframework.data.util.Streamable;
19+
import org.springframework.lang.Nullable;
1920

2021
/**
2122
* @author Oliver Gierke
@@ -28,6 +29,7 @@ public interface ResourceMappings extends Streamable<ResourceMetadata> {
2829
* @param type must not be {@literal null}.
2930
* @return the {@link ResourceMetadata} if available or {@literal null} otherwise.
3031
*/
32+
@Nullable
3133
ResourceMetadata getMetadataFor(Class<?> type);
3234

3335
/**

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,11 @@
3434
import org.springframework.data.mapping.PersistentProperty;
3535
import org.springframework.data.mapping.context.PersistentEntities;
3636
import org.springframework.data.projection.TargetAware;
37-
import org.springframework.data.repository.support.Repositories;
3837
import org.springframework.data.repository.support.RepositoryInvoker;
3938
import org.springframework.data.repository.support.RepositoryInvokerFactory;
4039
import org.springframework.data.rest.core.UriToEntityConverter;
41-
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
42-
import org.springframework.data.rest.core.mapping.ResourceMappings;
4340
import org.springframework.data.rest.core.mapping.ResourceMetadata;
4441
import org.springframework.data.rest.core.support.EntityLookup;
45-
import org.springframework.data.rest.core.support.SelfLinkProvider;
4642
import org.springframework.data.rest.webmvc.EmbeddedResourcesAssembler;
4743
import org.springframework.data.rest.webmvc.PersistentEntityResource;
4844
import org.springframework.data.rest.webmvc.mapping.Associations;
@@ -649,7 +645,7 @@ static class ProjectionSerializer extends StdSerializer<TargetAware> {
649645
* @param invoker must not be {@literal null}.
650646
* @param unwrapping
651647
*/
652-
private ProjectionSerializer(LinkCollector collector, Associations mappings,
648+
ProjectionSerializer(LinkCollector collector, Associations mappings,
653649
RepresentationModelProcessorInvoker invoker, boolean unwrapping) {
654650

655651
super(TargetAware.class);
@@ -694,11 +690,11 @@ public JsonSerializer<TargetAware> unwrappingSerializer(NameTransformer unwrappe
694690
* @param value must not be {@literal null}.
695691
* @return
696692
*/
697-
private ProjectionResource toModel(TargetAware value) {
693+
ProjectionResource toModel(TargetAware value) {
698694

699695
Object target = value.getTarget();
700696
ResourceMetadata metadata = associations.getMetadataFor(value.getTargetClass());
701-
Links links = metadata.isExported() ? collector.getLinksFor(target) : Links.NONE;
697+
Links links = metadata != null && metadata.isExported() ? collector.getLinksFor(target) : Links.NONE;
702698

703699
EntityModel<TargetAware> resource = invoker.invokeProcessorsFor(EntityModel.of(value, links));
704700

@@ -708,7 +704,6 @@ private ProjectionResource toModel(TargetAware value) {
708704

709705
static class ProjectionResource extends EntityModel<ProjectionResourceContent> {
710706

711-
@SuppressWarnings("deprecation")
712707
ProjectionResource(TargetAware projection, Iterable<Link> links) {
713708
super(new ProjectionResourceContent(projection, projection.getClass().getInterfaces()[0]), links);
714709
}

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/mapping/Associations.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.hateoas.TemplateVariable;
3434
import org.springframework.hateoas.TemplateVariables;
3535
import org.springframework.hateoas.UriTemplate;
36+
import org.springframework.lang.Nullable;
3637
import org.springframework.util.Assert;
3738

3839
/**
@@ -94,6 +95,7 @@ public List<Link> getLinksFor(Association<? extends PersistentProperty<?>> assoc
9495
* @param type must not be {@literal null}.
9596
* @return
9697
*/
98+
@Nullable
9799
public ResourceMetadata getMetadataFor(Class<?> type) {
98100

99101
Assert.notNull(type, "Type must not be null");

spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2ModuleUnitTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
4040
import org.springframework.data.mapping.PersistentProperty;
4141
import org.springframework.data.mapping.context.PersistentEntities;
42+
import org.springframework.data.projection.TargetAware;
4243
import org.springframework.data.repository.support.RepositoryInvoker;
4344
import org.springframework.data.repository.support.RepositoryInvokerFactory;
4445
import org.springframework.data.rest.core.UriToEntityConverter;
@@ -52,8 +53,10 @@
5253
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.LookupObjectSerializer;
5354
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.NestedEntitySerializer;
5455
import org.springframework.data.rest.webmvc.mapping.Associations;
56+
import org.springframework.data.rest.webmvc.mapping.DefaultLinkCollector;
5557
import org.springframework.data.rest.webmvc.support.ExcerptProjector;
5658
import org.springframework.hateoas.EntityModel;
59+
import org.springframework.hateoas.RepresentationModel;
5760
import org.springframework.hateoas.UriTemplate;
5861
import org.springframework.hateoas.server.EntityLinks;
5962
import org.springframework.hateoas.server.mvc.RepresentationModelProcessorInvoker;
@@ -263,6 +266,19 @@ void doesNotWrapValuesWithoutUnwrappingSerializer() {
263266
assertThatNoException().isThrownBy(() -> mapper.writeValueAsString(model));
264267
}
265268

269+
@Test // GH-1947
270+
void projectionsHandleMissingMetadata() {
271+
272+
var collector = new DefaultLinkCollector(persistentEntities, selfLinks, associations);
273+
var invoker = mock(RepresentationModelProcessorInvoker.class);
274+
when(invoker.invokeProcessorsFor(any(RepresentationModel.class)))
275+
.then(invocation -> invocation.getArgument(0));
276+
277+
var serializer = new PersistentEntityJackson2Module.ProjectionSerializer(collector, associations, invoker, false);
278+
279+
assertThatNoException().isThrownBy(() -> serializer.toModel(mock(SampleProjection.class)));
280+
}
281+
266282
/**
267283
* @author Oliver Gierke
268284
*/
@@ -365,4 +381,6 @@ public void serialize(CustomType value, JsonGenerator gen, SerializerProvider pr
365381
gen.writeEndObject();
366382
}
367383
}
384+
385+
interface SampleProjection extends TargetAware {}
368386
}

0 commit comments

Comments
 (0)