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

Add support for link headers via @ProvideLink, Add interface ... #3611

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/src/main/docbook/declarative-linking.xml
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ URI offers;</programlisting>
</para>

<para>
Resource links via <literal>@ProvideLink</literal> are currently not supported for link headers.
Resource links via <literal>@ProvideLink</literal> are supported and require the existence of
<literal>@InjectLinks</literal> on the entity or its ancestors. Both sources will be combined.
</para>
</section>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static synchronized EntityDescriptor getInstance(Class<?> entityClass) {
private Map<String, FieldDescriptor> nonLinkFields;
private Map<String, FieldDescriptor> linkFields;
private List<LinkHeaderDescriptor> linkHeaders;
private boolean injectLinkHeaders = false;

/**
* Construct an new descriptor by inspecting the supplied class.
Expand Down Expand Up @@ -111,6 +112,10 @@ List<LinkHeaderDescriptor> getLinkHeaders() {
return linkHeaders;
}

boolean isInjectLinkHeaders() {
return injectLinkHeaders;
}

/**
* Find and cache the fields of the supplied class and its superclasses and
* interfaces.
Expand Down Expand Up @@ -170,6 +175,7 @@ private void findLinkHeaders(Class<?> entityClass) {
}
InjectLinks linkHeadersAnnotation = entityClass.getAnnotation(InjectLinks.class);
if (linkHeadersAnnotation != null) {
injectLinkHeaders = true;
for (InjectLink linkHeader : linkHeadersAnnotation.value()) {
linkHeaders.add(new LinkHeaderDescriptor(linkHeader));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@
import java.util.Collections;
import java.util.List;

import javax.ws.rs.core.Link;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;

import org.glassfish.jersey.linking.contributing.ResourceLinkContributionContext;
import org.glassfish.jersey.linking.mapping.ResourceMappingContext;

/**
Expand All @@ -74,14 +76,16 @@ class HeaderProcessor<T> {
void processLinkHeaders(T entity,
UriInfo uriInfo,
ResourceMappingContext rmc,
ResourceLinkContributionContext rlcc,
MultivaluedMap<String, Object> headers) {
List<String> headerValues = getLinkHeaderValues(entity, uriInfo, rmc);
List<String> headerValues = getLinkHeaderValues(entity, uriInfo, rmc, rlcc);
for (String headerValue : headerValues) {
headers.add("Link", headerValue);
}
}

List<String> getLinkHeaderValues(Object entity, UriInfo uriInfo, ResourceMappingContext rmc) {
List<String> getLinkHeaderValues(Object entity, UriInfo uriInfo, ResourceMappingContext rmc,
ResourceLinkContributionContext rlcc) {
final List<Object> matchedResources = uriInfo.getMatchedResources();

if (!matchedResources.isEmpty()) {
Expand All @@ -94,6 +98,17 @@ List<String> getLinkHeaderValues(Object entity, UriInfo uriInfo, ResourceMapping
headerValues.add(headerValue);
}
}
if (instanceDescriptor.isInjectLinkHeaders()) {
List<ProvideLinkDescriptor> contributions = rlcc.getContributorsFor(entity.getClass());
for (ProvideLinkDescriptor contribution : contributions) {
if (ELLinkBuilder.evaluateCondition(contribution.getCondition(),
entity, contribution.getResource(), entity)) {
URI uri = ELLinkBuilder.buildURI(contribution, entity, resource, entity, uriInfo, rmc);
Link link = contribution.getLink(uri);
headerValues.add(link.toString());
}
}
}
return headerValues;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void filter(ContainerRequestContext request, ContainerResponseContext res
if (entity != null && !uriInfo.getMatchedResources().isEmpty()) {
Class<?> entityClass = entity.getClass();
HeaderProcessor lhp = new HeaderProcessor(entityClass);
lhp.processLinkHeaders(entity, uriInfo, rmc, response.getHeaders());
lhp.processLinkHeaders(entity, uriInfo, rmc, rlcc, response.getHeaders());
FieldProcessor lp = new FieldProcessor(entityClass);
lp.processLinks(entity, uriInfo, rmc, rlcc);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,12 @@ public List<ProvideLinkDescriptor> getContributorsFor(Class<?> entityClass) {
private List<ProvideLinkDescriptor> collectContributors(Class<?> entityClass, List<ProvideLinkDescriptor> contributors) {
contributors.addAll(mappings.getOrDefault(entityClass, Collections.emptyList()));
Class<?> sc = entityClass.getSuperclass();
if (sc != null && sc != Object.class) {
if ((sc != null) && (sc != Object.class)) {
collectContributors(sc, contributors);
}
for (Class<?> ic : entityClass.getInterfaces()) {
collectContributors(ic, contributors);
}
return contributors;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,19 @@
import java.util.Collections;
import java.util.List;
import java.util.regex.MatchResult;

import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.UriBuilder;

import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
import org.glassfish.jersey.linking.InjectLink.Extension;
import org.glassfish.jersey.linking.contributing.ResourceLinkContributionContext;
import org.glassfish.jersey.linking.mapping.ResourceMappingContext;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceMethod;
import org.glassfish.jersey.server.model.RuntimeResource;
import org.glassfish.jersey.uri.UriTemplate;

import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
Expand Down Expand Up @@ -225,6 +224,13 @@ public ResourceMappingContext.Mapping getMapping(Class<?> resource) {
}
};

protected final ResourceLinkContributionContext mockRlcc = new ResourceLinkContributionContext() {
@Override
public List<ProvideLinkDescriptor> getContributorsFor(Class<?> entityClass) {
return Collections.emptyList();
}
};

@InjectLink(value = "A")
public static class EntityA {
}
Expand All @@ -234,7 +240,7 @@ public void testLiteral() {
System.out.println("Literal");
HeaderProcessor<EntityA> instance = new HeaderProcessor(EntityA.class);
EntityA testClass = new EntityA();
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc);
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc, mockRlcc);
assertEquals(1, headerValues.size());
String headerValue = headerValues.get(0);
assertEquals("</application/resources/A>", headerValue);
Expand All @@ -253,7 +259,7 @@ public void testEL() {
System.out.println("EL");
HeaderProcessor<EntityB> instance = new HeaderProcessor(EntityB.class);
EntityB testClass = new EntityB();
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc);
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc, mockRlcc);
assertEquals(1, headerValues.size());
String headerValue = headerValues.get(0);
assertEquals("</application/resources/B>", headerValue);
Expand All @@ -272,7 +278,7 @@ public void testTemplateLiteral() {
System.out.println("Template Literal");
HeaderProcessor<EntityC> instance = new HeaderProcessor(EntityC.class);
EntityC testClass = new EntityC();
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc);
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc, mockRlcc);
assertEquals(1, headerValues.size());
String headerValue = headerValues.get(0);
assertEquals("</application/resources/C>", headerValue);
Expand All @@ -290,7 +296,7 @@ public void testMultiple() {
System.out.println("Multiple Literal");
HeaderProcessor<EntityD> instance = new HeaderProcessor(EntityD.class);
EntityD testClass = new EntityD();
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc);
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc, mockRlcc);
assertEquals(2, headerValues.size());
// not sure if annotation order is supposed to be preserved but it seems
// to work as expected
Expand Down Expand Up @@ -321,7 +327,7 @@ public void testParameters() {
System.out.println("Parameters");
HeaderProcessor<EntityE> instance = new HeaderProcessor(EntityE.class);
EntityE testClass = new EntityE();
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc);
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc, mockRlcc);
assertEquals(1, headerValues.size());
String headerValue = headerValues.get(0);
assertTrue(headerValue.contains("</application/resources/E>"));
Expand Down Expand Up @@ -364,7 +370,7 @@ public void testConditional() {
System.out.println("EL");
HeaderProcessor<EntityF> instance = new HeaderProcessor(EntityF.class);
EntityF testClass = new EntityF();
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc);
List<String> headerValues = instance.getLinkHeaderValues(testClass, mockUriInfo, mockRmc, mockRlcc);
assertEquals(1, headerValues.size());
String headerValue = headerValues.get(0);
assertEquals("</application/resources/1>", headerValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

package org.glassfish.jersey.linking.integration;

import java.util.List;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
Expand All @@ -49,8 +50,11 @@
import org.glassfish.jersey.linking.integration.app.LinkingApplication;
import org.glassfish.jersey.linking.integration.representations.OrderRequest;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;

public class LinkingTest extends JerseyTest {

Expand Down Expand Up @@ -163,4 +167,13 @@ public void provideCanBeUsedInConjunctionWithInject() throws Exception {
+ "{uri:'/orders/1',params:{rel:'order'},uriBuilder:{absolute:false},rel:'order',rels:['order']}]}",
order, true);
}


@Test
public void linkHeadersAreCollectedFromBothAnnotations() throws Exception {
Response response = target().path("/info").request().get();
List<Object> links = response.getHeaders().get("Link");
Assert.assertThat(links.size(), is(2));
Assert.assertThat(links, hasItems("</orders>; rel=\"create-order\"", "</info>; rel=\"self\""));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.glassfish.jersey.linking.integration.app;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.linking.integration.representations.Info;

@Path("/info")
public class InfoResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response info() {
Info info = new Info();
info.setVersion("1.1");
return Response.ok(info).build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@

public class LinkingManualApplication extends Application {

private static final Set<Class<?>> classes = new HashSet<>(Arrays.asList(OrdersResource.class, PaymentResource.class));
private static final Set<Class<?>> classes = new HashSet<>(Arrays.asList(OrdersResource.class,
PaymentResource.class, InfoResource.class));

@Override
public Set<Class<?>> getClasses() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.glassfish.jersey.linking.Binding;
import org.glassfish.jersey.linking.ProvideLink;
import org.glassfish.jersey.linking.integration.representations.ExtendedOrder;
import org.glassfish.jersey.linking.integration.representations.Info;
import org.glassfish.jersey.linking.integration.representations.Order;
import org.glassfish.jersey.linking.integration.representations.OrderPage;
import org.glassfish.jersey.linking.integration.representations.OrderRequest;
Expand All @@ -77,6 +78,7 @@ public class OrdersResource {


@ProvideLink(value = OrderPage.class, rel = "create")
@ProvideLink(value = Info.class, rel = "create-order")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@POST
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.glassfish.jersey.linking.integration.representations;

import org.glassfish.jersey.linking.InjectLink;
import org.glassfish.jersey.linking.InjectLinks;
import org.glassfish.jersey.linking.integration.app.InfoResource;

@InjectLinks({
@InjectLink(resource = InfoResource.class, method = "info", rel = "self")
})
public class Info {
private String version;

public String getVersion() {
return version;
}

public void setVersion(String version) {
this.version = version;
}
}