I'm not too familiar with link injection, but one easy way to add links is to insert javax.ws.rs.core.Link s in your JAXB entity classes. It comes with a built-in XmlAdapter, Link.JaxbAdapter , which allows the Link type to be marshaled and unmarshaled by JAXB. For example, you have a BookStore class that contains a Books collection. It will also have a Link from which you can manage navigation cases.
@XmlRootElement(name = "bookstore") public class BookStore { private List<Link> links; private Collection<Book> books; @XmlElementRef public Collection<Book> getBooks() { return books; } public void setBooks(Collection<Book> books) { this.books = books; } @XmlElement(name = "link") @XmlJavaTypeAdapter(Link.JaxbAdapter.class) public List<Link> getLinks() { return links; } public void setLinks(List<Link> links) { this.links = links; } @XmlTransient public URI getNext() { if (links == null) { return null; } for (Link link : links) { if ("next".equals(link.getRel())) { return link.getUri(); } } return null; } @XmlTransient public URI getPrevious() { if (links == null) { return null; } for (Link link : links) { if ("previous".equals(link.getRel())) { return link.getUri(); } } return null; } }
The Book class is simply the regular root element of the JAXB class
@XmlRootElement public class Book { @XmlAttribute private String author; @XmlAttribute private String title; public Book() {} public Book(String title, String author) { this.title = title; this.author = author; } }
In the BookResource class BookResource we can basically add links on demand based on your logic of which links you want to submit. In the example below, there is an in-memory bit (this class is used, for example, as one of the Singleton classes) of books for which I add five books with increasing identifiers. When the request arrives, one or two links will be added to the BookStore return. Depending on which identifier is requested, we will add a βnextβ link and / or a previous link. Links will have rel , which we call from our BookStore class.
@Path("/books") public class BookResource { private final Map<Integer, Book> booksDB = Collections.synchronizedMap(new LinkedHashMap<Integer, Book>()); private final AtomicInteger idCounter = new AtomicInteger(); public BookResource() { Book book = new Book("Book One", "Author One"); booksDB.put(idCounter.incrementAndGet(), book); book = new Book("Book Two", "Author Two"); booksDB.put(idCounter.incrementAndGet(), book); book = new Book("Book Three", "Author Three"); booksDB.put(idCounter.incrementAndGet(), book); book = new Book("Book Four", "Author Four"); booksDB.put(idCounter.incrementAndGet(), book); book = new Book("Book Five", "Author Five"); booksDB.put(idCounter.incrementAndGet(), book); } @GET @Formatted @Path("/{id}") @Produces(MediaType.APPLICATION_XML) public BookStore getBook(@Context UriInfo uriInfo, @PathParam("id") int id) { List<Link> links = new ArrayList<>(); Collection<Book> books = new ArrayList<>(); UriBuilder uriBuilder = uriInfo.getBaseUriBuilder(); uriBuilder.path("books"); uriBuilder.path("{id}"); Book book = booksDB.get(id); if (book == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } synchronized(booksDB) { if (id + 1 <= booksDB.size()) { int next = id + 1; URI nextUri = uriBuilder.clone().build(next); Link link = Link.fromUri(nextUri).rel("next").type(MediaType.APPLICATION_XML).build(); links.add(link); } if (id - 1 > 0) { int previous = id - 1; URI nextUri = uriBuilder.clone().build(previous); Link link = Link.fromUri(nextUri).rel("previous").type(MediaType.APPLICATION_XML).build(); links.add(link); } } books.add(book); BookStore bookStore = new BookStore(); bookStore.setLinks(links); bookStore.setBooks(books); return bookStore; } }
And in the test example, we request the third book, and we see that there are links to the "next" and "previous" books in our in-memory db. We also call getNext() on our BookStore to get the next book in db, and the result will have two different links.
public class BookResourceTest { private static Client client; @BeforeClass public static void setUpClass() { client = ClientBuilder.newClient(); } @AfterClass public static void tearDownClass() { client.close(); } @Test public void testBookResourceLinks() throws Exception { String BASE_URL = "http://localhost:8080/jaxrs-stackoverflow-book/rest/books/3"; WebTarget target = client.target(BASE_URL); String xmlResult = target.request().accept(MediaType.APPLICATION_XML).get(String.class); System.out.println(xmlResult); Unmarshaller unmarshaller = JAXBContext.newInstance(BookStore.class).createUnmarshaller(); BookStore bookStore = (BookStore)unmarshaller.unmarshal(new StringReader(xmlResult)); URI next = bookStore.getNext(); WebTarget nextTarget = client.target(next); String xmlNextResult = nextTarget.request().accept(MediaType.APPLICATION_XML).get(String.class); System.out.println(xmlNextResult); } }
Result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <bookstore> <book author="Author Three" title="Book Three"/> <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/4" rel="next" type="application/xml"/> <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/2" rel="previous" type="application/xml"/> </bookstore> <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <bookstore> <book author="Author Four" title="Book Four"/> <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/5" rel="next" type="application/xml"/> <link href="http://localhost:8080/jaxrs-stackoverflow-book/rest/books/3" rel="previous" type="application/xml"/> </bookstore>
FYI, I am using Resteasy 3.0.8 with Wildfly 8.1
UPDATE: using automatic detection
So, I tried the example reference manual and I cannot reproduce your problem. Not sure about your complete environment, but here is what I use
- Wildfly 8.1
- Resteasy 3.0.8
- Maven
Here is the code
Application class
@ApplicationPath("/rest") public class BookApplication extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<>(); classes.add(Bookstore.class); return classes; } }
Resource class
@Path("/books") @Produces({"application/xml", "application/json"}) public class Bookstore { @AddLinks @LinkResource(value = Book.class) @GET @Formatted public Collection<Book> getBooks() { List<Book> books = new ArrayList<>(); books.add(new Book("Book", "Author")); return books; } }
Book class
@XmlRootElement @XmlAccessorType(XmlAccessType.NONE) public class Book { @XmlAttribute private String author; @XmlID @XmlAttribute private String title; @XmlElementRef private RESTServiceDiscovery rest; public Book() {} public Book(String title, String author) { this.title = title; this.author = author; } }
pom.xml (Maybe you are missing some dependencies - Note below: resteasy-client and resteasy-servlet-initializer were only for testing)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.underdogdevs.web</groupId> <artifactId>jaxrs-stackoverflow-user</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>jaxrs-stackoverflow-user</name> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jackson2-provider</artifactId> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jaxb-provider</artifactId> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jaxrs</artifactId> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>jaxrs-api</artifactId> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-links</artifactId> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-client</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-servlet-initializer</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>jboss-javaee-7.0-with-resteasy</artifactId> <version>8.1.0.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>jboss-javaee-7.0-with-tools</artifactId> <version>8.1.0.Final</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.6</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>7.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Works great in browser

Works fine with api client
public class BookTest { private static Client client; @BeforeClass public static void setUpClass() { client = ClientBuilder.newClient(); } @AfterClass public static void tearDownClass() { client.close(); } @Test public void testBookLink() { String BASE_URL = "http://localhost:8080/jaxrs-stackoverflow-user/rest/books"; WebTarget target = client.target(BASE_URL); String result = target.request() .accept(MediaType.APPLICATION_XML).get(String.class); System.out.println(result); } }
Result
Running jaxrs.book.test.BookTest <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <collection xmlns:atom="http://www.w3.org/2005/Atom"> <book author="Author" title="Book"> <atom:link rel="list" href="http://localhost:8080/jaxrs-stackoverflow-user/rest/books"/> </book> </collection>
As for your fuzzy stuff
Annotate the JAX-RS method with @AddLinks to indicate that you want Atom links to be entered in your response object.
This means that the method will use link injection.
Annotate JAX-RS methods for which you need Atom links with @LinkResource , so RESTEasy knows which links are created for which resources.
This allows you to customize which links are entered and which objects. 8.2.4. Determining which JAX-RS methods are tied to which resources are more in-depth.
Add the RESTServiceDiscovery fields to the resource classes in which you want to enter Atom feeds.
"injected" means that the framework will create it for you, so you never have to explicitly do it yourself (as you tried to do). Perhaps some studies on injection dependency and inversion control (IoC)
Luck. Hope all this helps.