Skip to content

Commit 21976bd

Browse files
odrotbohmgregturn
authored andcommitted
#340 - Polishing.
Moved AffordanceModelFactory into core package as it's SPI. Switched to Spring Factories lookup of implementation classes so that we avoid a package dependency between the MVC package and the media type specific packages. Removed reference to MediaType from AffordanceModelFactory to AffordanceModel so that a factory can even provide models for different MediaTypes (i.e. different flavors of the same one, e.g. HAL Forms for JSON and XML). Also removed addAffordanceModel(…) from Affordance to not force the implementations into mutability. Made most of the affordance building API types package protected. HalFormsAffordanceModel now uses MethodParameters abstraction to simplify model parsing code. Tweaked HAL forms model to work with factory methods for required properties and wither methods to add optional properties. Tweaked and inlined mixin types in Jackson module for HAL forms. Slight API polishing on Link to make sure Affordance collecting methods are not named with…. Tweaked Lombok setup to use all caps for logger constants. Removed deprecation warnings in Jackson2HalModule.
1 parent 9dc5b9c commit 21976bd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+841
-737
lines changed

lombok.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lombok.anyConstructor.suppressConstructorProperties=true
2+
lombok.nonNull.exceptionType = IllegalArgumentException
3+
lombok.log.fieldName = LOG

src/main/java/org/springframework/hateoas/Affordance.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
*/
1616
package org.springframework.hateoas;
1717

18+
import org.springframework.http.HttpMethod;
1819
import org.springframework.http.MediaType;
1920

2021
/**
2122
* Abstract representation of an action a link is able to take. Web frameworks must provide concrete implementation.
2223
*
2324
* @author Greg Turnquist
25+
* @author Oliver Gierke
2426
*/
2527
public interface Affordance {
2628

@@ -29,7 +31,7 @@ public interface Affordance {
2931
*
3032
* @return
3133
*/
32-
String getHttpMethod();
34+
HttpMethod getHttpMethod();
3335

3436
/**
3537
* Name for the REST action this {@link Affordance} can take.
@@ -44,14 +46,5 @@ public interface Affordance {
4446
* @param mediaType
4547
* @return
4648
*/
47-
AffordanceModel getAffordanceModel(MediaType mediaType);
48-
49-
/**
50-
* Add a new {@link AffordanceModel} for a given {@link MediaType}.
51-
*
52-
* @param mediaType
53-
* @param affordanceModel
54-
*/
55-
void addAffordanceModel(MediaType mediaType, AffordanceModel affordanceModel);
56-
49+
<T extends AffordanceModel> T getAffordanceModel(MediaType mediaType);
5750
}

src/main/java/org/springframework/hateoas/AffordanceModel.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,23 @@
1515
*/
1616
package org.springframework.hateoas;
1717

18+
import java.util.Collection;
19+
20+
import org.springframework.http.MediaType;
21+
1822
/**
19-
* Marker interface for mediatypes to build up type-specific details for an {@link Affordance}
23+
* An affordance model is a media type specific description of an affordance.
2024
*
2125
* @author Greg Turnquist
26+
* @author Oliver Gierke
2227
*/
2328
public interface AffordanceModel {
2429

30+
/**
31+
* The media types this is a model for. Can be multiple ones as often media types come in different flavors like an
32+
* XML and JSON one and in simple cases a single model might serve them all.
33+
*
34+
* @return will never be {@literal null}.
35+
*/
36+
Collection<MediaType> getMediaTypes();
2537
}

src/main/java/org/springframework/hateoas/Link.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
import javax.xml.bind.annotation.XmlTransient;
3535
import javax.xml.bind.annotation.XmlType;
3636

37-
import org.springframework.hateoas.core.LinkBuilderSupport;
3837
import org.springframework.util.Assert;
3938
import org.springframework.util.StringUtils;
4039

4140
import com.fasterxml.jackson.annotation.JsonIgnore;
4241
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
42+
import com.fasterxml.jackson.annotation.JsonInclude;
4343

4444
/**
4545
* Value object for links.
@@ -48,6 +48,7 @@
4848
* @author Greg Turnquist
4949
*/
5050
@XmlType(name = "link", namespace = Link.ATOM_NAMESPACE)
51+
@JsonInclude(JsonInclude.Include.NON_NULL)
5152
@JsonIgnoreProperties("templated")
5253
@AllArgsConstructor(access = AccessLevel.PACKAGE)
5354
@Getter
@@ -113,11 +114,11 @@ public Link(UriTemplate template, String rel) {
113114
}
114115

115116
public Link(String href, String rel, List<Affordance> affordances) {
116-
117+
117118
this(href, rel);
118119

119120
Assert.notNull(affordances, "affordances must not be null!");
120-
121+
121122
this.affordances = affordances;
122123
}
123124

@@ -134,7 +135,7 @@ protected Link() {
134135
* @return
135136
*/
136137
public List<Affordance> getAffordances() {
137-
return new ArrayList<Affordance>(Collections.unmodifiableCollection(this.affordances));
138+
return Collections.unmodifiableList(this.affordances);
138139
}
139140

140141
/**
@@ -149,35 +150,46 @@ public Link withSelfRel() {
149150
/**
150151
* Create new {@link Link} with an additional {@link Affordance}.
151152
*
152-
* @param affordance
153+
* @param affordance must not be {@literal null}.
153154
* @return
154155
*/
155-
public Link withAffordance(Affordance affordance) {
156+
public Link andAffordance(Affordance affordance) {
157+
158+
Assert.notNull(affordance, "Affordance must not be null!");
156159

157160
List<Affordance> newAffordances = new ArrayList<Affordance>();
158161
newAffordances.addAll(this.affordances);
159162
newAffordances.add(affordance);
160163

161-
return new Link(this.rel, this.href, this.hreflang ,this.media, this.title, this.type,
162-
this.deprecation, this.template, newAffordances);
164+
return withAffordances(newAffordances);
163165
}
164166

165167
/**
166168
* Create new {@link Link} with additional {@link Affordance}s.
167169
*
168-
* @param affordances
170+
* @param affordances must not be {@literal null}.
169171
* @return
170172
*/
171-
public Link addAffordances(List<Affordance> affordances) {
173+
public Link andAffordances(List<Affordance> affordances) {
172174

173175
List<Affordance> newAffordances = new ArrayList<Affordance>();
174176
newAffordances.addAll(this.affordances);
175177
newAffordances.addAll(affordances);
176178

177-
return new Link(this.rel, this.href, this.hreflang ,this.media, this.title, this.type,
178-
this.deprecation, this.template, newAffordances);
179+
return withAffordances(newAffordances);
179180
}
180181

182+
/**
183+
* Creats a new {@link Link} with the given {@link Affordance}s.
184+
*
185+
* @param affordances must not be {@literal null}.
186+
* @return
187+
*/
188+
public Link withAffordances(List<Affordance> affordances) {
189+
190+
return new Link(this.rel, this.href, this.hreflang, this.media, this.title, this.type, this.deprecation,
191+
this.template, affordances);
192+
}
181193

182194
/**
183195
* Returns the variable names contained in the template.

src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.hateoas.config;
1717

18+
import lombok.extern.slf4j.Slf4j;
19+
1820
import java.lang.annotation.Documented;
1921
import java.lang.annotation.ElementType;
2022
import java.lang.annotation.Retention;
@@ -25,8 +27,6 @@
2527
import java.util.List;
2628
import java.util.Map;
2729

28-
import lombok.extern.slf4j.Slf4j;
29-
3030
import org.springframework.context.ApplicationContext;
3131
import org.springframework.context.annotation.Import;
3232
import org.springframework.context.annotation.ImportSelector;
@@ -54,7 +54,7 @@
5454
@Target(ElementType.TYPE)
5555
@Documented
5656
@Import({ HypermediaSupportBeanDefinitionRegistrar.class, HateoasConfiguration.class,
57-
EnableHypermediaSupport.HypermediaConfigurationImportSelector.class})
57+
EnableHypermediaSupport.HypermediaConfigurationImportSelector.class })
5858
public @interface EnableHypermediaSupport {
5959

6060
/**
@@ -68,6 +68,7 @@
6868
* Hypermedia representation types supported.
6969
*
7070
* @author Oliver Gierke
71+
* @author Greg Turnquist
7172
*/
7273
enum HypermediaType {
7374

@@ -81,6 +82,7 @@ enum HypermediaType {
8182

8283
/**
8384
* HAL-FORMS - Independent, backward-compatible extension of the HAL designed to add runtime FORM support
85+
*
8486
* @see https://rwcbook.github.io/hal-forms/
8587
*/
8688
HAL_FORMS(HalFormsWebMvcConfigurer.class);
@@ -109,8 +111,8 @@ public String[] selectImports(AnnotationMetadata metadata) {
109111
types = HypermediaType.values();
110112
}
111113

112-
log.debug("Registering support for hypermedia types {} according to configuration on {}",
113-
types, metadata.getClassName());
114+
LOG.debug("Registering support for hypermedia types {} according to configuration on {}", types,
115+
metadata.getClassName());
114116

115117
List<String> configurationNames = new ArrayList<String>();
116118

src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
8686

8787
private static final String DELEGATING_REL_PROVIDER_BEAN_NAME = "_relProvider";
8888
private static final String LINK_DISCOVERER_REGISTRY_BEAN_NAME = "_linkDiscovererRegistry";
89-
private static final String AFFORDANCE_MODEL_FACTORY_REGISTRY_BEAN_NAME = "_affordanceModelFactoryRegistry";
9089
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";
9190
private static final String HAL_FORMS_OBJECT_MAPPER_BEAN_NAME = "_halFormsObjectMapper";
9291
private static final String MESSAGE_SOURCE_BEAN_NAME = "linkRelationMessageSource";
@@ -146,7 +145,8 @@ public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionR
146145
registerRelProviderPluginRegistryAndDelegate(registry);
147146
}
148147

149-
private static void registerHypermediaComponents(AnnotationMetadata metadata, BeanDefinitionRegistry registry, String objectMapperBeanName) {
148+
private static void registerHypermediaComponents(AnnotationMetadata metadata, BeanDefinitionRegistry registry,
149+
String objectMapperBeanName) {
150150

151151
if (JACKSON2_PRESENT) {
152152

@@ -316,7 +316,7 @@ private List<HttpMessageConverter<?>> potentiallyRegisterModule(List<HttpMessage
316316

317317
ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);
318318
MessageSourceAccessor linkRelationMessageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME,
319-
MessageSourceAccessor.class);
319+
MessageSourceAccessor.class);
320320

321321
halObjectMapper.registerModule(new Jackson2HalModule());
322322

@@ -340,7 +340,7 @@ private List<HttpMessageConverter<?>> potentiallyRegisterModule(List<HttpMessage
340340

341341
ObjectMapper halFormsObjectMapper = beanFactory.getBean(HAL_FORMS_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);
342342
MessageSourceAccessor linkRelationMessageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME,
343-
MessageSourceAccessor.class);
343+
MessageSourceAccessor.class);
344344

345345
halFormsObjectMapper.registerModule(new Jackson2HalFormsModule());
346346

@@ -354,7 +354,7 @@ private List<HttpMessageConverter<?>> potentiallyRegisterModule(List<HttpMessage
354354
}
355355

356356
MappingJackson2HttpMessageConverter halFormsConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(
357-
ResourceSupport.class);
357+
ResourceSupport.class);
358358
halFormsConverter.setSupportedMediaTypes(Arrays.asList(HAL_FORMS_JSON));
359359
halFormsConverter.setObjectMapper(halFormsObjectMapper);
360360
result.add(halFormsConverter);

src/main/java/org/springframework/hateoas/AffordanceModelFactory.java renamed to src/main/java/org/springframework/hateoas/core/AffordanceModelFactory.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.hateoas;
16+
package org.springframework.hateoas.core;
1717

18+
import org.springframework.hateoas.Affordance;
19+
import org.springframework.hateoas.AffordanceModel;
1820
import org.springframework.hateoas.core.DummyInvocationUtils.MethodInvocation;
1921
import org.springframework.http.MediaType;
2022
import org.springframework.plugin.core.Plugin;
@@ -24,15 +26,9 @@
2426
* TODO: Replace this with an interface and a default implementation of {@link #supports(MediaType)} in Java 8.
2527
*
2628
* @author Greg Turnquist
29+
* @author Oliver Gierke
2730
*/
28-
public abstract class AffordanceModelFactory implements Plugin<MediaType> {
29-
30-
/**
31-
* Look up the {@link MediaType} of this factory.
32-
*
33-
* @return
34-
*/
35-
abstract public MediaType getMediaType();
31+
public interface AffordanceModelFactory extends Plugin<MediaType> {
3632

3733
/**
3834
* Look up the {@link AffordanceModel} for this factory.
@@ -42,16 +38,5 @@ public abstract class AffordanceModelFactory implements Plugin<MediaType> {
4238
* @param components
4339
* @return
4440
*/
45-
abstract public AffordanceModel getAffordanceModel(Affordance affordance, MethodInvocation invocationValue, UriComponents components);
46-
47-
/**
48-
* Find factories based on {@link MediaType}.
49-
*
50-
* @param delimiter
51-
* @return
52-
*/
53-
@Override
54-
public boolean supports(MediaType delimiter) {
55-
return delimiter != null && delimiter.equals(this.getMediaType());
56-
}
41+
AffordanceModel getAffordanceModel(Affordance affordance, MethodInvocation invocationValue, UriComponents components);
5742
}

src/main/java/org/springframework/hateoas/core/AnnotationMappingDiscoverer.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
import java.lang.annotation.Annotation;
2222
import java.lang.reflect.Method;
2323
import java.util.ArrayList;
24+
import java.util.Collection;
2425
import java.util.List;
2526
import java.util.regex.Pattern;
2627

28+
import org.springframework.http.HttpMethod;
2729
import org.springframework.util.Assert;
2830
import org.springframework.web.bind.annotation.RequestMethod;
2931

@@ -111,15 +113,15 @@ public String getMapping(Class<?> type, Method method) {
111113
}
112114

113115
/**
114-
* Extract {@link org.springframework.web.bind.annotation.RequestMapping}'s list of {@link RequestMethod}s
115-
* into an array of {@link String}s.
116+
* Extract {@link org.springframework.web.bind.annotation.RequestMapping}'s list of {@link RequestMethod}s into an
117+
* array of {@link String}s.
116118
*
117119
* @param type
118120
* @param method
119121
* @return
120122
*/
121123
@Override
122-
public String[] getRequestType(Class<?> type, Method method) {
124+
public Collection<HttpMethod> getRequestMethod(Class<?> type, Method method) {
123125

124126
Assert.notNull(type, "Type must not be null!");
125127
Assert.notNull(method, "Method must not be null!");
@@ -129,13 +131,13 @@ public String[] getRequestType(Class<?> type, Method method) {
129131

130132
RequestMethod[] requestMethods = (RequestMethod[]) value;
131133

132-
List<String> requestMethodNames = new ArrayList<String>();
134+
List<HttpMethod> requestMethodNames = new ArrayList<HttpMethod>();
133135

134136
for (RequestMethod requestMethod : requestMethods) {
135-
requestMethodNames.add(requestMethod.toString());
137+
requestMethodNames.add(HttpMethod.valueOf(requestMethod.toString()));
136138
}
137139

138-
return requestMethodNames.toArray(new String[]{});
140+
return requestMethodNames;
139141
}
140142

141143
private String[] getMappingFrom(Annotation annotation) {

0 commit comments

Comments
 (0)