Skip to content

Commit 36c34a7

Browse files
committed
#572 - Create SimpleResourceAssembler for Resource<T>
Implement a ResourceAssembler that returns a Resource<T> solely based on a non-Resource domain class. Related issues: #416
1 parent 4807ae0 commit 36c34a7

7 files changed

+579
-6
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2017 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+
* http://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.hateoas;
17+
18+
/**
19+
* Analogous to {@link ResourceAssembler} but for resource collections.
20+
*
21+
* @author Greg Turnquist
22+
*/
23+
public interface ResourcesAssembler<T, D extends ResourceSupport> {
24+
25+
/**
26+
* Converts all given entities into resources and wraps the collection as a resource as well.
27+
*
28+
* @see ResourceAssembler#toResource(Object)
29+
* @param entities must not be {@literal null}.
30+
* @return {@link Resources} containing {@link Resource} of {@code T}.
31+
*/
32+
Resources<D> toResources(Iterable<? extends T> entities);
33+
34+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2017 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+
* http://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.hateoas;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import org.springframework.util.Assert;
22+
23+
/**
24+
* A {@link ResourceAssembler}/{@link ResourcesAssembler} that focuses purely on the domain type,
25+
* returning back {@link Resource} and {@link Resources} for that type instead of
26+
* {@link ResourceSupport}.
27+
*
28+
* @author Greg Turnquist
29+
*/
30+
public class SimpleResourceAssembler<T> implements ResourceAssembler<T, Resource<T>>, ResourcesAssembler<T, Resource<T>> {
31+
32+
/**
33+
* Converts the given entity into a {@link Resource}.
34+
*
35+
* @param entity
36+
* @return
37+
*/
38+
@Override
39+
public Resource<T> toResource(T entity) {
40+
41+
Resource<T> resource = new Resource<T>(entity);
42+
addLinks(resource);
43+
return resource;
44+
}
45+
46+
/**
47+
* Converts all given entities into resources and wraps the collection as a resource as well.
48+
*
49+
* @see #toResource(Object)
50+
* @param entities must not be {@literal null}.
51+
* @return {@link Resources} containing {@link Resource} of {@code T}.
52+
*/
53+
public Resources<Resource<T>> toResources(Iterable<? extends T> entities) {
54+
55+
Assert.notNull(entities, "Entities must not be null!");
56+
List<Resource<T>> result = new ArrayList<Resource<T>>();
57+
58+
for (T entity : entities) {
59+
result.add(toResource(entity));
60+
}
61+
62+
Resources<Resource<T>> resources = new Resources<Resource<T>>(result);
63+
addLinks(resources);
64+
return resources;
65+
}
66+
67+
/**
68+
* Define links to add to every individual {@link Resource}.
69+
*
70+
* @see org.springframework.hateoas.mvc.SimpleIdentifiableResourceAssembler#addLinks(Resource)
71+
*
72+
* @param resource
73+
*/
74+
protected void addLinks(Resource<T> resource) {
75+
// Default adds no links
76+
}
77+
78+
/**
79+
* Define links to add to the {@link Resources} collection.
80+
*
81+
* @see org.springframework.hateoas.mvc.SimpleIdentifiableResourceAssembler#addLinks(Resources)
82+
*
83+
* @param resources
84+
*/
85+
protected void addLinks(Resources<Resource<T>> resources) {
86+
// Default adds no links.
87+
}
88+
}

src/main/java/org/springframework/hateoas/mvc/ResourceAssemblerSupport.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.springframework.beans.BeanUtils;
2424
import org.springframework.hateoas.ResourceAssembler;
2525
import org.springframework.hateoas.ResourceSupport;
26+
import org.springframework.hateoas.Resources;
27+
import org.springframework.hateoas.ResourcesAssembler;
2628
import org.springframework.util.Assert;
2729

2830
/**
@@ -31,7 +33,8 @@
3133
*
3234
* @author Oliver Gierke
3335
*/
34-
public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> implements ResourceAssembler<T, D> {
36+
public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport>
37+
implements ResourceAssembler<T, D>, ResourcesAssembler<T, D> {
3538

3639
private final Class<?> controllerClass;
3740
private final Class<D> resourceType;
@@ -58,7 +61,7 @@ public ResourceAssemblerSupport(Class<?> controllerClass, Class<D> resourceType)
5861
* @param entities must not be {@literal null}.
5962
* @return
6063
*/
61-
public List<D> toResources(Iterable<? extends T> entities) {
64+
public Resources<D> toResources(Iterable<? extends T> entities) {
6265

6366
Assert.notNull(entities, "Entities must not be null!");
6467
List<D> result = new ArrayList<D>();
@@ -67,7 +70,7 @@ public List<D> toResources(Iterable<? extends T> entities) {
6770
result.add(toResource(entity));
6871
}
6972

70-
return result;
73+
return new Resources<D>(result);
7174
}
7275

7376
/**
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright 2017 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+
* http://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.hateoas.mvc;
17+
18+
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
19+
20+
import org.springframework.core.GenericTypeResolver;
21+
import org.springframework.hateoas.Identifiable;
22+
import org.springframework.hateoas.Link;
23+
import org.springframework.hateoas.LinkBuilder;
24+
import org.springframework.hateoas.RelProvider;
25+
import org.springframework.hateoas.Resource;
26+
import org.springframework.hateoas.Resources;
27+
import org.springframework.hateoas.SimpleResourceAssembler;
28+
import org.springframework.hateoas.core.EvoInflectorRelProvider;
29+
30+
/**
31+
* Using a Spring MVC-based {@link ControllerLinkBuilder} and an {@link Identifiable} domain object, construct
32+
* a {@link SimpleResourceAssembler}.
33+
*
34+
* @author Greg Turnquist
35+
*/
36+
public class SimpleIdentifiableResourceAssembler<T extends Identifiable<?>> extends SimpleResourceAssembler<T> {
37+
38+
/**
39+
* The Spring MVC class for the {@link Identifiable} from which links will be built.
40+
*/
41+
private final Class<?> controllerClass;
42+
43+
/**
44+
* A {@link RelProvider} to look up names of links as options for resource paths.
45+
*/
46+
private final RelProvider relProvider;
47+
48+
/**
49+
* A {@link Class} depicting the {@link Identifiable}'s type.
50+
*/
51+
private final Class<?> resourceType;
52+
53+
/**
54+
* Default base path as empty.
55+
*/
56+
private String basePath = "";
57+
58+
/**
59+
* Default assembler based on a Spring MVC controller, resource type, and {@link RelProvider}. With this combination
60+
* of information, resources can be defined.
61+
*
62+
* @see #setBasePath(String) to adjust base path to something like "/api"/
63+
*
64+
* @param controllerClass - Spring MVC controller to base links off of
65+
* @param relProvider - generates the links items
66+
*/
67+
public SimpleIdentifiableResourceAssembler(Class<?> controllerClass, RelProvider relProvider) {
68+
69+
this.controllerClass = controllerClass;
70+
this.relProvider = relProvider;
71+
72+
// Find the "T" type contained in "T extends Identifiable<?>", e.g. SimpleIdentifiableResourceAssembler<User> -> User
73+
this.resourceType = GenericTypeResolver.resolveTypeArgument(this.getClass(), SimpleIdentifiableResourceAssembler.class);
74+
}
75+
76+
/**
77+
* Alternate constructor that falls back to {@link EvoInflectorRelProvider}.
78+
*
79+
* @param controllerClass
80+
*/
81+
public SimpleIdentifiableResourceAssembler(Class<?> controllerClass) {
82+
this(controllerClass, new EvoInflectorRelProvider());
83+
}
84+
85+
/**
86+
* Define links to add to every {@link Resource}.
87+
*
88+
* @param resource
89+
*/
90+
@Override
91+
protected void addLinks(Resource<T> resource) {
92+
93+
Link resourceSelfLink = getCollectionLinkBuilder().slash(resource.getContent()).withSelfRel();
94+
Link resourceCollectionLink = getCollectionLinkBuilder().withRel(this.relProvider.getCollectionResourceRelFor(this.resourceType));
95+
96+
resource.add(resourceSelfLink, resourceCollectionLink);
97+
}
98+
99+
/**
100+
* Define links to add to {@link Resources} collection.
101+
*
102+
* @param resources
103+
*/
104+
@Override
105+
protected void addLinks(Resources<Resource<T>> resources) {
106+
107+
Link resourceCollectionLink = getCollectionLinkBuilder().withSelfRel();
108+
resources.add(resourceCollectionLink);
109+
}
110+
111+
/**
112+
* Build up a URI for the collection using the Spring MVC controller followed by the resource type transformed
113+
* by the {@link RelProvider}.
114+
*
115+
* Assumption is that a {@literal EmployeeController} serving up {@literal Employee}
116+
* objects will be serving resources at {@code /employees} and {@code /employees/1} respectively.
117+
*
118+
* If this is not the case, simply override this method in your concrete instance, or simply resort to
119+
* overriding {@link #addLinks(Resource)} and {@link #addLinks(Resources)} where you have full control over exactly
120+
* what links are put in the individual and collection resources.
121+
*
122+
* @return
123+
*/
124+
protected LinkBuilder getCollectionLinkBuilder() {
125+
126+
ControllerLinkBuilder linkBuilder = linkTo(this.controllerClass);
127+
128+
String[] pathComponents = (getPrefix() + this.relProvider.getCollectionResourceRelFor(this.resourceType)).split("/");
129+
130+
for (String pathComponent : pathComponents) {
131+
if (!pathComponent.isEmpty()) {
132+
linkBuilder = linkBuilder.slash(pathComponent);
133+
}
134+
}
135+
136+
return linkBuilder;
137+
}
138+
139+
/**
140+
* Construct the prefix to a resource, with {@literal "/"} being the basis for no prefix at all.
141+
*
142+
* @return
143+
*/
144+
private String getPrefix() {
145+
return getBasePath().isEmpty() ? "" : getBasePath() + "/";
146+
}
147+
148+
public String getBasePath() {
149+
return this.basePath;
150+
}
151+
152+
public void setBasePath(String basePath) {
153+
this.basePath = basePath;
154+
}
155+
156+
157+
}

0 commit comments

Comments
 (0)