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