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