Skip to content

Commit 65d4d71

Browse files
committed
Support for jMolecules Identifier types.
We now properly handle jMolecules Identifier instances by integration with the corresponding Converter implementations provided by the jmolecules-spring integrations library. Fixes GH-1982.
1 parent e9bc98a commit 65d4d71

File tree

3 files changed

+178
-1
lines changed

3 files changed

+178
-1
lines changed

Diff for: spring-data-rest-webmvc/pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@
3535
<groupId>org.springframework</groupId>
3636
<artifactId>spring-webmvc</artifactId>
3737
</dependency>
38+
39+
<dependency>
40+
<groupId>org.jmolecules.integrations</groupId>
41+
<artifactId>jmolecules-spring</artifactId>
42+
<version>${jmolecules-integration}</version>
43+
<optional>true</optional>
44+
</dependency>
3845

3946
<dependency>
4047
<groupId>javax.servlet</groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.rest.webmvc.config;
17+
18+
import java.io.Serializable;
19+
import java.util.function.Supplier;
20+
21+
import org.jmolecules.ddd.types.Entity;
22+
import org.jmolecules.ddd.types.Identifier;
23+
import org.jmolecules.spring.IdentifierToPrimitivesConverter;
24+
import org.jmolecules.spring.PrimitivesToIdentifierConverter;
25+
import org.springframework.beans.factory.ObjectFactory;
26+
import org.springframework.beans.factory.annotation.Qualifier;
27+
import org.springframework.context.annotation.Bean;
28+
import org.springframework.context.annotation.Configuration;
29+
import org.springframework.context.annotation.Lazy;
30+
import org.springframework.core.ResolvableType;
31+
import org.springframework.core.convert.ConversionService;
32+
import org.springframework.core.convert.TypeDescriptor;
33+
import org.springframework.core.convert.support.ConfigurableConversionService;
34+
import org.springframework.data.mapping.PersistentEntity;
35+
import org.springframework.data.mapping.PersistentProperty;
36+
import org.springframework.data.mapping.context.PersistentEntities;
37+
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
38+
import org.springframework.format.FormatterRegistry;
39+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
40+
41+
/**
42+
* Configuration for JMolecules integration. Registers the following components:
43+
* <ul>
44+
* <li>{@link IdentifierToPrimitivesConverter} and {@link PrimitivesToIdentifierConverter} in case not already present
45+
* in the Spring MVC {@link ConversionService}</li>
46+
* <li>the same converters on the {@link ConversionService} exposed to {@link RepositoryRestConfigurer}.</li>
47+
* <li>a {@link BackendIdConverter} using those converters</li>
48+
* </ol>
49+
*
50+
* @author Oliver Drotbohm
51+
* @since 3.5
52+
*/
53+
@Configuration(proxyBeanMethods = false)
54+
class JMoleculesConfigurer implements WebMvcConfigurer, RepositoryRestConfigurer {
55+
56+
/*
57+
* (non-Javadoc)
58+
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#addFormatters(org.springframework.format.FormatterRegistry)
59+
*/
60+
@Override
61+
public void addFormatters(FormatterRegistry registry) {
62+
63+
ConversionService conversionService = (ConversionService) registry;
64+
Supplier<ConversionService> supplier = () -> conversionService;
65+
66+
if (!conversionService.canConvert(Identifier.class, String.class)) {
67+
registry.addConverter(new IdentifierToPrimitivesConverter(supplier));
68+
}
69+
70+
if (!conversionService.canConvert(String.class, Identifier.class)) {
71+
registry.addConverter(new PrimitivesToIdentifierConverter(supplier));
72+
}
73+
}
74+
75+
/*
76+
* (non-Javadoc)
77+
* @see org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer#configureConversionService(org.springframework.core.convert.support.ConfigurableConversionService)
78+
*/
79+
@Override
80+
public void configureConversionService(ConfigurableConversionService conversionService) {
81+
82+
Supplier<ConversionService> supplier = () -> conversionService;
83+
84+
conversionService.addConverter(new PrimitivesToIdentifierConverter(supplier));
85+
conversionService.addConverter(new IdentifierToPrimitivesConverter(supplier));
86+
}
87+
88+
@Lazy
89+
@Bean
90+
BackendIdConverter jMoleculesEntitiesBackendIdConverter(PersistentEntities entities,
91+
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService) {
92+
return new JMoleculesBackendIdentifierConverter(entities, () -> conversionService.getObject());
93+
}
94+
95+
/**
96+
* A {@link BackendIdConverter} to convert from and to JMolecules' Identifier
97+
*
98+
* @author Oliver Drotbohm
99+
*/
100+
private static final class JMoleculesBackendIdentifierConverter implements BackendIdConverter {
101+
102+
private static final TypeDescriptor STRING_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
103+
private static final ResolvableType IDENTIFIER_TYPE = ResolvableType.forClass(Identifier.class);
104+
105+
private final PrimitivesToIdentifierConverter identifierConverter;
106+
private final IdentifierToPrimitivesConverter primitivesConverter;
107+
private final PersistentEntities entities;
108+
109+
/**
110+
* Creates a new {@link JMoleculesBackendIdentifierConverter} for the given {@link PersistentEntities} and
111+
* {@link ConversionService}.
112+
*
113+
* @param entities must not be {@literal null}.
114+
* @param conversionService must not be {@literal null}.
115+
*/
116+
public JMoleculesBackendIdentifierConverter(PersistentEntities entities,
117+
Supplier<? extends ConversionService> conversionService) {
118+
119+
this.identifierConverter = new PrimitivesToIdentifierConverter(conversionService);
120+
this.primitivesConverter = new IdentifierToPrimitivesConverter(conversionService);
121+
this.entities = entities;
122+
}
123+
124+
/*
125+
* (non-Javadoc)
126+
* @see org.springframework.data.rest.webmvc.spi.BackendIdConverter#fromRequestId(java.lang.String, java.lang.Class)
127+
*/
128+
@Override
129+
public Serializable fromRequestId(String id, Class<?> entityType) {
130+
131+
PersistentEntity<?, ? extends PersistentProperty<?>> entity = entities.getRequiredPersistentEntity(entityType);
132+
Class<?> idType = entity.getRequiredIdProperty().getType();
133+
134+
return (Serializable) identifierConverter.convert(id, TypeDescriptor.forObject(id),
135+
TypeDescriptor.valueOf(idType));
136+
}
137+
138+
/*
139+
* (non-Javadoc)
140+
* @see org.springframework.data.rest.webmvc.spi.BackendIdConverter#toRequestId(java.io.Serializable, java.lang.Class)
141+
*/
142+
@Override
143+
public String toRequestId(Serializable id, Class<?> entityType) {
144+
return (String) primitivesConverter.convert(id, TypeDescriptor.forObject(id), STRING_DESCRIPTOR);
145+
}
146+
147+
/*
148+
* (non-Javadoc)
149+
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
150+
*/
151+
@Override
152+
public boolean supports(Class<?> delimiter) {
153+
154+
if (!Entity.class.isAssignableFrom(delimiter)) {
155+
return false;
156+
}
157+
158+
return IDENTIFIER_TYPE.isAssignableFrom(ResolvableType.forClass(delimiter)
159+
.as(Entity.class)
160+
.getGeneric(1));
161+
}
162+
}
163+
}

Diff for: spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RestControllerImportSelector.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
class RestControllerImportSelector implements ImportSelector {
3535

3636
private static final String HAL_EXPLORER_CONFIGURATION = "org.springframework.data.rest.webmvc.halexplorer.HalExplorerConfiguration";
37+
private static final String JMOLECULES_SPRING = "org.jmolecules.spring.IdentifierToPrimitivesConverter";
3738

3839
/*
3940
* (non-Javadoc)
@@ -45,10 +46,16 @@ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
4546
List<String> configurations = new ArrayList<>();
4647
configurations.add(RestControllerConfiguration.class.getName());
4748

48-
if (ClassUtils.isPresent(HAL_EXPLORER_CONFIGURATION, importingClassMetadata.getClass().getClassLoader())) {
49+
ClassLoader classLoader = importingClassMetadata.getClass().getClassLoader();
50+
51+
if (ClassUtils.isPresent(HAL_EXPLORER_CONFIGURATION, classLoader)) {
4952
configurations.add(HAL_EXPLORER_CONFIGURATION);
5053
}
5154

55+
if (ClassUtils.isPresent(JMOLECULES_SPRING, classLoader)) {
56+
configurations.add(JMoleculesConfigurer.class.getName());
57+
}
58+
5259
return configurations.toArray(new String[configurations.size()]);
5360
}
5461
}

0 commit comments

Comments
 (0)