users@jersey.java.net

Jersey 1.1 and implicit views

From: Owen Jacobson <owen.jacobson_at_grimoire.ca>
Date: Mon, 14 Dec 2009 21:09:26 -0500

Hi guys,

I'm trying out Jersey, since a lot of JAX-RS makes good sense to me,
and while I'm very happy with it as a way of producing APIs, I'm
having trouble getting implicit views to work the way I think they
should.

First, some background: my philosophy on webapp development runs along
the lines that the URLs for a given resource should be the same
whether it's a user with a browser accessing it (and getting HTML
back) or a program accessing it (and getting XML or JSON back). URL
decorations like ".xml" vs ".html" extensions don't appeal to me. JAX-
RS's content negotiation supports that really well, provided you're
building HTML as a String or Reader somewhere for HTML views. Jersey's
implicit viewables, from what I've read on this mailing list and on
Paul Sandoz' blog, seem to do what I want using JSP as the view
technology.

So, with all of that said, here's what I've got. There are two
classes: a JAX-RS resource class:

package ca.grimoire.scratch.jersey;

import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import com.sun.jersey.api.view.ImplicitProduces;

@Path("/hello")
@ImplicitProduces("text/html")
public class Hello {
        @Produces("application/xml")
        public HelloDocument getIndex() {
                return new HelloDocument();
        }
}

// EOF //

and a JAXB-mapped bean class:

package ca.grimoire.scratch.jersey;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "hello", namespace = "http://www.example.com/jersey-jsp-example
")
public class HelloDocument {
        @XmlElement
        public String getMessage() {
                return "Hello, world!";
        }

}

// EOF //

I've also got a template in ca/grimoire/scratch/jersey/Hello/index.jsp:

<html>
<head>
<title>It works!</title>
</head>

<body>
<h1>Message</h1>
<p>${it.index.message}</p>
</body>
</html>

Finally, for completeness, web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     version="2.5">
     <servlet>
         <servlet-name>Jersey</servlet-name>
         <servlet-
class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-
class>
         <init-param>
             <param-name>com.sun.jersey.config.property.packages</
param-name>
             <param-value>ca.grimoire.scratch.jersey</param-value>
         </init-param>
         <init-param>
             <param-
name>com.sun.jersey.config.feature.ImplicitViewables</param-name>
             <param-value>true</param-value>
         </init-param>
     </servlet>

     <servlet-mapping>
         <servlet-name>Jersey</servlet-name>
         <url-pattern>/rs/*</url-pattern>
     </servlet-mapping>
</web-app>

I'm serving my app inside Jetty 7.0.1.v20091125 (via jetty-maven-
plugin).

Looking at https://jersey.dev.java.net/servlets/ReadMsg?listName=users&msgNo=4832
  suggests that this should produce HTML by preference, or XML when
XML is requested. Unfortunately, that's not what I actually see:

$ curl http://localhost:8080/rs/hello
<html>
<head>
<title>It works!</title>
</head>

<body>
<h1>Message</h1>
<p>Hello, world!</p>
</body>
</html>

(This is as desired.)

$ curl --header 'Accept: application/xml' http://localhost:8080/rs/hello
<html>
<head>
<title>It works!</title>
</head>

<body>
<h1>Message</h1>
<p>Hello, world!</p>
</body>
</html>

(This is not, and a 406 NOT ACCEPTABLE would've been vastly preferable
to the wrong content type.)

I also get a WARNING in the logs:
WARNING: A resource class, class ca.grimoire.scratch.jersey.Hello,
does not have any resource method, sub-resource method, or sub-
resource locator.

Since none of the methods are annotated with @GET, this makes sense,
but I can't figure out from the docs where @GET belongs in this case.

Rewriting Hello without @ImplicitProduces but *with* a @GET, as

package ca.grimoire.scratch.jersey;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/hello")
public class Hello {
        @GET
        @Produces("application/xml")
        public HelloDocument getIndex() {
                return new HelloDocument();
        }
}

does almost what I want -- provided the Accept header is specific
enough. Browsers, or at least Safari, still get the "wrong" content
type (application/xml, instead of text/html) by default, which makes
actually using the application a little painful.

It's also odd to me that the "it" attribute in the resulting page is
the resource itself, rather than the bean returned from the resource
method -- note that Hello#getIndex() returns a HelloDocument, which
has a "message" bean property (getMessage() only), but the JSP uses $
{it.index.message} and invokes Hello#getIndex().getMessage() rather
than ${it.message} and HelloDocument#getMessage(), as I would've
expected.

So here are my questions:

1. Is it possible to use implicit viewables and prioritize text/html
instead of application/xml?
1b. Why did my first attempt produce HTML when I asked for XML, and
how can I avoid that (and ideally produce 406 responses instead) in
the future?
2. What's the rationale behind passing the JAX-RS resource instance to
the JSP as "it" rather than the value returned from the request
handler method?
3. Is it possible to configure Jersey to pass the resulting
HelloDocument bean to the JSP as "it", instead of the Hello instance?

Cheers, and thanks for an otherwise awesome tool.

-o