I have a rich client application that uses the Apache Cayenne Remote Object 
Persistence framework to access and store data. Thus far I have been unable to 
find a graceful way of detecting and handling a client connection timeout. A 
MissingSessionException is thrown on both the client and server when an action 
is attempted after the session has timed out, but on the client side this 
exception is caught and a stacktrace is printed to the console without 
propagating out to the caller:

    Apr 07, 2016 2:48:48 PM org.apache.cayenne.remote.BaseConnection sendMessage
    INFO: --- Message 6: Query
    Apr 07, 2016 2:48:48 PM org.apache.cayenne.remote.BaseConnection sendMessage
    INFO: *** Message error for 6: Query - took 16 ms.
    org.apache.cayenne.remote.service.MissingSessionException: [v.4.0.M2 Feb 26 
2015 08:16:32] [v.4.0.M2 Feb 26 2015 08:16:32] No session associated with 
request.
        at 
org.apache.cayenne.remote.service.BaseRemoteService.processMessage(BaseRemoteService.java:127)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at 
com.caucho.hessian.server.HessianSkeleton.invoke(HessianSkeleton.java:180)
        at 
com.caucho.hessian.server.HessianSkeleton.invoke(HessianSkeleton.java:109)
        at 
com.caucho.hessian.server.HessianServlet.service(HessianServlet.java:396)
        at 
org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:808)
        at 
org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
        at 
org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)
        at 
org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
        at 
org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
        at 
org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
        at 
org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
        at 
org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
        at 
org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
        at 
org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
        at 
org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
        at 
org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
        at 
org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
        at 
org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
        at 
org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at 
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
        at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
        at 
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
        at 
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
        at 
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
        at 
org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
        at 
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
        at 
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
        at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
        at 
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
        at 
org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:110)
        at 
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
        at org.eclipse.jetty.server.Server.handle(Server.java:499)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:310)
        at 
org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
        at 
org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
        at 
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
        at 
org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
        at java.lang.Thread.run(Thread.java:745)

Is there a simple way of detecting that the connection is dead so that I can 
prompt the user to reconnect?

On a related note, what happens to the existing client ObjectContext objects 
that are in use if the connection is able to be re-established? Are the 
uncommitted PersistentObjects previously created in those contexts lost forever?

The only (hacky) solution I can think of is to actually run a keep-alive thread 
to periodically send a low-latency query to Cayenne to keep the ROP session 
active and separately track application activity and prompt the user to enter 
their password if a certain period of time has passed with no activity. The 
problem that I see with an approach like that is that there are lots of ways 
that activity could be missed, the application is not truly timing out, and it 
really doesn't address the underlying problem which is that sessions need to 
time out for a reason and there doesn't seem to be a way to detect such a 
timeout.

Thanks in advance for any help you can provide!
-Adam

Reply via email to