You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@wicket.apache.org by Per Fragemann <pe...@small-improvements.com> on 2011/03/01 23:40:36 UTC

My failed 1.5 migration. Advice on HttpSessionDataStore and/or Jetty clustering needed

Hi all,

I'm running a pretty complex web application on Wicket 1.4 and Google 
App Engine, and want to move to Wicket 1.5.  Now before you think "GAE, 
whatever...", please hear me out! :-)   GAE is radically clustered, and 
problems showing here may eventually surface in other clustered 
environments too. Maybe not as instantly as on GAE, but possibly harder 
to track then.

While most things work fine currently in 1.4 (except multitab-support), 
the 1.5 migration was a bit of a desaster, and I had to revert it. I 
reduced my problem back to a simple quickstart (see attached). Using 
Martin's gae-initialiser, I got it running easily, but here's the problem:

* Add an AJAX component onto a page, e.g. an AjaxButton, and let it 
modify the page by updating a label's model to a random number. Click it 
a couple of times.
* Now go to a different page.
* Use the browsers back button.

When I test this locally the following happens: If the render-strategy 
is ONE_PASS_RENDER or REDIRECT_TO_RENDER, the page has been reset to its 
original state when we return to it. when using REDIRECT_TO_BUFFER, the 
page displays the right version (e.g. just like we left it). Sweet.

Now, when testing this on the remote GAE server however, things go bad: 
one-pass and redirect-to-render continue to not work. But 
redirect-to-buffer now displays a random version of the page after 
pushing the back button! I can even go back and forward, and get 
different versions of the page each time! I suppose the different nodes 
of App Engine have different page versions each.

So basically the whole page-serialisation concept is out of order here 
(while it works fine on 1.4, and also works fine in 1.5 without using GAE)

Maybe a missing history entry while using the back button isn't that 
bad, but I reckon the broken history is *also* to blame for countless 
page-expired errors I got, which then led me to roll back the release 
and deploy Wicket 1.4 again.


IMO, the bugfix in https://issues.apache.org/jira/browse/WICKET-3483 
hints at the problem, but only fights the symptom, not the root cause. 
The fix wants to avoid that null pages screw up the finding of pages. 
But when a null page just got passed into the PersistentPageManager, 
it's not enough to properly ignore it; something else seems to be broken 
in the first place. To my limited understanding we encounter null here 
because there's a check in DefaultPageStore:


     public Serializable prepareForSerialization(final String sessionId, 
final Object object)
     {
         if (pageDataStore.isReplicated())
         {
             return null;
         }

The current implementation of the HttpSessionDataStore returns 'true' in 
its isReplicated() method, so a null page gets returned then serialised 
by the above method, and later it gets deserialised into null again, and 
then wicket decides to re-render that page from scratch because it can't 
deal with a null object/find the right page in the pagestore.

Now, I wonder, how this is supposed to work at all? How can the 
prepareForSerialisation method simply return and then serialise null, 
and still expect things to work out later on?

So I thought it might make more sense to return "false" in the 
isReplicatedMethod of HttpSessionDataStore, to force the 
DefaultPageStore down the other part. When I do that, *something* is 
indeed serialised and wants to get deserialised. However, now I run into 
a new exciting problem:

Nested in javax.servlet.ServletException: org.apache.wicket.WicketRuntimeException: There is no application attached to current thread pool-4-thread-1:
org.apache.wicket.WicketRuntimeException: There is no application attached to current thread pool-4-thread-1
	at org.apache.wicket.Application.get(Application.java:227)
	at org.apache.wicket.Session.get(Session.java:154)
	at org.apache.wicket.page.DefaultPageManagerContext.getSessionAttribute(DefaultPageManagerContext.java:63)
	at org.apache.wicket.pageStore.memory.HttpSessionDataStore.getPageTable(HttpSessionDataStore.java:128)
	at org.apache.wicket.pageStore.memory.HttpSessionDataStore.storeData(HttpSessionDataStore.java:108)
	at org.apache.wicket.pageStore.DefaultPageStore.storePageData(DefaultPageStore.java:107)
	at org.apache.wicket.pageStore.DefaultPageStore.restoreAfterSerialization(DefaultPageStore.java:287)
	at org.apache.wicket.page.PersistentPageManager$SessionEntry.readObject(PersistentPageManager.java:240)
	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:616)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:991)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1865)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)
	at java.util.HashMap.readObject(HashMap.java:1047)
	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:616)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:991)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1865)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1963)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1887)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)
	at com.google.apphosting.runtime.jetty.SessionManager.deserialize(SessionManager.java:417)
	at com.google.apphosting.runtime.jetty.SessionManager.loadSession(SessionManager.java:315)
	at com.google.apphosting.runtime.jetty.SessionManager.getSession(SessionManager.java:288)
	at org.mortbay.jetty.servlet.AbstractSessionManager.getHttpSession(AbstractSessionManager.java:237)
	at org.mortbay.jetty.servlet.SessionHandler.setRequestedId(SessionHandler.java:246)
	at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:136)
	at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
	at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
	at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:238)
	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
	at org.mortbay.jetty.Server.handle(Server.java:326)
	at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
	at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
	at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
	at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
	at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:135)
	at com.google.apphosting.runtime.JavaRuntime.handleRequest(JavaRuntime.java:261)
	at com.google.apphosting.base.RuntimePb$EvaluationRuntime$2.handleRequest(RuntimePb.java:8440)
	at com.google.net.rpc.impl.RpcUtil.runRpcInApplication(RpcUtil.java:454)
	at com.google.net.rpc.impl.Server$RpcTask.runInContext(Server.java:572)
	at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:448)
	at com.google.tracing.TraceContext.runInContext(TraceContext.java:688)
	at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:326)
	at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:318)
	at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:446)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
	at java.lang.Thread.run(Thread.java:636)

And that's where my knowledge of Wicket and Jetty is simply not deep 
enough :-(

The above error does *not* happen on the local development server, but 
then again, it doesn't even check the isReplicated() locally.  I assume 
that the above exception only occurs when a JVM spins up freshly (e.g. 
after idling for a few minutes, or when you're using the always-on 
clustering option, and a node takes over which was not initialised 
before). Yeah, that sounds awfully GAE-specific again, but I think that 
it could strike in any other sufficiently clustered environment.

So I wonder if anyone else has gotten 1.5 running on GAE, but also if 
anyone has gotten 1.5 run in clustered mode on Jetty, and can shed some 
insight.

Cheers,
Per







Re: My failed 1.5 migration. Advice on HttpSessionDataStore and/or Jetty clustering needed

Posted by Attila Király <ki...@gmail.com>.
Your last exception seems pretty similar to those mentioned in
https://issues.apache.org/jira/browse/WICKET-3470 (especially the last one
also related to GAE: loading a persisted session at application startup).

Attila

2011/3/1 Per Fragemann <pe...@small-improvements.com>

>
> Hi all,
>
> I'm running a pretty complex web application on Wicket 1.4 and Google App
> Engine, and want to move to Wicket 1.5.  Now before you think "GAE,
> whatever...", please hear me out! :-)   GAE is radically clustered, and
> problems showing here may eventually surface in other clustered environments
> too. Maybe not as instantly as on GAE, but possibly harder to track then.
>
> While most things work fine currently in 1.4 (except multitab-support), the
> 1.5 migration was a bit of a desaster, and I had to revert it. I reduced my
> problem back to a simple quickstart (see attached). Using Martin's
> gae-initialiser, I got it running easily, but here's the problem:
>
> * Add an AJAX component onto a page, e.g. an AjaxButton, and let it modify
> the page by updating a label's model to a random number. Click it a couple
> of times.
> * Now go to a different page.
> * Use the browsers back button.
>
> When I test this locally the following happens: If the render-strategy is
> ONE_PASS_RENDER or REDIRECT_TO_RENDER, the page has been reset to its
> original state when we return to it. when using REDIRECT_TO_BUFFER, the page
> displays the right version (e.g. just like we left it). Sweet.
>
> Now, when testing this on the remote GAE server however, things go bad:
> one-pass and redirect-to-render continue to not work. But redirect-to-buffer
> now displays a random version of the page after pushing the back button! I
> can even go back and forward, and get different versions of the page each
> time! I suppose the different nodes of App Engine have different page
> versions each.
>
> So basically the whole page-serialisation concept is out of order here
> (while it works fine on 1.4, and also works fine in 1.5 without using GAE)
>
> Maybe a missing history entry while using the back button isn't that bad,
> but I reckon the broken history is *also* to blame for countless
> page-expired errors I got, which then led me to roll back the release and
> deploy Wicket 1.4 again.
>
>
> IMO, the bugfix in https://issues.apache.org/jira/browse/WICKET-3483 hints
> at the problem, but only fights the symptom, not the root cause. The fix
> wants to avoid that null pages screw up the finding of pages. But when a
> null page just got passed into the PersistentPageManager, it's not enough to
> properly ignore it; something else seems to be broken in the first place. To
> my limited understanding we encounter null here because there's a check in
> DefaultPageStore:
>
>
>    public Serializable prepareForSerialization(final String sessionId,
> final Object object)
>    {
>        if (pageDataStore.isReplicated())
>        {
>            return null;
>        }
>
> The current implementation of the HttpSessionDataStore returns 'true' in
> its isReplicated() method, so a null page gets returned then serialised by
> the above method, and later it gets deserialised into null again, and then
> wicket decides to re-render that page from scratch because it can't deal
> with a null object/find the right page in the pagestore.
>
> Now, I wonder, how this is supposed to work at all? How can the
> prepareForSerialisation method simply return and then serialise null, and
> still expect things to work out later on?
>
> So I thought it might make more sense to return "false" in the
> isReplicatedMethod of HttpSessionDataStore, to force the DefaultPageStore
> down the other part. When I do that, *something* is indeed serialised and
> wants to get deserialised. However, now I run into a new exciting problem:
>
> Nested in javax.servlet.ServletException:
> org.apache.wicket.WicketRuntimeException: There is no application attached
> to current thread pool-4-thread-1:
> org.apache.wicket.WicketRuntimeException: There is no application attached
> to current thread pool-4-thread-1
>        at org.apache.wicket.Application.get(Application.java:227)
>        at org.apache.wicket.Session.get(Session.java:154)
>        at
> org.apache.wicket.page.DefaultPageManagerContext.getSessionAttribute(DefaultPageManagerContext.java:63)
>        at
> org.apache.wicket.pageStore.memory.HttpSessionDataStore.getPageTable(HttpSessionDataStore.java:128)
>        at
> org.apache.wicket.pageStore.memory.HttpSessionDataStore.storeData(HttpSessionDataStore.java:108)
>        at
> org.apache.wicket.pageStore.DefaultPageStore.storePageData(DefaultPageStore.java:107)
>        at
> org.apache.wicket.pageStore.DefaultPageStore.restoreAfterSerialization(DefaultPageStore.java:287)
>        at
> org.apache.wicket.page.PersistentPageManager$SessionEntry.readObject(PersistentPageManager.java:240)
>        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:616)
>        at
> java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:991)
>        at
> java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1865)
>        at
> java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
>        at
> java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
>        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)
>        at java.util.HashMap.readObject(HashMap.java:1047)
>        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:616)
>        at
> java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:991)
>        at
> java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1865)
>        at
> java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
>        at
> java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
>        at
> java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1963)
>        at
> java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1887)
>        at
> java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
>        at
> java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
>        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)
>        at
> com.google.apphosting.runtime.jetty.SessionManager.deserialize(SessionManager.java:417)
>        at
> com.google.apphosting.runtime.jetty.SessionManager.loadSession(SessionManager.java:315)
>        at
> com.google.apphosting.runtime.jetty.SessionManager.getSession(SessionManager.java:288)
>        at
> org.mortbay.jetty.servlet.AbstractSessionManager.getHttpSession(AbstractSessionManager.java:237)
>        at
> org.mortbay.jetty.servlet.SessionHandler.setRequestedId(SessionHandler.java:246)
>        at
> org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:136)
>        at
> org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
>        at
> org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
>        at
> com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:238)
>        at
> org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
>        at org.mortbay.jetty.Server.handle(Server.java:326)
>        at
> org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
>        at
> org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
>        at
> com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
>        at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
>        at
> com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:135)
>        at
> com.google.apphosting.runtime.JavaRuntime.handleRequest(JavaRuntime.java:261)
>        at
> com.google.apphosting.base.RuntimePb$EvaluationRuntime$2.handleRequest(RuntimePb.java:8440)
>        at
> com.google.net.rpc.impl.RpcUtil.runRpcInApplication(RpcUtil.java:454)
>        at
> com.google.net.rpc.impl.Server$RpcTask.runInContext(Server.java:572)
>        at
> com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:448)
>        at
> com.google.tracing.TraceContext.runInContext(TraceContext.java:688)
>        at
> com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:326)
>        at
> com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:318)
>        at
> com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:446)
>        at
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
>        at
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
>        at java.lang.Thread.run(Thread.java:636)
>
> And that's where my knowledge of Wicket and Jetty is simply not deep enough
> :-(
>
> The above error does *not* happen on the local development server, but then
> again, it doesn't even check the isReplicated() locally.  I assume that the
> above exception only occurs when a JVM spins up freshly (e.g. after idling
> for a few minutes, or when you're using the always-on clustering option, and
> a node takes over which was not initialised before). Yeah, that sounds
> awfully GAE-specific again, but I think that it could strike in any other
> sufficiently clustered environment.
>
> So I wonder if anyone else has gotten 1.5 running on GAE, but also if
> anyone has gotten 1.5 run in clustered mode on Jetty, and can shed some
> insight.
>
> Cheers,
> Per
>
>
>
>
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>