You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Adam Boyle <ab...@valsphere.com> on 2016/04/08 05:28:31 UTC

ROP: detecting that my client connection has timed out

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


Re: ROP: detecting that my client connection has timed out

Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 9/04/2016 1:34am, Adam Boyle wrote:
> I'll also see about getting a build from trunk to dig into the latest ROP code. Admittedly back in January I questioned my decision to use Cayenne when I saw that the latest alpha release was from February 2015, so it's a relief to see 4.0.M3 and more commits coming in for ROP :)

Yes, we really should release milestones more often. However trunk is kept very stable and I find myself using custom builds from it in production fairly often.

Perhaps if you have any feedback about the new ROP integration bits, we might plan for another milestone release sooner rather than later.

Do let us know if you have any questions or suggestions about the new ROP integration. Hopefully I'll be moving to HTTP2 in our own application this coming week.

Ari


-- 
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: ROP: detecting that my client connection has timed out

Posted by Adam Boyle <ab...@valsphere.com>.
I'll also see about getting a build from trunk to dig into the latest ROP code. Admittedly back in January I questioned my decision to use Cayenne when I saw that the latest alpha release was from February 2015, so it's a relief to see 4.0.M3 and more commits coming in for ROP :)

-Adam

________________________________________
From: Aristedes Maniatis <ar...@maniatis.org>
Sent: Friday, April 8, 2016 2:45 AM
To: user@cayenne.apache.org
Subject: Re: ROP: detecting that my client connection has timed out

On 8/04/2016 3:06pm, Adam Boyle wrote:
> Does anyone know of a better way? Perhaps some advice for this novice enterprise developer?

Also I strongly advise taking a build of Cayenne trunk from after the release of 4.0M3. The new ROP refactoring in there is brand new but I've already got it in production and it works nicely. Much easier to customise for your needs.

We are currently right now finishing up migration to Jetty HTTP client as an option in Cayenne. This will allow us to use HTTP2 and hopefully get better performance over high latency connections.



Ari


--
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: ROP: detecting that my client connection has timed out

Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 8/04/2016 3:06pm, Adam Boyle wrote:
> Does anyone know of a better way? Perhaps some advice for this novice enterprise developer?

Also I strongly advise taking a build of Cayenne trunk from after the release of 4.0M3. The new ROP refactoring in there is brand new but I've already got it in production and it works nicely. Much easier to customise for your needs.

We are currently right now finishing up migration to Jetty HTTP client as an option in Cayenne. This will allow us to use HTTP2 and hopefully get better performance over high latency connections.



Ari


-- 
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: ROP: detecting that my client connection has timed out

Posted by Adam Boyle <ab...@valsphere.com>.
We're still in the initial implementation phase, so we haven't made a firm decision on the server container. In development I've been using the jetty-maven-plugin to run the server locally, so it probably makes sense to use Jetty in production as well.  I will try implementing the filter today and get back to you about it.

Thanks,
Adam

________________________________________
From: Aristedes Maniatis <ar...@maniatis.org>
Sent: Friday, April 8, 2016 2:40 AM
To: user@cayenne.apache.org
Subject: Re: ROP: detecting that my client connection has timed out

What container are you using on the server? For us, we use Jetty and it is simple enough to track sessions there (and store additional data against the session such as the database User object). Then use https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionListener.html to do something server-side in reaction to a session timeout.

Also look at javax.servlet.Filter. Implement:

        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {

and you should be able to add something like:

                try {
                        chain.doFilter(request, response);
                } catch (org.apache.cayenne.remote.service.MissingSessionException ex) {
                        // session forged or expired or user kicked out
                        session.invalidate();
                        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                }

You should be able to catch the 401 error on the client end and do something useful with it.


Does that help?

Ari


On 8/04/2016 3:06pm, Adam Boyle wrote:
> Thanks for the response. I could be overlooking it, but I don't see an answer to my central question: how can I detect that the client has timed out? The nature of our application prevents us from using a keep-alive type solution; it is meant to be used in highly regulated environments (which is one of the reasons I chose Cayenne ROP: it provides a way to hide the database details from the users). Even if I could use an auto-reconnect solution, I would still need to be able to detect that the connection has timed out, and I have failed to find a way to actually detect that.
>
> I suppose that another (less-hacky) way I could approach this would be to logically detect a "timeout" by keeping the actual connection alive and storing login session info in the database and updating it with the current date time every time a Cayenne action is performed. This code would check for a timeout beforehand and prompt the user to re-authenticate before performing said action. The main problem there is maintainability; I'd have to add this check to every place I intend to submit any sort of Cayenne action unless there is a way to hook this sort of check into the ObjectContext.
>
> Does anyone know of a better way? Perhaps some advice for this novice enterprise developer?
>
> Thanks again,
> Adam
>
> ________________________________________
> From: Aristedes Maniatis <ar...@maniatis.org>
> Sent: Friday, April 8, 2016 12:30 AM
> To: user@cayenne.apache.org
> Subject: Re: ROP: detecting that my client connection has timed out
>
> On 8/04/2016 1:28pm, Adam Boyle wrote:
>> Is there a simple way of detecting that the connection is dead so that I can prompt the user to reconnect?
>
> I don't think you need to prompt the user if you keep the user's authentication details in memory. Just create a new session with them.
>
>
>> 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?
>
> Those contexts are no longer associated with a session on the server (once your session times out). So you either have to recognise that a user is connecting again after a connection error and give them a handle on the same session id they had last time, or else start a new session and lose the old contexts.
>
> The problem is: can you be sure the state of those contexts still make sense?
>
>
>> 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.
>
> Well this is outside Cayenne itself, but part of the reason we recently did the work in trunk (CAY-2065) to untangle Cayenne from Hessian and from the HTTP layer. Then you can manage the session more easily yourself to do whatever you want.
>
> For example, we have a ping every minute from the client that:
>
> * keeps the session alive (we don't want the session to die just because the user went to have lunch)
> * allows us the keep the server-side session timeout quite low (good to expire sessions for users who dropped off the network without a proper logout)
> * allows the server to track which users are having network issues
>
> In the last six months we've been doing a bunch of work with ActiveMQ/STOMP which might eventually replace that ping. That way we have two way server-client communication and the server can quickly see which clients have lost network connection without a ping.
>
>
> Nice to see a fellow Cayenne ROP user. There aren't many of us and it is really a very powerful bit of functionality.
>
>
> Ari
>
>
>
> --
> -------------------------->
> Aristedes Maniatis
> GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A
>

--
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: ROP: detecting that my client connection has timed out

Posted by Aristedes Maniatis <ar...@maniatis.org>.
What container are you using on the server? For us, we use Jetty and it is simple enough to track sessions there (and store additional data against the session such as the database User object). Then use https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionListener.html to do something server-side in reaction to a session timeout.

Also look at javax.servlet.Filter. Implement:

	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {

and you should be able to add something like:

		try {
			chain.doFilter(request, response);
		} catch (org.apache.cayenne.remote.service.MissingSessionException ex) {
			// session forged or expired or user kicked out
			session.invalidate();
			response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
		}

You should be able to catch the 401 error on the client end and do something useful with it.


Does that help?

Ari


On 8/04/2016 3:06pm, Adam Boyle wrote:
> Thanks for the response. I could be overlooking it, but I don't see an answer to my central question: how can I detect that the client has timed out? The nature of our application prevents us from using a keep-alive type solution; it is meant to be used in highly regulated environments (which is one of the reasons I chose Cayenne ROP: it provides a way to hide the database details from the users). Even if I could use an auto-reconnect solution, I would still need to be able to detect that the connection has timed out, and I have failed to find a way to actually detect that.
> 
> I suppose that another (less-hacky) way I could approach this would be to logically detect a "timeout" by keeping the actual connection alive and storing login session info in the database and updating it with the current date time every time a Cayenne action is performed. This code would check for a timeout beforehand and prompt the user to re-authenticate before performing said action. The main problem there is maintainability; I'd have to add this check to every place I intend to submit any sort of Cayenne action unless there is a way to hook this sort of check into the ObjectContext.
> 
> Does anyone know of a better way? Perhaps some advice for this novice enterprise developer?
> 
> Thanks again,
> Adam
> 
> ________________________________________
> From: Aristedes Maniatis <ar...@maniatis.org>
> Sent: Friday, April 8, 2016 12:30 AM
> To: user@cayenne.apache.org
> Subject: Re: ROP: detecting that my client connection has timed out
> 
> On 8/04/2016 1:28pm, Adam Boyle wrote:
>> Is there a simple way of detecting that the connection is dead so that I can prompt the user to reconnect?
> 
> I don't think you need to prompt the user if you keep the user's authentication details in memory. Just create a new session with them.
> 
> 
>> 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?
> 
> Those contexts are no longer associated with a session on the server (once your session times out). So you either have to recognise that a user is connecting again after a connection error and give them a handle on the same session id they had last time, or else start a new session and lose the old contexts.
> 
> The problem is: can you be sure the state of those contexts still make sense?
> 
> 
>> 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.
> 
> Well this is outside Cayenne itself, but part of the reason we recently did the work in trunk (CAY-2065) to untangle Cayenne from Hessian and from the HTTP layer. Then you can manage the session more easily yourself to do whatever you want.
> 
> For example, we have a ping every minute from the client that:
> 
> * keeps the session alive (we don't want the session to die just because the user went to have lunch)
> * allows us the keep the server-side session timeout quite low (good to expire sessions for users who dropped off the network without a proper logout)
> * allows the server to track which users are having network issues
> 
> In the last six months we've been doing a bunch of work with ActiveMQ/STOMP which might eventually replace that ping. That way we have two way server-client communication and the server can quickly see which clients have lost network connection without a ping.
> 
> 
> Nice to see a fellow Cayenne ROP user. There aren't many of us and it is really a very powerful bit of functionality.
> 
> 
> Ari
> 
> 
> 
> --
> -------------------------->
> Aristedes Maniatis
> GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A
> 

-- 
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: ROP: detecting that my client connection has timed out

Posted by Adam Boyle <ab...@valsphere.com>.
Thanks for the response. I could be overlooking it, but I don't see an answer to my central question: how can I detect that the client has timed out? The nature of our application prevents us from using a keep-alive type solution; it is meant to be used in highly regulated environments (which is one of the reasons I chose Cayenne ROP: it provides a way to hide the database details from the users). Even if I could use an auto-reconnect solution, I would still need to be able to detect that the connection has timed out, and I have failed to find a way to actually detect that.

I suppose that another (less-hacky) way I could approach this would be to logically detect a "timeout" by keeping the actual connection alive and storing login session info in the database and updating it with the current date time every time a Cayenne action is performed. This code would check for a timeout beforehand and prompt the user to re-authenticate before performing said action. The main problem there is maintainability; I'd have to add this check to every place I intend to submit any sort of Cayenne action unless there is a way to hook this sort of check into the ObjectContext.

Does anyone know of a better way? Perhaps some advice for this novice enterprise developer?

Thanks again,
Adam

________________________________________
From: Aristedes Maniatis <ar...@maniatis.org>
Sent: Friday, April 8, 2016 12:30 AM
To: user@cayenne.apache.org
Subject: Re: ROP: detecting that my client connection has timed out

On 8/04/2016 1:28pm, Adam Boyle wrote:
> Is there a simple way of detecting that the connection is dead so that I can prompt the user to reconnect?

I don't think you need to prompt the user if you keep the user's authentication details in memory. Just create a new session with them.


> 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?

Those contexts are no longer associated with a session on the server (once your session times out). So you either have to recognise that a user is connecting again after a connection error and give them a handle on the same session id they had last time, or else start a new session and lose the old contexts.

The problem is: can you be sure the state of those contexts still make sense?


> 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.

Well this is outside Cayenne itself, but part of the reason we recently did the work in trunk (CAY-2065) to untangle Cayenne from Hessian and from the HTTP layer. Then you can manage the session more easily yourself to do whatever you want.

For example, we have a ping every minute from the client that:

* keeps the session alive (we don't want the session to die just because the user went to have lunch)
* allows us the keep the server-side session timeout quite low (good to expire sessions for users who dropped off the network without a proper logout)
* allows the server to track which users are having network issues

In the last six months we've been doing a bunch of work with ActiveMQ/STOMP which might eventually replace that ping. That way we have two way server-client communication and the server can quickly see which clients have lost network connection without a ping.


Nice to see a fellow Cayenne ROP user. There aren't many of us and it is really a very powerful bit of functionality.


Ari



--
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: ROP: detecting that my client connection has timed out

Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 8/04/2016 1:28pm, Adam Boyle wrote:
> Is there a simple way of detecting that the connection is dead so that I can prompt the user to reconnect?

I don't think you need to prompt the user if you keep the user's authentication details in memory. Just create a new session with them.


> 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?

Those contexts are no longer associated with a session on the server (once your session times out). So you either have to recognise that a user is connecting again after a connection error and give them a handle on the same session id they had last time, or else start a new session and lose the old contexts.

The problem is: can you be sure the state of those contexts still make sense?


> 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.

Well this is outside Cayenne itself, but part of the reason we recently did the work in trunk (CAY-2065) to untangle Cayenne from Hessian and from the HTTP layer. Then you can manage the session more easily yourself to do whatever you want.

For example, we have a ping every minute from the client that:

* keeps the session alive (we don't want the session to die just because the user went to have lunch)
* allows us the keep the server-side session timeout quite low (good to expire sessions for users who dropped off the network without a proper logout)
* allows the server to track which users are having network issues

In the last six months we've been doing a bunch of work with ActiveMQ/STOMP which might eventually replace that ping. That way we have two way server-client communication and the server can quickly see which clients have lost network connection without a ping.


Nice to see a fellow Cayenne ROP user. There aren't many of us and it is really a very powerful bit of functionality.


Ari



-- 
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A