users@jersey.java.net

Re: [Jersey] Doh! Re: [Jersey] ExceptionMapper and Viewable

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Thu, 15 Oct 2009 12:50:44 +0200

H Jordi,

I have attached a very simple project that exercises many of the
possible ways of mapping exceptions.

Hopefully it should be fairly intuitive to understand. The main
index.jsp provides links to click on that results in various forms of
error pages.

Results of investigation:

1) Jersey will propagate runtime exceptions to the servlet layer if
those exceptions are not mapped by
      an ExceptionMapper. Those exceptions can then be mapped using an
error page.
      See the error page SecurityExceptionErrorPage.jsp and the link:

        http://localhost:8080/exceptions/webresources/myresource/runtime?ex=java.lang.SecurityException

      supported by the following resource method:

       @Path("runtime")
       @GET
       @Produces("text/plain")
       public String runtimeException(@QueryParam("ex") String
className)
               throws ClassNotFoundException, InstantiationException,
IllegalAccessException {
           Class c = Class.forName(className, true,
this.getClass().getClassLoader());
           RuntimeException re = (RuntimeException)c.newInstance();
           if (true) throw re;
           return "Hi there!";
       }


2) Jersey will propagate checked exceptions wrapped in a
MappableContainerException to the servlet layer.
      That exception can then be mapped using an error page and the
cause can be extracted.
      See the error page MappableContainerException.jsp and the links:

        http://localhost:8080/exceptions/webresources/myresource/checked/ioexception
        http://localhost:8080/exceptions/webresources/myresource/checked/myexception

      supported by the following resource methods:

       @Path("checked/ioexception")
       @GET
       @Produces("text/plain")
       public String checkedIOException() throws IOException {
           if (true) throw new IOException();
           return "Hi there!";
       }

       public static class MyException extends Exception {
       }

       @Path("checked/myexception")
       @GET
       @Produces("text/plain")
       public String checkedMyException() throws MyException {
           if (true) throw new MyException();
           return "Hi there!";
       }


3) If Jersey sets the status code in the servlet response it is not
possible for error pages to be mapped to the
      status code.
      See the links:

        http://localhost:8080/exceptions/404
        http://localhost:8080/exceptions/webresources/404

     The first link is mapped to the 404 bound error page,
NotFound.jsp. The the second link returns the default
     GF 404 error page. The first works because the Jersey servlet is
not invoked.

     I verified i get the same behavior with a simple servlet if one
only sets the status code to 404.
     However, i could get this to work if instead of calling
HttpServletResponse.setStatus i call
     HttpServletResponse.sendError.


Conclusions:

Result 1 is working correctly.

Result 2 could be improved by unwrapping the cause in the
MappableContainerException and re-throwing the cause wrapped in
ServletException.

Result 3 might be a bug in the servlet container. I am not quite sure
how to work around it. For example if using sendError then any headers
that have been added are ignored. It seems to me the correct behavior
would be to map status codes if no entity is present (nor no bytes
have been written to the response output stream).

Paul.





On Oct 15, 2009, at 9:20 AM, Jordi Domingo wrote:

> Hi Paul,
>
> Thanks for your help. Your hack does his work, a NPE is generated
> then forwaded to the error page. Is there a way to recover the
> original exception?
>
> On the other way, I dont understand why you get the
> SecurityException passed to the Servlet Container (im using GFv3
> also). I followed your suggestions and simplified the jsp to its
> smallest form and it never gets passed without the hack, maybe I'm
> missing something.
>
> Jordi
>
> Date: Wed, 14 Oct 2009 12:32:16 +0200
> From: Paul.Sandoz_at_Sun.COM
> To: users_at_jersey.dev.java.net
> Subject: Re: [Jersey] Doh! Re: [Jersey] ExceptionMapper and Viewable
>
> Hi,
>
> I just did a quick test throwing the runtime SecurtyException as in
> your example and verified that it gets passed to the Servlet
> container (in GF v3 at least no stack trace is presented to the user).
>
> When i threw a checked exception, say IOException, a
> MappableContainerException, (of the cause IOException), gets passed
> to the Servlet container.
>
> So if you are not using an exception mapper it should be possible to
> react to both runtime and checked exceptions in the servlet layer.
>
> In your web.xml i think the following is incorrect:
>
> <error-page>
> <exception-type>java.lang.Throwable</exception-type>
> <location>/ejbException.jsp</location>
> </error-page>
>
> because a 404 will be returned. You will need to place that under
> the jsp directory. I verified that i can do this can get error pages
> called for java.lang.Exception and
> com.sun.jersey.api.container.MappableContainerException, or just for
> java.lang.Throwable.
>
>
> There are cases when Jersey will send out the stack trace with a 500
> response. That is something we can fix, but not in the time-frame
> you require.
>
> But, i think there may be a possible solution for certain cases (see
> later). That is to write a ContainerResponseFilter to check for a
> 500 status code and to remove the entity.
>
> package foo;
> public class HackFilter implements ContainerResponseFilter {
>
> public ContainerResponse filter(ContainerRequest request,
> ContainerResponse response) {
> if (response.getStatus() == 500) {
> response.setEntity(null);
> response.getHttpHeaders().put("Content-Type", null);
> }
> return response;
> }
> }
>
>
> In your web.xml you can register that filter:
>
> <init-param>
> <param-
> name>com.sun.jersey.spi.container.ContainerResponseFilters</param-
> name>
> <param-value>foo.HackFilter</param-value>
> </init-param>
>
> The cases where this will work is when any response filters before
> HackFilter throw an exception. Or when an exception occurs when
> writing out the response entity.
>
> Hope this helps,
> Paul.
>
> On Oct 14, 2009, at 9:53 AM, Jordi Domingo wrote:
>
> Good morning Paul,
>
> Did you get a solution for this? We are going to publish the site
> very soon (tomorrow) and we are focused on security so dumping full
> stack trace is not very secure :)
>
> Thanks again,
>
> Jordi
>
> Date: Mon, 5 Oct 2009 14:54:12 +0200
> From: Paul.Sandoz_at_Sun.COM
> To: users_at_jersey.dev.java.net
> Subject: [Jersey] Doh! Re: [Jersey] ExceptionMapper and Viewable
>
> DOH! i should read the email more carefully. Extracted successfully.
>
> Paul.
>
>
> On Oct 5, 2009, at 2:39 PM, Paul Sandoz wrote:
>
> Thanks, this is most helpful.
>
> I tried to unrar and it is complaining that a password is required.
> Looks like it might be an issue with the rar app i am using on the
> mac.
>
> UNRAR 3.60 beta 6 freeware Copyright (c) 1993-2006 Alexander
> Roshal
>
> Enter password (will not be echoed) for RestTest.rar:
>
> Encrypted file: CRC failed in /Users/paulsandoz/Downloads/
> RestTest.rar (password incorrect ?)
> No files to extract
>
> I am a bit busy this week with Java EE stuff, so i am unlikely to
> get time to look at this in detail until next week. Hope things are
> not so urgent that it can wait until then.
>
>
> FWIW i think there is a bug in Jersey and any
> MappedContainerException passed to the ServletContainer should re-
> throw the ServletException with the cause set to what was mapped.
>
> Paul.
>
> On Oct 2, 2009, at 6:03 PM, Jordi Domingo wrote:
>
> I made a very simple example, just a resource that throws a
> SecurityException without anything else. Its not maven, i dont use
> it :(
> I also added a system out of the media type, i get null again
> (resource has a Produces annotation)
>
> I uploaded the file to rapidshare, the password is jersey. http://rapidshare.com/files/287805087/RestTest.rar.html
>
> Thanks,
>
> Jordi
>
> P.D.
> The stacktrace of the example is:
>
> javax.ejb.EJBException
> at
> com
> .sun
> .ejb
> .containers.BaseContainer.processSystemException(BaseContainer.java:
> 4947)
> at
> com
> .sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:
> 4845)
> at
> com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:
> 4633)
> at
> com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:
> 1871)
> at
> com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:
> 1822)
> at
> com
> .sun
> .ejb
> .containers
> .EJBLocalObjectInvocationHandler
> .invoke(EJBLocalObjectInvocationHandler.java:198)
> at
> com
> .sun
> .ejb
> .containers
> .EJBLocalObjectInvocationHandlerDelegate
> .invoke(EJBLocalObjectInvocationHandlerDelegate.java:84)
> at $Proxy185.failMethod(Unknown Source)
> at
> com
> .domborjo
> .rest
> .__EJB31_Generated__TestResource__Intf____Bean__.failMethod(Unknown
> Source)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun
> .reflect
> .NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
> at
> sun
> .reflect
> .DelegatingMethodAccessorImpl
> .invoke(DelegatingMethodAccessorImpl.java:25)
> at java.lang.reflect.Method.invoke(Method.java:597)
> at
> com
> .sun
> .jersey
> .server
> .impl.model.method.dispatch.AbstractResourceMethodDispatchProvider
> $
> TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:
> 156)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .model
> .method
> .dispatch
> .ResourceJavaMethodDispatcher
> .dispatch(ResourceJavaMethodDispatcher.java:67)
> at
> com
> .sun
> .jersey
> .server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:208)
> at
> com
> .sun
> .jersey
> .server
> .impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:75)
> at
> com
> .sun
> .jersey
> .server
> .impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:115)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .uri
> .rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:67)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .application
> .WebApplicationImpl._handleRequest(WebApplicationImpl.java:724)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .application
> .WebApplicationImpl.handleRequest(WebApplicationImpl.java:689)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .application
> .WebApplicationImpl.handleRequest(WebApplicationImpl.java:680)
> at
> com
> .sun
> .jersey.spi.container.servlet.WebComponent.service(WebComponent.java:
> 324)
> at
> com
> .sun
> .jersey
> .spi
> .container.servlet.ServletContainer.service(ServletContainer.java:425)
> at
> com
> .sun
> .jersey
> .spi
> .container.servlet.ServletContainer.doFilter(ServletContainer.java:
> 751)
> at
> com
> .sun
> .jersey
> .spi
> .container.servlet.ServletContainer.doFilter(ServletContainer.java:
> 703)
> at
> org
> .apache
> .catalina
> .core
> .ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:
> 256)
> at
> org
> .apache
> .catalina
> .core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
> at
> org
> .apache
> .catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:
> 277)
> at
> org
> .apache
> .catalina.core.StandardContextValve.invoke(StandardContextValve.java:
> 188)
> at
> org
> .apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:
> 641)
> at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
> at
> com
> .sun
> .enterprise
> .web
> .PESessionLockingStandardPipeline
> .invoke(PESessionLockingStandardPipeline.java:85)
> at
> org
> .apache
> .catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
> at
> org
> .apache
> .catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:333)
> at
> org
> .apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:
> 234)
> at
> com
> .sun
> .enterprise
> .v3.services.impl.ContainerMapper.service(ContainerMapper.java:146)
> at
> com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:
> 753)
> at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:
> 661)
> at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:914)
> at
> com
> .sun
> .grizzly
> .http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:166)
> at
> com
> .sun
> .grizzly
> .DefaultProtocolChain
> .executeProtocolFilter(DefaultProtocolChain.java:135)
> at
> com
> .sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:
> 102)
> at
> com
> .sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:
> 88)
> at
> com
> .sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
> at
> com
> .sun
> .grizzly
> .ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
> at
> com
> .sun
> .grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
> at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
> at com.sun.grizzly.util.FixedThreadPool
> $BasicWorker.dowork(FixedThreadPool.java:379)
> at com.sun.grizzly.util.FixedThreadPool
> $BasicWorker.run(FixedThreadPool.java:360)
> at java.lang.Thread.run(Thread.java:619)
> Caused by: java.lang.SecurityException
> at com.domborjo.rest.TestResource.failMethod(TestResource.java:24)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun
> .reflect
> .NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
> at
> sun
> .reflect
> .DelegatingMethodAccessorImpl
> .invoke(DelegatingMethodAccessorImpl.java:25)
> at java.lang.reflect.Method.invoke(Method.java:597)
> at
> org
> .glassfish
> .ejb
> .security
> .application.EJBSecurityManager.runMethod(EJBSecurityManager.java:
> 1038)
> at
> org
> .glassfish
> .ejb
> .security
> .application.EJBSecurityManager.invoke(EJBSecurityManager.java:1110)
> at
> com
> .sun
> .ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:
> 5120)
> at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:610)
> at
> com
> .sun
> .ejb
> .containers
> .interceptors
> .AroundInvokeChainImpl.invokeNext(InterceptorManager.java:775)
> at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:562)
> at
> com
> .sun
> .ejb
> .containers
> .interceptors
> .SystemInterceptorProxy.doAround(SystemInterceptorProxy.java:157)
> at
> com
> .sun
> .ejb
> .containers
> .interceptors
> .SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:139)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun
> .reflect
> .NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
> at
> sun
> .reflect
> .DelegatingMethodAccessorImpl
> .invoke(DelegatingMethodAccessorImpl.java:25)
> at java.lang.reflect.Method.invoke(Method.java:597)
> at
> com
> .sun
> .ejb
> .containers
> .interceptors
> .AroundInvokeInterceptor.intercept(InterceptorManager.java:836)
> at
> com
> .sun
> .ejb
> .containers
> .interceptors
> .AroundInvokeChainImpl.invokeNext(InterceptorManager.java:775)
> at
> com
> .sun
> .ejb
> .containers
> .interceptors.InterceptorManager.intercept(InterceptorManager.java:
> 349)
> at
> com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:
> 5092)
> at
> com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:
> 5080)
> at
> com
> .sun
> .ejb
> .containers
> .EJBLocalObjectInvocationHandler
> .invoke(EJBLocalObjectInvocationHandler.java:190)
> ... 45 more
>
>
> Date: Fri, 2 Oct 2009 17:23:42 +0200
> From: Paul.Sandoz_at_Sun.COM
> To: users_at_jersey.dev.java.net
> Subject: Re: [Jersey] ExceptionMapper and Viewable
>
>
> On Oct 2, 2009, at 5:10 PM, Jordi Domingo wrote:
>
> Hi again Paul!
>
> I saw that sometimes (to change behavior the server must be
> restarted) the exception goes through the mapper and sometimes not
> and it gets dumped. Maybe is a glassfish bug :S
>
> Quite possibly.
>
>
> In any case, do you know wich component may be dumping the exception
> (its an stacktrace like the one in glassfish logs).
>
> Can you send the stacktrace ?
>
>
> ----
>
> I tried using HttpContext:
>
> @Context HttpContext httpContext;
>
> And in the toResponse(..)
>
> MediaType m = httpContext.getResponse().getMediaType();
> MediaType selected = httpContext.getRequest().getMediaType();
>
> I get null.
>
> If "m" is null it implies that no resource method was called with
> @Produces.
>
> I think i would need to see more of your application to understand
> where the EJBException is originating from. In fact if you can send
> me a zip file of a maven project that would help.
>
> Recently i modified the behavior of processing EJBExcpetion such
> that Jersey registered a mapper for that class (see end of email).
> This is in 1.1.2. So actually EJBExcpetion will never be propagated
> to the Servlet container. Perhaps that is why things are not working
> for you. Note that you can override this with your own implementation.
>
> The mapper will attempt to extract the cause of the exception then
> attempt to map that.
>
> Does that give any more clues? as i said an example would help then
> i can debug it.
>
> Paul.
>
>
> public class EJBExceptionMapper implements
> ExceptionMapper<EJBException> {
>
> private final Providers providers;
>
> public EJBExceptionMapper(@Context Providers providers) {
> this.providers = providers;
> }
>
> public Response toResponse(EJBException exception) {
> final Exception cause = exception.getCausedByException();
> if (cause != null) {
> final ExceptionMapper mapper =
> providers.getExceptionMapper(cause.getClass());
> if (mapper != null) {
> return mapper.toResponse(cause);
> } else if (cause instanceof WebApplicationException) {
> return ((WebApplicationException)cause).getResponse();
> }
> }
>
> return Response.serverError().build();
> }
> }
>
>
>
> ---
> The first thing i tried was using the servlet exception rules (im
> using that yet with the mapper for EJBException) but happens the
> same with glassfish dumping the exception:
>
> <error-page>
> <exception-
> type>com.sun.jersey.api.container.MappableContainerException</
> exception-type>
> <location>/jsp/ejbException.jsp</location>
> </error-page>
> <error-page>
> <error-code>500</error-code>
> <location>/jsp/ejbException.jsp</location>
> </error-page>
>
> ---
> When a user sends a request, jersey selects wich method must attend
> the user and jersey know which one will be the response type by
> default. Is there any method for me, to know wich one will be?
> (httpContext.getResponse().getMediaType() returns null)
>
> Thanks Paul!
>
> > Date: Thu, 1 Oct 2009 10:33:48 +0200
> > From: Paul.Sandoz_at_Sun.COM
> > To: users_at_jersey.dev.java.net
> > Subject: Re: [Jersey] ExceptionMapper and Viewable
> >
> > On Sep 30, 2009, at 1:44 PM, Jordi Domingo wrote:
> >
> > >
> > > Hi :)
> > >
> > > I'm trying to make an ExceptionMapper that returns the response
> > > expected by the client. I dont know how i can return a Viewable
> or a
> > > JSON response because i dont have access to any object with that
> > > information
> > >
> > > public Response toResponse(EJBException exception) {}
> > >
> >
> > If a resource method was selected for invocation then the Content-
> Type
> > of the response will have been selected if the @Produces specifies a
> > concrete (non-wild card) media type. So if the Content-Type is
> present
> > you can look at that.
> >
> > You can inject Jersey's HttpContext on the ExceptionMapper and in
> the
> > toResponse method you can do:
> >
> > MediaType m = httpContext.getResponse().getMediaType();
> >
> > You can also get access to the acceptable headers by doing:
> >
> > httpContext.getRequest().getAcceptableMediaTypes()
> >
> > (That method is a method on javax.ws.rs.core.HttpHeaders, which
> can be
> > injected if required rather than referring to something Jersey
> > specific).
> >
> >
> > > On another side, when Jersey finds an Exception (not Runtime) that
> > > is not mapped, forwards it to the ServletContainer, thats fine.
> >
> > Any RuntimeException that is not mapped will be re-thrown and it
> will
> > pass through Jersey's Servlet layer to the Servlet framework from
> > where it can be mapped using Servlet-based rules.
> >
> > Any checked exception that is not mapped will be wrapped around a
> > Jersey runtime exception (ContainerException) and that will be
> passed
> > to Jersey's Servlet layer and wrapped in a ServletException
> exception
> > and re-thrown.
> >
> >
> > > When i do the same with an EJBException (SystemException) I dont
> > > know how to achieve the same behavior. This is my code for
> testing:
> > >
> > > public Response toResponse(EJBException exception) {
> > > final Exception cause = exception.getCausedByException();
> > >
> > > return new WebApplicationException(cause).getResponse();
> > > }
> > >
> >
> > I am not sure i understand what you want to achieve.
> >
> > Do you want the EJBException to be passed through to the Servlet
> > framework?
> >
> > Paul.
> >
> >
> ---------------------------------------------------------------------
> > To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> > For additional commands, e-mail: users-help_at_jersey.dev.java.net
> >
>
> Charlas más divertidas con el nuevo Windows Live Messenger
>
>
> Charlas más divertidas con el nuevo Windows Live Messenger
>
>
>
> Todo el espacio y cuidado que merecen tus fotos digitales lo tienes
> en Windows Live Fotos. ¡Pruébalo!
>
>
> ¿Para qué descargarte juegos, si tienes los más divertidos online?
> Entra ya en Juegos y prepárate para muchas horas de diversión