Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 4a8f2b2

Browse files
leonard84pavelbucek
authored andcommitted
Add @ProvideLink(s) to declarative-linking
@ProvideLinks allows a different way of creating links, with this feature you can annotate the resource methods to contribute links to entities. This has the advantage, that you don't have to build the resource path by hand. It is fully compatible with the existing @InjectLinks and can be mixed. Furthermore, the link collection will now be merged with an existing collection, this allows users to manually add links as well. Change-Id: I66f474433f0bb55c66ca827bd85114622548adcb
1 parent 3847eef commit 4a8f2b2

37 files changed

+2387
-173
lines changed

incubator/declarative-linking/pom.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,25 @@
8787
<artifactId>junit</artifactId>
8888
<scope>test</scope>
8989
</dependency>
90+
91+
<dependency>
92+
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
93+
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
94+
<version>${project.version}</version>
95+
<scope>test</scope>
96+
</dependency>
97+
<dependency>
98+
<groupId>org.glassfish.jersey.media</groupId>
99+
<artifactId>jersey-media-json-jackson</artifactId>
100+
<version>${jersey.version}</version>
101+
<scope>test</scope>
102+
</dependency>
103+
<dependency>
104+
<groupId>org.skyscreamer</groupId>
105+
<artifactId>jsonassert</artifactId>
106+
<version>1.4.0</version>
107+
<scope>test</scope>
108+
</dependency>
90109
</dependencies>
91110

92111
<profiles>

incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/DeclarativeLinkingFeature.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848

4949
import org.glassfish.jersey.Beta;
5050
import org.glassfish.jersey.internal.inject.AbstractBinder;
51+
import org.glassfish.jersey.linking.contributing.NaiveResourceLinkContributionContext;
52+
import org.glassfish.jersey.linking.contributing.ResourceLinkContributionContext;
5153
import org.glassfish.jersey.linking.mapping.NaiveResourceMappingContext;
5254
import org.glassfish.jersey.linking.mapping.ResourceMappingContext;
5355

@@ -57,7 +59,6 @@
5759
* @author Mark Hadley
5860
* @author Gerard Davison (gerard.davison at oracle.com)
5961
*/
60-
6162
@Beta
6263
public class DeclarativeLinkingFeature implements Feature {
6364

@@ -74,6 +75,14 @@ protected void configure() {
7475
.to(ResourceMappingContext.class).in(Singleton.class);
7576
}
7677
});
78+
context.register(new AbstractBinder() {
79+
80+
@Override
81+
protected void configure() {
82+
bindAsContract(NaiveResourceLinkContributionContext.class)
83+
.to(ResourceLinkContributionContext.class).in(Singleton.class);
84+
}
85+
});
7786

7887
context.register(ResponseLinkFilter.class);
7988

incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/ELLinkBuilder.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -69,7 +69,13 @@ private ELLinkBuilder() {
6969
ExpressionFactory.newInstance();
7070

7171
/**
72-
* TODO javadoc.
72+
* Evaluates the condition
73+
*
74+
* @param condition the condition expression
75+
* @param entity the entity returned from the resource method
76+
* @param resource the resource class instance that returned the entity
77+
* @param instance the instance that contains the entity, e.g. the value of a field within an entity class.
78+
* @return the result of the condition
7379
*/
7480
static boolean evaluateCondition(String condition,
7581
Object entity,
@@ -87,7 +93,15 @@ static boolean evaluateCondition(String condition,
8793
}
8894

8995
/**
90-
* TODO javadoc.
96+
* Creates the URI using the link descriptor.
97+
*
98+
* @param link the link descriptor
99+
* @param entity the entity returned from the resource method
100+
* @param resource the resource class instance that returned the entity
101+
* @param instance the instance that contains the entity, e.g. the value of a field within an entity class.
102+
* @param uriInfo JAX-RS {@link UriInfo}
103+
* @param rmc the {@link ResourceMappingContext}
104+
* @return the URI
91105
*/
92106
static URI buildURI(InjectLinkDescriptor link,
93107
Object entity,

incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/EntityDescriptor.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -59,14 +59,12 @@
5959
* @author Mark Hadley
6060
* @author Gerard Davison (gerard.davison at oracle.com)
6161
*/
62-
6362
class EntityDescriptor {
6463

6564
// Maintains an internal static cache to optimize processing
66-
6765
private static final Map<Class<?>, EntityDescriptor> descriptors = new HashMap<>();
6866

69-
public static synchronized EntityDescriptor getInstance(Class<?> entityClass) {
67+
static synchronized EntityDescriptor getInstance(Class<?> entityClass) {
7068
if (descriptors.containsKey(entityClass)) {
7169
return descriptors.get(entityClass);
7270
} else {
@@ -84,6 +82,7 @@ public static synchronized EntityDescriptor getInstance(Class<?> entityClass) {
8482

8583
/**
8684
* Construct an new descriptor by inspecting the supplied class.
85+
*
8786
* @param entityClass
8887
*/
8988
private EntityDescriptor(Class<?> entityClass) {
@@ -100,21 +99,22 @@ private EntityDescriptor(Class<?> entityClass) {
10099
this.linkFields = Collections.unmodifiableMap(this.linkFields);
101100
}
102101

103-
public Collection<FieldDescriptor> getLinkFields() {
102+
Collection<FieldDescriptor> getLinkFields() {
104103
return linkFields.values();
105104
}
106105

107-
public Collection<FieldDescriptor> getNonLinkFields() {
106+
Collection<FieldDescriptor> getNonLinkFields() {
108107
return nonLinkFields.values();
109108
}
110109

111-
public List<LinkHeaderDescriptor> getLinkHeaders() {
110+
List<LinkHeaderDescriptor> getLinkHeaders() {
112111
return linkHeaders;
113112
}
114113

115114
/**
116115
* Find and cache the fields of the supplied class and its superclasses and
117116
* interfaces.
117+
*
118118
* @param entityClass the class
119119
*/
120120
private void findFields(Class<?> entityClass) {

incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/FieldDescriptor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -61,7 +61,7 @@ class FieldDescriptor {
6161
this.field = f;
6262
}
6363

64-
public Object getFieldValue(Object instance) {
64+
Object getFieldValue(Object instance) {
6565
setAccessibleField(field);
6666
Object value = null;
6767
try {
@@ -76,7 +76,7 @@ public String getFieldName() {
7676
return field.getName();
7777
}
7878

79-
protected static void setAccessibleField(final Field f) {
79+
static void setAccessibleField(final Field f) {
8080
if (Modifier.isPublic(f.getModifiers())) {
8181
return;
8282
}

incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/FieldProcessor.java

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2010-2016 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -43,16 +43,19 @@
4343
import java.lang.reflect.Modifier;
4444
import java.net.URI;
4545
import java.util.ArrayList;
46-
import java.util.logging.Level;
47-
import java.util.logging.Logger;
4846
import java.util.HashSet;
4947
import java.util.List;
5048
import java.util.Map;
5149
import java.util.Set;
50+
import java.util.logging.Level;
51+
import java.util.logging.Logger;
52+
5253
import javax.ws.rs.core.Link;
5354
import javax.ws.rs.core.UriInfo;
55+
5456
import javax.xml.bind.annotation.XmlTransient;
5557

58+
import org.glassfish.jersey.linking.contributing.ResourceLinkContributionContext;
5659
import org.glassfish.jersey.linking.mapping.ResourceMappingContext;
5760

5861
/**
@@ -67,33 +70,39 @@ class FieldProcessor<T> {
6770
private EntityDescriptor instanceDescriptor;
6871
private static final Logger log = Logger.getLogger(FieldProcessor.class.getName());
6972

70-
public FieldProcessor(Class<T> c) {
73+
FieldProcessor(Class<T> c) {
7174
instanceDescriptor = EntityDescriptor.getInstance(c);
7275
}
7376

7477
/**
7578
* Inject any {@link org.glassfish.jersey.linking.InjectLink} annotated fields in the supplied entity and
7679
* recursively process its fields.
77-
* @param entity the entity object returned by the resource method
80+
*
81+
* @param entity the entity object returned by the resource method
7882
* @param uriInfo the uriInfo for the request
83+
* @param rmc the ResourceMappingContext used for building URIs
84+
* @param rlcc the ResourceLinkContributionContext used to find link contributors
7985
*/
80-
public void processLinks(T entity, UriInfo uriInfo, ResourceMappingContext rmc) {
86+
void processLinks(T entity, UriInfo uriInfo, ResourceMappingContext rmc, ResourceLinkContributionContext rlcc) {
8187
Set<Object> processed = new HashSet<Object>();
8288
Object resource = uriInfo.getMatchedResources().get(0);
83-
processLinks(entity, resource, entity, processed, uriInfo, rmc);
89+
processLinks(entity, resource, entity, processed, uriInfo, rmc, rlcc);
8490
}
8591

8692
/**
8793
* Inject any {@link org.glassfish.jersey.linking.InjectLink} annotated fields in the supplied instance. Called
8894
* once for the entity and then recursively for each member and field.
89-
* @param entity
95+
*
96+
* @param entity the entity object returned by the resource method
9097
* @param processed a list of already processed objects, used to break
91-
* recursion when processing circular references.
92-
* @param uriInfo
98+
* recursion when processing circular references.
99+
* @param uriInfo the uriInfo for the request
100+
* @param rmc the ResourceMappingContext used for building URIs
101+
* @param rlcc the ResourceLinkContributionContext used to find link contributors
93102
*/
94103
private void processLinks(Object entity, Object resource, Object instance,
95-
Set<Object> processed, UriInfo uriInfo,
96-
ResourceMappingContext rmc) {
104+
Set<Object> processed, UriInfo uriInfo,
105+
ResourceMappingContext rmc, ResourceLinkContributionContext rlcc) {
97106

98107
try {
99108
if (instance == null || processed.contains(instance)) {
@@ -121,14 +130,23 @@ private void processLinks(Object entity, Object resource, Object instance,
121130
} else if (field instanceof InjectLinksFieldDescriptor) {
122131

123132
InjectLinksFieldDescriptor linksField = (InjectLinksFieldDescriptor) field;
124-
List<Link> list = new ArrayList<Link>();
133+
List<Link> list = new ArrayList<>();
125134
for (InjectLinkFieldDescriptor linkField : linksField.getLinksToInject()) {
126135
if (ELLinkBuilder.evaluateCondition(linkField.getCondition(), entity, resource, instance)) {
127136
URI uri = ELLinkBuilder.buildURI(linkField, entity, resource, instance, uriInfo, rmc);
128137
Link link = linkField.getLink(uri);
129138
list.add(link);
130139
}
131140
}
141+
List<ProvideLinkDescriptor> linkContributors = rlcc.getContributorsFor(instance.getClass());
142+
for (ProvideLinkDescriptor linkContributor : linkContributors) {
143+
if (ELLinkBuilder.evaluateCondition(linkContributor.getCondition(),
144+
entity, linkContributor.getResource(), instance)) {
145+
URI uri = ELLinkBuilder.buildURI(linkContributor, entity, resource, instance, uriInfo, rmc);
146+
Link link = linkContributor.getLink(uri);
147+
list.add(link);
148+
}
149+
}
132150

133151
linksField.setPropertyValue(instance, list);
134152
}
@@ -139,25 +157,25 @@ private void processLinks(Object entity, Object resource, Object instance,
139157
if (instanceClass.isArray() && Object[].class.isAssignableFrom(instanceClass)) {
140158
Object array[] = (Object[]) instance;
141159
for (Object member : array) {
142-
processMember(entity, resource, member, processed, uriInfo, rmc);
160+
processMember(entity, resource, member, processed, uriInfo, rmc, rlcc);
143161
}
144162
} else if (instance instanceof Iterable) {
145163
Iterable iterable = (Iterable) instance;
146164
for (Object member : iterable) {
147-
processMember(entity, resource, member, processed, uriInfo, rmc);
165+
processMember(entity, resource, member, processed, uriInfo, rmc, rlcc);
148166
}
149167
} else if (instance instanceof Map) {
150168
Map map = (Map) instance;
151169
for (Object member : map.entrySet()) {
152-
processMember(entity, resource, member, processed, uriInfo, rmc);
170+
processMember(entity, resource, member, processed, uriInfo, rmc, rlcc);
153171
}
154172
}
155173

156174
// Recursively process all member fields
157175
for (FieldDescriptor member : instanceDescriptor.getNonLinkFields()) {
158176

159177
if (fieldSuitableForIntrospection(member)) {
160-
processMember(entity, resource, member.getFieldValue(instance), processed, uriInfo, rmc);
178+
processMember(entity, resource, member.getFieldValue(instance), processed, uriInfo, rmc, rlcc);
161179
}
162180
}
163181

@@ -174,10 +192,10 @@ private boolean fieldSuitableForIntrospection(FieldDescriptor member) {
174192
}
175193

176194
private void processMember(Object entity, Object resource, Object member, Set<Object> processed, UriInfo uriInfo,
177-
ResourceMappingContext rmc) {
195+
ResourceMappingContext rmc, ResourceLinkContributionContext rlcc) {
178196
if (member != null) {
179-
FieldProcessor proc = new FieldProcessor(member.getClass());
180-
proc.processLinks(entity, resource, member, processed, uriInfo, rmc);
197+
FieldProcessor<?> proc = new FieldProcessor(member.getClass());
198+
proc.processLinks(entity, resource, member, processed, uriInfo, rmc, rlcc);
181199
}
182200
}
183201

incubator/declarative-linking/src/main/java/org/glassfish/jersey/linking/HeaderProcessor.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -61,7 +61,7 @@ class HeaderProcessor<T> {
6161

6262
private EntityDescriptor instanceDescriptor;
6363

64-
public HeaderProcessor(Class<T> c) {
64+
HeaderProcessor(Class<T> c) {
6565
instanceDescriptor = EntityDescriptor.getInstance(c);
6666
}
6767

@@ -71,10 +71,10 @@ public HeaderProcessor(Class<T> c) {
7171
* @param uriInfo the uriInfo for the request
7272
* @param headers the map into which the headers will be added
7373
*/
74-
public void processLinkHeaders(T entity,
75-
UriInfo uriInfo,
76-
ResourceMappingContext rmc,
77-
MultivaluedMap<String, Object> headers) {
74+
void processLinkHeaders(T entity,
75+
UriInfo uriInfo,
76+
ResourceMappingContext rmc,
77+
MultivaluedMap<String, Object> headers) {
7878
List<String> headerValues = getLinkHeaderValues(entity, uriInfo, rmc);
7979
for (String headerValue : headerValues) {
8080
headers.add("Link", headerValue);
@@ -100,8 +100,8 @@ List<String> getLinkHeaderValues(Object entity, UriInfo uriInfo, ResourceMapping
100100
return Collections.emptyList();
101101
}
102102

103-
static String getLinkHeaderValue(LinkHeaderDescriptor desc, Object entity, Object resource, UriInfo uriInfo,
104-
ResourceMappingContext rmc) {
103+
private static String getLinkHeaderValue(LinkHeaderDescriptor desc, Object entity, Object resource, UriInfo uriInfo,
104+
ResourceMappingContext rmc) {
105105
URI uri = ELLinkBuilder.buildURI(desc, entity, resource, entity, uriInfo, rmc);
106106
InjectLink link = desc.getLinkHeader();
107107
return InjectLink.Util.buildLinkFromUri(uri, link).toString();

0 commit comments

Comments
 (0)