Diver
Diver

Reputation: 1608

How do I catch or raise exceptions inside a Viewable object in a restful service

I want to be able to return a specific jsp page from restful service given the page name as a PathParam. These pages will be added and removed by a different group of people after the application is deployed.

I believe using the Viewable object is a decent way to go (I'm open to a better/correct way.)

@GET
@Produces(MediaType.TEXT_HTML)
@Path("{page}") 
public Response showJSP(@PathParam("page") String page) {
    Viewable loadPage = null;

    try {
      loadPage = new Viewable("/" + page, null);
    } catch (Exception e) {
        return Response.status(404).build();
    }

  Response ret = Response.ok(loadPage).build();

//    if (ret.getStatus() == 500){
//          ret = Response.status(404).build();
//      }

 return ret;
}

The above is my experimental code and contains my attempts to handle the error.

This works fine as long as the page name is a valid jsp page. If I pass in an invalid page I get

500 Internal Server Error

java.io.IOException: The template name, /someWrongFileName, could not be resolved to a fully qualified template name.....

What I've figured out is this error is generated internal to the Viewable object so it's not throwing an exception and the Response of course is a valid page so checking for the 500 status doesn't work.

I can hack in several things that I'm pretty sure are wrong such as, I really do not want to toString the generated page and regex for the error text.

I would like to be able to detect that the page is invalid and simply return a 404.

Am I on the right path or is there a better way?

If I'm on the right path how do I detect the bad page name?

Upvotes: 2

Views: 1363

Answers (2)

Diver
Diver

Reputation: 1608

I found what I'm looking for in another post and modified my method below.

@GET
@Produces("text/html")
@Path("{page}") 
public void showJSP(@Context HttpServletResponse response,
                    @Context HttpServletRequest request,
                    @PathParam("orderId") String orderId) throws ServletException, IOException {
    request.getRequestDispatcher(page + ".jsp").forward(request, response);
}

This gives me my 404 that I'm looking for and all of my services still work.

If this is the 'wrong' way to do this I'm still open for ideas but for now it works.

Upvotes: 2

toniedzwiedz
toniedzwiedz

Reputation: 18563

I've tried catching the error like you did but it seems pretty hard. I suppose the class was never intended to be used this way.

The Viewable interface lets you use JSPs as a means to visualize the representation of your application's resources. In general, the resource is represented by a POJO that you serialize to JSON, XML or pass to the Viewable constructor.

As far as I understand, what you're trying to do here is a little different. Your resources are the JSPs themselves and it seems you only want to make the pages available to clients, without passing any objects to the Viewable constructor.

The HTTP 404 error means that a resource can not be found. In your case (while treating the JSPs as resources), when the path to a JSP is incorrect, this is exactly what happens so I understand why you want to use the status code.

However, I think that the creators of the interface you're trying to use had a different opinion on the matter. They didn't see the JSPs as resources but as a tool to represent them. The construction of a view is seen here as a completely different thing. A matter internal to the server and something that should be hidden from the client. The client has to receive a response with an HTML string in it. How it happens should not matter at all. An HTTP 500 is totally understandable in such context.

If you only want to use GET requests to fetch the contents of your JSPs, you can just ignore the Viewable interface or even Jersey itself. If your web.xml is appropriately set, the pages should be accessible anyway. You don't have to pass a JSP name to an annotated method. Just use paths to the documents themselves. Custom 404s can be dealt with in web.xml as well.

Assume that you have a project called MyApp and deployed to the path <host>:<port>/MyApp

With the following structure of its Web pages directory.

-Web pages
|-META-INF
|-WEB-INF
|-error
|\-error404.jsp
|-package1
||-ResourceClass
||\-page1.jsp
|-pages
||-plainpage1.jsp
|\-plainpage2.jsp
\-index.jsp

Now, let's assume the web.xml looks like this:

<web-app version="3.0" 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_3_0.xsd">
<servlet>
    <servlet-name>ServletAdaptor</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>ServletAdaptor</servlet-name>
    <url-pattern>/resources/ *</url-pattern>
</servlet-mapping>
<error-page>
    <error-code>404</error-code>
    <location>/err/err404.jsp</location>
</error-page>
<session-config>
    <session-timeout>
        30
    </session-timeout>
</session-config>

You can use the Viewable interface to show a page for your actual resource. This is how the interface is supposed to be used. As a means to show a resource's html representation, not the resource itself.

package package1;

@Path("/")
public class ResourceClass {    

@Path("/POJO/{id}")
@GET
public Response tryToGetBadView(@PathParam("id") String id) {
    MyPOJOClass entity = null;
    //fetch your POJO from database/file/whatever          
    return Response.ok().entity(new Viewable("page1",entity)).build();        
}
}

You can get the appropriate view using a path like:

<host>:<port>/MyApp/resources/POJO/1

while also being able to get the plain JSPs without the help of Jersey Servlet Container. The files are there and they don't need any other representation than themselves. The path to your Jersey Servlet Container is specified as

<host>:<port>/resources/*

So you can omit the container and reach normal files from your Web Apps folder. Images, css and also JSPs. Like this:

<host>:<port>/MyApp/pages/plainpage1.jsp

the same way you get

<host>:<port>/MyApp/index.jsp

and even a page that you'd normally use to construct a Viewable object (without the guarantee that it would work properly without passing a POJO)

<host>:<port>/MyApp/package1/ResourceClass/page1.jsp

For these "static" files, you'll get a 404 every time you pick a name of a non-existent page. The error page displayed will be the one specified in web.xml

This is how I'd do it. Using Jersey to serve plain JSPs seems like an overkill and brings unnecessary complication. If it's not what you meant, I suggest rethinking the design.

Notice that while Jersey is not used, the pages can still be accessed in a way you can consider RESTful.

If you really want to stick to your experimantal code, you can also try accessing the directories in your Web Pages folder using the IO API and checking manually whether the requested files exist. It'd be ugly but it might just work.

Upvotes: 2

Related Questions