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
>